mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-27 06:53:01 +00:00
Merge branch 'tildearrow:master' into master
This commit is contained in:
commit
7850e892d5
24 changed files with 512 additions and 140 deletions
|
@ -29,6 +29,7 @@ furthermore, an `or reserved` indicates this field is always present, but is res
|
|||
|
||||
the format versions are:
|
||||
|
||||
- 79: Furnace dev79
|
||||
- 78: Furnace dev78
|
||||
- 77: Furnace dev77
|
||||
- 76: Furnace dev76
|
||||
|
@ -579,6 +580,19 @@ size | description
|
|||
--- | **OPZ instrument extra data** (>=77)
|
||||
1 | fms2
|
||||
1 | ams2
|
||||
--- | **wavetable synth data** (>=79)
|
||||
4 | first wave
|
||||
4 | second wave
|
||||
1 | rate divider
|
||||
1 | effect
|
||||
| - bit 7: single or dual effect
|
||||
1 | enabled
|
||||
1 | global
|
||||
1 | speed (+1)
|
||||
1 | parameter 1
|
||||
1 | parameter 2
|
||||
1 | parameter 3
|
||||
1 | parameter 4
|
||||
```
|
||||
|
||||
# wavetable
|
||||
|
|
|
@ -42,8 +42,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev78"
|
||||
#define DIV_ENGINE_VERSION 78
|
||||
#define DIV_VERSION "dev79"
|
||||
#define DIV_ENGINE_VERSION 79
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
|
|
@ -69,7 +69,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
ds.version=(unsigned char)reader.readC();
|
||||
logI("module version %d (0x%.2x)\n",ds.version,ds.version);
|
||||
if (ds.version>0x19) {
|
||||
if (ds.version>0x1a) {
|
||||
logE("this version is not supported by Furnace yet!\n");
|
||||
lastError="this version is not supported by Furnace yet";
|
||||
delete[] file;
|
||||
|
@ -579,6 +579,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
} else {
|
||||
wave->data[j]=reader.readI();
|
||||
}
|
||||
wave->data[j]&=wave->max;
|
||||
}
|
||||
// #FDS4Bit
|
||||
if (ds.system[0]==DIV_SYSTEM_NES_FDS && ds.version<0x1a) {
|
||||
for (int j=0; j<wave->len; j++) {
|
||||
wave->data[j]*=4;
|
||||
}
|
||||
}
|
||||
ds.wave.push_back(wave);
|
||||
}
|
||||
|
@ -2222,7 +2229,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
|
||||
SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
||||
// fail if version is not supported
|
||||
if (version<24 || version>25) {
|
||||
if (version<24 || version>26) {
|
||||
logE("cannot save in this version!\n");
|
||||
lastError="invalid version to save in! this is a bug!";
|
||||
return NULL;
|
||||
|
@ -2273,6 +2280,12 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
lastError="NES + VRC7 not supported in 1.0/legacy .dmf!";
|
||||
return NULL;
|
||||
}
|
||||
// fail if the system is FDS and version<25
|
||||
if (version<25 && song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_FDS) {
|
||||
logE("FDS not supported in 1.0/legacy .dmf!\n");
|
||||
lastError="FDS not supported in 1.0/legacy .dmf!";
|
||||
return NULL;
|
||||
}
|
||||
// fail if the system is Furnace-exclusive
|
||||
if (!isFlat && systemToFileDMF(song.system[0])==0) {
|
||||
logE("cannot save Furnace-exclusive system song!\n");
|
||||
|
@ -2308,7 +2321,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
sys=DIV_SYSTEM_NES_VRC7;
|
||||
} else if (song.system[0]==DIV_SYSTEM_NES && song.system[1]==DIV_SYSTEM_FDS) {
|
||||
w->writeC(systemToFileDMF(DIV_SYSTEM_NES_FDS));
|
||||
sys=DIV_SYSTEM_NES_VRC7;
|
||||
sys=DIV_SYSTEM_NES_FDS;
|
||||
} else {
|
||||
w->writeC(systemToFileDMF(song.system[0]));
|
||||
sys=song.system[0];
|
||||
|
@ -2481,7 +2494,13 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
w->writeC(song.wave.size());
|
||||
for (DivWavetable* i: song.wave) {
|
||||
w->writeI(i->len);
|
||||
w->write(i->data,4*i->len);
|
||||
if (sys==DIV_SYSTEM_NES_FDS && version<26) {
|
||||
for (int j=0; j<i->len; j++) {
|
||||
w->writeI(i->data[j]>>2);
|
||||
}
|
||||
} else {
|
||||
w->write(i->data,4*i->len);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<getChannelCount(sys); i++) {
|
||||
|
|
|
@ -467,6 +467,19 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
// OPZ
|
||||
w->writeC(fm.fms2);
|
||||
w->writeC(fm.ams2);
|
||||
|
||||
// wave synth
|
||||
w->writeI(ws.wave1);
|
||||
w->writeI(ws.wave2);
|
||||
w->writeC(ws.rateDivider);
|
||||
w->writeC(ws.effect);
|
||||
w->writeC(ws.enabled);
|
||||
w->writeC(ws.global);
|
||||
w->writeC(ws.speed);
|
||||
w->writeC(ws.param1);
|
||||
w->writeC(ws.param2);
|
||||
w->writeC(ws.param3);
|
||||
w->writeC(ws.param4);
|
||||
}
|
||||
|
||||
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
||||
|
@ -895,6 +908,21 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
fm.fms2=reader.readC();
|
||||
fm.ams2=reader.readC();
|
||||
}
|
||||
|
||||
// wave synth
|
||||
if (version>=79) {
|
||||
ws.wave1=reader.readI();
|
||||
ws.wave2=reader.readI();
|
||||
ws.rateDivider=reader.readC();
|
||||
ws.effect=reader.readC();
|
||||
ws.enabled=reader.readC();
|
||||
ws.global=reader.readC();
|
||||
ws.speed=reader.readC();
|
||||
ws.param1=reader.readC();
|
||||
ws.param2=reader.readC();
|
||||
ws.param3=reader.readC();
|
||||
ws.param4=reader.readC();
|
||||
}
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -485,6 +485,9 @@ enum DivWaveSynthEffects {
|
|||
DIV_WS_SUBTRACT,
|
||||
DIV_WS_AVERAGE,
|
||||
DIV_WS_PHASE,
|
||||
|
||||
DIV_WS_SINGLE_MAX,
|
||||
|
||||
// two waveform effects
|
||||
DIV_WS_NONE_DUAL=128,
|
||||
DIV_WS_WIPE,
|
||||
|
@ -493,25 +496,25 @@ enum DivWaveSynthEffects {
|
|||
DIV_WS_OVERLAY,
|
||||
DIV_WS_NEGATIVE_OVERLAY,
|
||||
DIV_WS_PHASE_DUAL,
|
||||
|
||||
DIV_WS_DUAL_MAX
|
||||
};
|
||||
|
||||
struct DivInstrumentWaveSynth {
|
||||
int wave1, wave2;
|
||||
unsigned char rateDivider, width, height;
|
||||
DivWaveSynthEffects effect;
|
||||
unsigned char rateDivider;
|
||||
unsigned char effect;
|
||||
bool oneShot, enabled, global;
|
||||
unsigned char speed, param1, param2, param3, param4;
|
||||
DivInstrumentWaveSynth():
|
||||
wave1(0),
|
||||
wave2(0),
|
||||
rateDivider(1),
|
||||
width(32),
|
||||
height(32),
|
||||
effect(DIV_WS_NONE),
|
||||
oneShot(false),
|
||||
enabled(false),
|
||||
global(false),
|
||||
speed(1),
|
||||
speed(0),
|
||||
param1(0),
|
||||
param2(0),
|
||||
param3(0),
|
||||
|
|
|
@ -71,17 +71,13 @@ void DivPlatformBubSysWSG::acquire(short* bufL, short* bufR, size_t start, size_
|
|||
}
|
||||
|
||||
void DivPlatformBubSysWSG::updateWave(int ch) {
|
||||
DivWavetable* wt=parent->getWave(chan[ch].wave);
|
||||
//DivWavetable* wt=parent->getWave(chan[ch].wave);
|
||||
for (int i=0; i<32; i++) {
|
||||
if (wt->max>0 && wt->len>0) {
|
||||
int data=wt->data[i*wt->len/32]*15/wt->max; // 4 bit PROM at bubble system
|
||||
if (data<0) data=0;
|
||||
if (data>15) data=15;
|
||||
chan[ch].waveROM[i]=data-8; // convert to signed
|
||||
}
|
||||
// convert to signed
|
||||
chan[ch].waveROM[i]=chan[ch].ws.output[i]-8;
|
||||
}
|
||||
if (chan[ch].active) {
|
||||
rWrite(2+ch,(chan[ch].wave<<5)|chan[ch].outVol);
|
||||
rWrite(2+ch,(ch<<5)|chan[ch].outVol);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,12 +104,17 @@ void DivPlatformBubSysWSG::tick() {
|
|||
}
|
||||
}
|
||||
if (chan[i].std.hadWave) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
if (chan[i].wave!=chan[i].std.wave || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
updateWave(i);
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick()) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||
chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||
|
@ -123,10 +124,7 @@ void DivPlatformBubSysWSG::tick() {
|
|||
rWrite(i,chan[i].freq);
|
||||
k005289->update(i);
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].wave<0) {
|
||||
chan[i].wave=0;
|
||||
updateWave(i);
|
||||
}
|
||||
// ???
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(2+i,(chan[i].wave<<5)|0);
|
||||
|
@ -151,6 +149,12 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOn=true;
|
||||
rWrite(2+c.chan,(chan[c.chan].wave<<5)|chan[c.chan].vol);
|
||||
chan[c.chan].std.init(ins);
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,32,15,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
|
@ -188,7 +192,7 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
updateWave(c.chan);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
|
@ -270,6 +274,8 @@ void DivPlatformBubSysWSG::reset() {
|
|||
memset(regPool,0,4*2);
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i]=DivPlatformBubSysWSG::Channel();
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,15,false);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
@ -288,6 +294,7 @@ bool DivPlatformBubSysWSG::keyOffAffectsArp(int ch) {
|
|||
void DivPlatformBubSysWSG::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<2; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/k005289/k005289.hpp"
|
||||
|
||||
class DivPlatformBubSysWSG: public DivDispatch {
|
||||
|
@ -33,6 +34,7 @@ class DivPlatformBubSysWSG: public DivDispatch {
|
|||
signed char vol, outVol, wave;
|
||||
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
|
||||
DivMacroInt std;
|
||||
DivWaveSynth ws;
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
|
|
|
@ -89,18 +89,10 @@ void DivPlatformFDS::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
|
||||
void DivPlatformFDS::updateWave() {
|
||||
DivWavetable* wt=parent->getWave(chan[0].wave);
|
||||
// TODO: master volume
|
||||
rWrite(0x4089,0x80);
|
||||
for (int i=0; i<64; i++) {
|
||||
if (wt->max<1 || wt->len<1) {
|
||||
rWrite(0x4040+i,0);
|
||||
} else {
|
||||
int data=wt->data[i*wt->len/64]*63/wt->max;
|
||||
if (data<0) data=0;
|
||||
if (data>63) data=63;
|
||||
rWrite(0x4040+i,data);
|
||||
}
|
||||
rWrite(0x4040+i,ws.output[i]);
|
||||
}
|
||||
rWrite(0x4089,0);
|
||||
}
|
||||
|
@ -157,12 +149,18 @@ void DivPlatformFDS::tick() {
|
|||
}
|
||||
}*/
|
||||
if (chan[i].std.hadWave) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
if (chan[i].wave!=chan[i].std.wave || ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
updateWave();
|
||||
ws.changeWave1(chan[i].wave);
|
||||
//if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (ws.tick()) {
|
||||
updateWave();
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadEx1) { // mod depth
|
||||
chan[i].modOn=chan[i].std.ex1;
|
||||
chan[i].modDepth=chan[i].std.ex1;
|
||||
|
@ -190,10 +188,7 @@ void DivPlatformFDS::tick() {
|
|||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].wave<0) {
|
||||
chan[i].wave=0;
|
||||
updateWave();
|
||||
}
|
||||
// ???
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(0x4080,0x80);
|
||||
|
@ -209,20 +204,20 @@ void DivPlatformFDS::tick() {
|
|||
|
||||
int DivPlatformFDS::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
if (ins->fds.initModTableWithFirstWave) { // compatible
|
||||
if (chan[c.chan].wave==-1) {
|
||||
DivWavetable* wt=parent->getWave(0);
|
||||
for (int i=0; i<32; i++) {
|
||||
if (wt->max<1 || wt->len<1) {
|
||||
rWrite(0x4040+i,0);
|
||||
chan[c.chan].modTable[i]=0;
|
||||
} else {
|
||||
int data=wt->data[i*MIN(32,wt->len)/32]*7/wt->max;
|
||||
if (data<0) data=0;
|
||||
|
@ -253,9 +248,16 @@ int DivPlatformFDS::dispatch(DivCommand c) {
|
|||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||
chan[c.chan].std.init(ins);
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
ws.init(ins,64,63,chan[c.chan].insChanged);
|
||||
rWrite(0x4080,0x80|chan[c.chan].vol);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
|
@ -268,6 +270,7 @@ int DivPlatformFDS::dispatch(DivCommand c) {
|
|||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
|
@ -289,7 +292,7 @@ int DivPlatformFDS::dispatch(DivCommand c) {
|
|||
case DIV_CMD_WAVE:
|
||||
if (chan[c.chan].wave!=c.value) {
|
||||
chan[c.chan].wave=c.value;
|
||||
updateWave();
|
||||
ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FDS_MOD_DEPTH:
|
||||
|
@ -406,6 +409,8 @@ void DivPlatformFDS::reset() {
|
|||
for (int i=0; i<1; i++) {
|
||||
chan[i]=DivPlatformFDS::Channel();
|
||||
}
|
||||
ws.setEngine(parent);
|
||||
ws.init(NULL,64,63,false);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
|
||||
class DivPlatformFDS: public DivDispatch {
|
||||
struct Channel {
|
||||
|
@ -59,6 +60,7 @@ class DivPlatformFDS: public DivDispatch {
|
|||
};
|
||||
Channel chan[1];
|
||||
bool isMuted[1];
|
||||
DivWaveSynth ws;
|
||||
unsigned char apuType;
|
||||
struct _fds* fds;
|
||||
unsigned char regPool[128];
|
||||
|
|
|
@ -91,20 +91,11 @@ void DivPlatformGB::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
|
||||
void DivPlatformGB::updateWave() {
|
||||
DivWavetable* wt=parent->getWave(chan[2].wave);
|
||||
rWrite(0x1a,0);
|
||||
for (int i=0; i<16; i++) {
|
||||
if (wt->max<1 || wt->len<1) {
|
||||
rWrite(0x30+i,0);
|
||||
} else {
|
||||
int nibble1=15-((wt->data[(i*2)*wt->len/32]*15)/wt->max);
|
||||
int nibble2=15-((wt->data[(1+i*2)*wt->len/32]*15)/wt->max);
|
||||
if (nibble1<0) nibble1=0;
|
||||
if (nibble1>15) nibble1=15;
|
||||
if (nibble2<0) nibble2=0;
|
||||
if (nibble2>15) nibble2=15;
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
int nibble1=15-ws.output[i<<1];
|
||||
int nibble2=15-ws.output[1+(i<<1)];
|
||||
rWrite(0x30+i,(nibble1<<4)|nibble2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,10 +185,16 @@ void DivPlatformGB::tick() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadWave) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
if (i==2 && chan[i].std.hadWave) {
|
||||
if (chan[i].wave!=chan[i].std.wave || ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
if (i==2) {
|
||||
ws.changeWave1(chan[i].wave);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (i==2) {
|
||||
if (chan[i].active) {
|
||||
if (ws.tick()) {
|
||||
updateWave();
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
|
@ -222,10 +219,6 @@ void DivPlatformGB::tick() {
|
|||
}
|
||||
if (chan[i].keyOn) {
|
||||
if (i==2) { // wave
|
||||
if (chan[i].wave<0) {
|
||||
chan[i].wave=0;
|
||||
updateWave();
|
||||
}
|
||||
rWrite(16+i*5,0x80);
|
||||
rWrite(16+i*5+2,gbVolMap[chan[i].vol]);
|
||||
} else {
|
||||
|
@ -261,7 +254,8 @@ void DivPlatformGB::muteChannel(int ch, bool mute) {
|
|||
|
||||
int DivPlatformGB::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
if (c.chan==3) { // noise
|
||||
chan[c.chan].baseFreq=c.value;
|
||||
|
@ -273,8 +267,17 @@ int DivPlatformGB::dispatch(DivCommand c) {
|
|||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||
chan[c.chan].std.init(ins);
|
||||
if (c.chan==2) {
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
ws.init(ins,32,15,chan[c.chan].insChanged);
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
|
@ -287,6 +290,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
|
|||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
if (c.chan!=2) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
chan[c.chan].vol=ins->gb.envVol;
|
||||
|
@ -312,7 +316,7 @@ int DivPlatformGB::dispatch(DivCommand c) {
|
|||
case DIV_CMD_WAVE:
|
||||
if (c.chan!=2) break;
|
||||
chan[c.chan].wave=c.value;
|
||||
updateWave();
|
||||
ws.changeWave1(chan[c.chan].wave);
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
|
@ -415,6 +419,8 @@ void DivPlatformGB::reset() {
|
|||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformGB::Channel();
|
||||
}
|
||||
ws.setEngine(parent);
|
||||
ws.init(NULL,32,15,false);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
@ -445,6 +451,7 @@ void DivPlatformGB::notifyInsChange(int ins) {
|
|||
|
||||
void DivPlatformGB::notifyWaveChange(int wave) {
|
||||
if (chan[2].wave==wave) {
|
||||
ws.changeWave1(wave);
|
||||
updateWave();
|
||||
if (!chan[2].keyOff) chan[2].keyOn=true;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/gb/gb.h"
|
||||
|
||||
class DivPlatformGB: public DivDispatch {
|
||||
|
@ -53,6 +54,7 @@ class DivPlatformGB: public DivDispatch {
|
|||
Channel chan[4];
|
||||
bool isMuted[4];
|
||||
unsigned char lastPan;
|
||||
DivWaveSynth ws;
|
||||
|
||||
GB_gameboy_t* gb;
|
||||
unsigned char regPool[128];
|
||||
|
|
|
@ -947,7 +947,7 @@ void DivPlatformOPL::reset() {
|
|||
drumVol[3]=0;
|
||||
drumVol[4]=0;
|
||||
|
||||
if (oplType==1) { // disable waveforms
|
||||
if (oplType==2) { // enable OPL2 waveforms
|
||||
immWrite(0x01,0x20);
|
||||
}
|
||||
|
||||
|
|
|
@ -131,18 +131,10 @@ void DivPlatformPCE::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
|
||||
void DivPlatformPCE::updateWave(int ch) {
|
||||
DivWavetable* wt=parent->getWave(chan[ch].wave);
|
||||
chWrite(ch,0x04,0x5f);
|
||||
chWrite(ch,0x04,0x1f);
|
||||
for (int i=0; i<32; i++) {
|
||||
if (wt->max<1 || wt->len<1) {
|
||||
chWrite(ch,0x06,0);
|
||||
} else {
|
||||
int data=wt->data[i*wt->len/32]*31/wt->max;
|
||||
if (data<0) data=0;
|
||||
if (data>31) data=31;
|
||||
chWrite(ch,0x06,data);
|
||||
}
|
||||
chWrite(ch,0x06,chan[ch].ws.output[i]);
|
||||
}
|
||||
if (chan[ch].active) {
|
||||
chWrite(ch,0x04,0x80|chan[ch].outVol);
|
||||
|
@ -198,12 +190,17 @@ void DivPlatformPCE::tick() {
|
|||
}
|
||||
}
|
||||
if (chan[i].std.hadWave && !chan[i].pcm) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
if (chan[i].wave!=chan[i].std.wave || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
updateWave(i);
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick()) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||
|
@ -224,10 +221,6 @@ void DivPlatformPCE::tick() {
|
|||
chWrite(i,0x02,chan[i].freq&0xff);
|
||||
chWrite(i,0x03,chan[i].freq>>8);
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].wave<0) {
|
||||
chan[i].wave=0;
|
||||
updateWave(i);
|
||||
}
|
||||
//rWrite(16+i*5,0x80);
|
||||
//chWrite(i,0x04,0x80|chan[i].vol);
|
||||
}
|
||||
|
@ -310,6 +303,12 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOn=true;
|
||||
chWrite(c.chan,0x04,0x80|chan[c.chan].vol);
|
||||
chan[c.chan].std.init(ins);
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,32,31,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
|
@ -327,6 +326,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
|
@ -350,7 +350,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
updateWave(c.chan);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_PCE_LFO_MODE:
|
||||
|
@ -462,6 +462,8 @@ void DivPlatformPCE::reset() {
|
|||
memset(regPool,0,128);
|
||||
for (int i=0; i<6; i++) {
|
||||
chan[i]=DivPlatformPCE::Channel();
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,31,false);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
@ -499,6 +501,7 @@ bool DivPlatformPCE::keyOffAffectsArp(int ch) {
|
|||
void DivPlatformPCE::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<6; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(wave);
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/pce_psg.h"
|
||||
|
||||
class DivPlatformPCE: public DivDispatch {
|
||||
|
@ -35,6 +36,7 @@ class DivPlatformPCE: public DivDispatch {
|
|||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac;
|
||||
signed char vol, outVol, wave;
|
||||
DivMacroInt std;
|
||||
DivWaveSynth ws;
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
|
|
|
@ -111,22 +111,11 @@ void DivPlatformSwan::acquire(short* bufL, short* bufR, size_t start, size_t len
|
|||
}
|
||||
|
||||
void DivPlatformSwan::updateWave(int ch) {
|
||||
DivWavetable* wt=parent->getWave(chan[ch].wave);
|
||||
unsigned char addr=0x40+ch*16;
|
||||
if (wt->max<1 || wt->len<1) {
|
||||
for (int i=0; i<16; i++) {
|
||||
rWrite(addr+i,0);
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=(wt->data[(i*2)*wt->len/32]*15)/wt->max;
|
||||
int nibble2=(wt->data[(1+i*2)*wt->len/32]*15)/wt->max;
|
||||
if (nibble1<0) nibble1=0;
|
||||
if (nibble1>15) nibble1=15;
|
||||
if (nibble2<0) nibble2=0;
|
||||
if (nibble2>15) nibble2=15;
|
||||
rWrite(addr+i,nibble1|(nibble2<<4));
|
||||
}
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=chan[ch].ws.output[i<<1];
|
||||
int nibble2=chan[ch].ws.output[1+(i<<1)];
|
||||
rWrite(addr+i,nibble1|(nibble2<<4));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,13 +168,16 @@ void DivPlatformSwan::tick() {
|
|||
}
|
||||
}
|
||||
if (chan[i].std.hadWave && !(i==1 && pcm)) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
if (chan[i].wave!=chan[i].std.wave || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
updateWave(i);
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
}
|
||||
}
|
||||
if (chan[i].active) {
|
||||
sndCtrl|=(1<<i);
|
||||
if (chan[i].ws.tick()) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||
|
@ -211,10 +203,6 @@ void DivPlatformSwan::tick() {
|
|||
if (!chan[i].std.willVol) {
|
||||
calcAndWriteOutVol(i,15);
|
||||
}
|
||||
if (chan[i].wave<0) {
|
||||
chan[i].wave=0;
|
||||
updateWave(i);
|
||||
}
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
|
@ -300,6 +288,12 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].std.init(ins);
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,32,15,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
|
@ -319,6 +313,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
|
@ -338,7 +333,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
updateWave(c.chan);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_WS_SWEEP_TIME:
|
||||
|
@ -458,6 +453,8 @@ void DivPlatformSwan::reset() {
|
|||
chan[i]=Channel();
|
||||
chan[i].vol=15;
|
||||
chan[i].pan=0xff;
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,15,false);
|
||||
rWrite(0x08+i,0xff);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
|
@ -484,6 +481,7 @@ bool DivPlatformSwan::isStereo() {
|
|||
void DivPlatformSwan::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(wave);
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/swan.h"
|
||||
#include <queue>
|
||||
|
||||
|
@ -32,6 +33,7 @@ class DivPlatformSwan: public DivDispatch {
|
|||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||
int vol, outVol, wave;
|
||||
DivMacroInt std;
|
||||
DivWaveSynth ws;
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
|
|
|
@ -274,19 +274,12 @@ double DivPlatformX1_010::NoteX1_010(int ch, int note) {
|
|||
}
|
||||
|
||||
void DivPlatformX1_010::updateWave(int ch) {
|
||||
DivWavetable* wt=parent->getWave(chan[ch].wave);
|
||||
if (chan[ch].active) {
|
||||
chan[ch].waveBank ^= 1;
|
||||
chan[ch].waveBank^=1;
|
||||
}
|
||||
for (int i=0; i<128; i++) {
|
||||
if (wt->max<1 || wt->len<1) {
|
||||
waveWrite(ch,i,0);
|
||||
} else {
|
||||
int data=wt->data[i*wt->len/128]*255/wt->max;
|
||||
if (data<0) data=0;
|
||||
if (data>255) data=255;
|
||||
waveWrite(ch,i,data);
|
||||
}
|
||||
int data=chan[ch].ws.output[i];
|
||||
waveWrite(ch,i,data);
|
||||
}
|
||||
if (!chan[ch].pcm) {
|
||||
chWrite(ch,1,(chan[ch].waveBank<<4)|(ch&0xf));
|
||||
|
@ -371,10 +364,10 @@ void DivPlatformX1_010::tick() {
|
|||
}
|
||||
}
|
||||
if (chan[i].std.hadWave && !chan[i].pcm) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
if (chan[i].wave!=chan[i].std.wave || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
if (!chan[i].pcm) {
|
||||
updateWave(i);
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
@ -458,6 +451,11 @@ void DivPlatformX1_010::tick() {
|
|||
if (!chan[i].std.willEx3) chan[i].autoEnvNum=1;
|
||||
}
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick()) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].envChanged) {
|
||||
chan[i].lvol=isMuted[i]?0:(((chan[i].outVol&0xf)*((chan[i].pan>>4)&0xf))/15);
|
||||
chan[i].rvol=isMuted[i]?0:(((chan[i].outVol&0xf)*((chan[i].pan>>0)&0xf))/15);
|
||||
|
@ -575,6 +573,12 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].envChanged=true;
|
||||
chan[c.chan].std.init(ins);
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,128,255,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
refreshControl(c.chan);
|
||||
break;
|
||||
}
|
||||
|
@ -591,6 +595,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
|
@ -618,7 +623,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
updateWave(c.chan);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_X1_010_ENVELOPE_SHAPE:
|
||||
|
@ -813,6 +818,8 @@ void DivPlatformX1_010::reset() {
|
|||
for (int i=0; i<16; i++) {
|
||||
chan[i]=DivPlatformX1_010::Channel();
|
||||
chan[i].reset();
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,128,255,false);
|
||||
}
|
||||
x1_010->reset();
|
||||
sampleBank=0;
|
||||
|
@ -833,6 +840,7 @@ bool DivPlatformX1_010::keyOffAffectsArp(int ch) {
|
|||
void DivPlatformX1_010::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<16; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(wave);
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "../dispatch.h"
|
||||
#include "../engine.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/x1_010/x1_010.hpp"
|
||||
|
||||
class DivX1_010Interface: public x1_010_mem_intf {
|
||||
|
@ -86,6 +87,7 @@ class DivPlatformX1_010: public DivDispatch {
|
|||
unsigned char waveBank;
|
||||
Envelope env;
|
||||
DivMacroInt std;
|
||||
DivWaveSynth ws;
|
||||
void reset() {
|
||||
freq = baseFreq = pitch = note = 0;
|
||||
wave = sample = ins = -1;
|
||||
|
|
|
@ -102,6 +102,8 @@ struct DivSong {
|
|||
// version number used for saving the song.
|
||||
// Furnace will save using the latest possible version,
|
||||
// known version numbers:
|
||||
// - 26: v1.1.3
|
||||
// - changes height of FDS wave to 6-bit (it was 4-bit before)
|
||||
// - 25: v1.1
|
||||
// - adds pattern names (in a rather odd way)
|
||||
// - introduces SMS+OPLL system
|
||||
|
|
|
@ -1,5 +1,99 @@
|
|||
#include "waveSynth.h"
|
||||
#include "engine.h"
|
||||
#include "instrument.h"
|
||||
|
||||
bool DivWaveSynth::activeChanged() {
|
||||
if (activeChangedB) {
|
||||
activeChangedB=false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DivWaveSynth::tick() {
|
||||
return false;
|
||||
bool updated=first;
|
||||
first=false;
|
||||
if (!state.enabled) return updated;
|
||||
|
||||
if (--divCounter<=0) {
|
||||
// run effect
|
||||
switch (state.effect) {
|
||||
case DIV_WS_INVERT:
|
||||
for (int i=0; i<=state.speed; i++) {
|
||||
output[pos]=height-output[pos];
|
||||
if (++pos>=width) pos=0;
|
||||
}
|
||||
updated=true;
|
||||
break;
|
||||
}
|
||||
divCounter=state.rateDivider;
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
void DivWaveSynth::changeWave1(int num) {
|
||||
DivWavetable* w1=e->getWave(num);
|
||||
for (int i=0; i<width; i++) {
|
||||
if (w1->max<1 || w1->len<1) {
|
||||
wave1[i]=0;
|
||||
output[i]=0;
|
||||
} else {
|
||||
int data=w1->data[i*w1->len/width]*height/w1->max;
|
||||
if (data<0) data=0;
|
||||
if (data>height) data=height;
|
||||
wave1[i]=data;
|
||||
output[i]=data;
|
||||
}
|
||||
}
|
||||
first=true;
|
||||
}
|
||||
|
||||
void DivWaveSynth::changeWave2(int num) {
|
||||
DivWavetable* w2=e->getWave(num);
|
||||
for (int i=0; i<width; i++) {
|
||||
if (w2->max<1 || w2->len<1) {
|
||||
wave2[i]=0;
|
||||
} else {
|
||||
int data=w2->data[i*w2->len/width]*height/w2->max;
|
||||
if (data<0) data=0;
|
||||
if (data>height) data=height;
|
||||
wave2[i]=data;
|
||||
}
|
||||
}
|
||||
first=true;
|
||||
}
|
||||
|
||||
void DivWaveSynth::setEngine(DivEngine* engine) {
|
||||
e=engine;
|
||||
}
|
||||
|
||||
void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) {
|
||||
width=w;
|
||||
height=h;
|
||||
if (width<0) width=0;
|
||||
if (width>256) width=256;
|
||||
if (e==NULL) return;
|
||||
if (which==NULL) {
|
||||
if (state.enabled) activeChangedB=true;
|
||||
state=DivInstrumentWaveSynth();
|
||||
return;
|
||||
}
|
||||
if (!which->ws.enabled) {
|
||||
if (state.enabled) activeChangedB=true;
|
||||
state=DivInstrumentWaveSynth();
|
||||
return;
|
||||
} else {
|
||||
if (!state.enabled) activeChangedB=true;
|
||||
}
|
||||
state=which->ws;
|
||||
if (insChanged || !state.global) {
|
||||
pos=0;
|
||||
stage=0;
|
||||
divCounter=1+state.rateDivider;
|
||||
first=true;
|
||||
|
||||
changeWave1(state.wave1);
|
||||
changeWave2(state.wave2);
|
||||
}
|
||||
}
|
|
@ -23,22 +23,60 @@
|
|||
#include "instrument.h"
|
||||
#include "wavetable.h"
|
||||
|
||||
class DivEngine;
|
||||
|
||||
class DivWaveSynth {
|
||||
DivInstrument* ins;
|
||||
int pos, stage, divCounter;
|
||||
int output[256];
|
||||
DivEngine* e;
|
||||
DivInstrumentWaveSynth state;
|
||||
int pos, stage, divCounter, width, height;
|
||||
bool first, activeChangedB;
|
||||
unsigned char wave1[256];
|
||||
unsigned char wave2[256];
|
||||
public:
|
||||
/**
|
||||
* the output.
|
||||
*/
|
||||
int output[256];
|
||||
/**
|
||||
* check whether the "active" status has changed.
|
||||
* @return truth.
|
||||
*/
|
||||
bool activeChanged();
|
||||
/**
|
||||
* tick this DivWaveSynth.
|
||||
* @return whether the wave has changed.
|
||||
*/
|
||||
bool tick();
|
||||
void init(DivInstrument* ins);
|
||||
/**
|
||||
* change the first wave.
|
||||
* @param num wavetable number.
|
||||
*/
|
||||
void changeWave1(int num);
|
||||
/**
|
||||
* change the second wave.
|
||||
* @param num wavetable number.
|
||||
*/
|
||||
void changeWave2(int num);
|
||||
/**
|
||||
* initialize this DivWaveSynth.
|
||||
* @param which the instrument.
|
||||
* @param width the system's wave width.
|
||||
* @param height the system's wave height.
|
||||
* @param insChanged whether the instrument has changed.
|
||||
*/
|
||||
void init(DivInstrument* which, int width, int height, bool insChanged=false);
|
||||
void setEngine(DivEngine* engine);
|
||||
DivWaveSynth():
|
||||
ins(NULL),
|
||||
e(NULL),
|
||||
pos(0),
|
||||
stage(0),
|
||||
divCounter(0) {
|
||||
divCounter(0),
|
||||
width(32),
|
||||
height(31),
|
||||
first(false),
|
||||
activeChangedB(false) {
|
||||
memset(wave1,0,sizeof(int)*256);
|
||||
memset(wave2,0,sizeof(int)*256);
|
||||
memset(output,0,sizeof(int)*256);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1016,11 +1016,17 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
|||
if (!settings.effectCursorDir) {
|
||||
editAdvance();
|
||||
} else {
|
||||
if (cursor.xFine&1) {
|
||||
cursor.xFine++;
|
||||
if (settings.effectCursorDir==2) {
|
||||
if (++cursor.xFine>=(3+(e->song.pat[cursor.xCoarse].effectRows*2))) {
|
||||
cursor.xFine=3;
|
||||
}
|
||||
} else {
|
||||
editAdvance();
|
||||
cursor.xFine--;
|
||||
if (cursor.xFine&1) {
|
||||
cursor.xFine++;
|
||||
} else {
|
||||
editAdvance();
|
||||
cursor.xFine--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1300,8 +1306,8 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
hasOpened=fileDialog->openSave(
|
||||
"Save File",
|
||||
{"Furnace song", "*.fur",
|
||||
"DefleMask 1.1 module", "*.dmf"},
|
||||
"Furnace song{.fur},DefleMask 1.1 module{.dmf}",
|
||||
"DefleMask 1.1.3 module", "*.dmf"},
|
||||
"Furnace song{.fur},DefleMask 1.1.3 module{.dmf}",
|
||||
workingDirSong,
|
||||
dpiScale
|
||||
);
|
||||
|
@ -2714,7 +2720,7 @@ bool FurnaceGUI::loop() {
|
|||
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
||||
}
|
||||
} else {
|
||||
if (save(copyOfName,25)>0) {
|
||||
if (save(copyOfName,26)>0) {
|
||||
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -157,6 +157,25 @@ const int orderedOps[4]={
|
|||
0, 2, 1, 3
|
||||
};
|
||||
|
||||
const char* singleWSEffects[6]={
|
||||
"None",
|
||||
"Invert",
|
||||
"Add",
|
||||
"Subtract",
|
||||
"Average",
|
||||
"Phase",
|
||||
};
|
||||
|
||||
const char* dualWSEffects[7]={
|
||||
"None (dual)",
|
||||
"Wipe",
|
||||
"Fade",
|
||||
"Wipe (ping-pong)",
|
||||
"Overlay",
|
||||
"Negative Overlay",
|
||||
"Phase (dual)",
|
||||
};
|
||||
|
||||
String macroHoverNote(int id, float val) {
|
||||
if (val<-60 || val>=120) return "???";
|
||||
return fmt::sprintf("%d: %s",id,noteNames[(int)val+60]);
|
||||
|
@ -2301,6 +2320,109 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ins->type==DIV_INS_GB ||
|
||||
ins->type==DIV_INS_AMIGA ||
|
||||
ins->type==DIV_INS_X1_010 ||
|
||||
ins->type==DIV_INS_N163 ||
|
||||
ins->type==DIV_INS_FDS ||
|
||||
ins->type==DIV_INS_SWAN ||
|
||||
ins->type==DIV_INS_PCE ||
|
||||
ins->type==DIV_INS_SCC) {
|
||||
if (ImGui::BeginTabItem("Wavetable")) {
|
||||
ImGui::Checkbox("Enable synthesizer",&ins->ws.enabled);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ins->ws.effect&0x80) {
|
||||
if ((ins->ws.effect&0x7f)>=DIV_WS_DUAL_MAX) {
|
||||
ins->ws.effect=0;
|
||||
}
|
||||
} else {
|
||||
if ((ins->ws.effect&0x7f)>=DIV_WS_SINGLE_MAX) {
|
||||
ins->ws.effect=0;
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginCombo("##WSEffect",(ins->ws.effect&0x80)?dualWSEffects[ins->ws.effect&0x7f]:singleWSEffects[ins->ws.effect&0x7f])) {
|
||||
ImGui::Text("Single-waveform");
|
||||
ImGui::Indent();
|
||||
for (int i=0; i<DIV_WS_SINGLE_MAX; i++) {
|
||||
if (ImGui::Selectable(singleWSEffects[i])) {
|
||||
ins->ws.effect=i;
|
||||
}
|
||||
}
|
||||
ImGui::Unindent();
|
||||
ImGui::Text("Dual-waveform");
|
||||
ImGui::Indent();
|
||||
for (int i=129; i<DIV_WS_DUAL_MAX; i++) {
|
||||
if (ImGui::Selectable(dualWSEffects[i-128])) {
|
||||
ins->ws.effect=i;
|
||||
}
|
||||
}
|
||||
ImGui::Unindent();
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::BeginTable("WSPreview",2)) {
|
||||
DivWavetable* wave1=e->getWave(ins->ws.wave1);
|
||||
DivWavetable* wave2=e->getWave(ins->ws.wave2);
|
||||
float wavePreview1[256];
|
||||
float wavePreview2[256];
|
||||
for (int i=0; i<wave1->len; i++) {
|
||||
if (wave1->data[i]>wave1->max) {
|
||||
wavePreview1[i]=wave1->max;
|
||||
} else {
|
||||
wavePreview1[i]=wave1->data[i];
|
||||
}
|
||||
}
|
||||
for (int i=0; i<wave2->len; i++) {
|
||||
if (wave2->data[i]>wave2->max) {
|
||||
wavePreview2[i]=wave2->max;
|
||||
} else {
|
||||
wavePreview2[i]=wave2->data[i];
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImVec2 size1=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale);
|
||||
PlotNoLerp("##WaveformP1",wavePreview1,wave1->len+1,0,NULL,0,wave1->max,size1);
|
||||
ImGui::TableNextColumn();
|
||||
ImVec2 size2=ImVec2(ImGui::GetContentRegionAvail().x,64.0f*dpiScale);
|
||||
PlotNoLerp("##WaveformP2",wavePreview2,wave2->len+1,0,NULL,0,wave2->max,size2);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Wave 1");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SelWave1",&ins->ws.wave1,1,4)) {
|
||||
if (ins->ws.wave1<0) ins->ws.wave1=0;
|
||||
if (ins->ws.wave1>=(int)e->song.wave.size()) ins->ws.wave1=e->song.wave.size()-1;
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("Wave 2");
|
||||
ImGui::SameLine();
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
if (ImGui::InputInt("##SelWave2",&ins->ws.wave2,1,4)) {
|
||||
if (ins->ws.wave2<0) ins->ws.wave2=0;
|
||||
if (ins->ws.wave2>=(int)e->song.wave.size()) ins->ws.wave2=e->song.wave.size()-1;
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::InputScalar("Update Rate",ImGuiDataType_U8,&ins->ws.rateDivider,&_ONE,&_SEVEN);
|
||||
int speed=ins->ws.speed+1;
|
||||
if (ImGui::InputInt("Speed",&speed,1,16)) {
|
||||
if (speed<1) speed=1;
|
||||
if (speed>256) speed=256;
|
||||
ins->ws.speed=speed-1;
|
||||
}
|
||||
|
||||
ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_SEVEN);
|
||||
|
||||
ImGui::Checkbox("Global",&ins->ws.global);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginTabItem("Macros")) {
|
||||
float asFloat[256];
|
||||
int asInt[256];
|
||||
|
|
|
@ -250,11 +250,6 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.stepOnInsert=stepOnInsertB;
|
||||
}
|
||||
|
||||
bool effectCursorDirB=settings.effectCursorDir;
|
||||
if (ImGui::Checkbox("Move cursor to effect value on effect input",&effectCursorDirB)) {
|
||||
settings.effectCursorDir=effectCursorDirB;
|
||||
}
|
||||
|
||||
bool cursorPastePosB=settings.cursorPastePos;
|
||||
if (ImGui::Checkbox("Move cursor to end of clipboard content when pasting",&cursorPastePosB)) {
|
||||
settings.cursorPastePos=cursorPastePosB;
|
||||
|
@ -315,6 +310,17 @@ void FurnaceGUI::drawSettings() {
|
|||
settings.scrollStep=1;
|
||||
}
|
||||
|
||||
ImGui::Text("Effect input cursor behavior:");
|
||||
if (ImGui::RadioButton("Move down##eicb0",settings.effectCursorDir==0)) {
|
||||
settings.effectCursorDir=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Move to effect value (otherwise move down)##eicb1",settings.effectCursorDir==1)) {
|
||||
settings.effectCursorDir=1;
|
||||
}
|
||||
if (ImGui::RadioButton("Move to effect value/next effect and wrap around##eicb2",settings.effectCursorDir==2)) {
|
||||
settings.effectCursorDir=2;
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Audio/MIDI")) {
|
||||
|
@ -1503,7 +1509,7 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.loadJapanese,0,1);
|
||||
clampSetting(settings.fmLayout,0,3);
|
||||
clampSetting(settings.susPosition,0,1);
|
||||
clampSetting(settings.effectCursorDir,0,1);
|
||||
clampSetting(settings.effectCursorDir,0,2);
|
||||
clampSetting(settings.cursorPastePos,0,1);
|
||||
clampSetting(settings.titleBarInfo,0,3);
|
||||
clampSetting(settings.titleBarSys,0,1);
|
||||
|
|
Loading…
Reference in a new issue