initial bring-up of the wave synth

issue #16
This commit is contained in:
tildearrow 2022-04-08 02:11:33 -05:00
parent 0c1e2ddcb0
commit 5bd076d13e
9 changed files with 162 additions and 72 deletions

View file

@ -579,6 +579,7 @@ 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) {

View file

@ -516,7 +516,7 @@ struct DivInstrumentWaveSynth {
oneShot(false),
enabled(false),
global(false),
speed(1),
speed(0),
param1(0),
param2(0),
param3(0),

View file

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

View file

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

View file

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

View file

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

View file

@ -1,52 +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() {
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) {
if (e==NULL) return;
if (which==NULL) {
state=DivInstrumentWaveSynth();
}
state=which->ws;
width=w;
height=h;
pos=0;
stage=0;
divCounter=0;
first=true;
DivWavetable* w1=e->getWave(state.wave1);
DivWavetable* w2=e->getWave(state.wave2);
for (int i=0; i<width; i++) {
if (w1->max<1 || w1->len<1) {
wave1[i]=0;
} else {
int data=w1->data[i*w1->len/width]*height/w1->max;
if (data<0) data=0;
if (data>31) data=31;
wave1[i]=data;
}
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;
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>31) data=31;
wave2[i]=data;
}
changeWave1(state.wave1);
changeWave2(state.wave2);
}
}

View file

@ -29,16 +29,41 @@ class DivWaveSynth {
DivEngine* e;
DivInstrumentWaveSynth state;
int pos, stage, divCounter, width, height;
bool first;
bool first, activeChangedB;
unsigned char wave1[256];
unsigned char wave2[256];
int output[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();
/**
* 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():
@ -48,7 +73,8 @@ class DivWaveSynth {
divCounter(0),
width(32),
height(31),
first(false) {
first(false),
activeChangedB(false) {
memset(wave1,0,sizeof(int)*256);
memset(wave2,0,sizeof(int)*256);
memset(output,0,sizeof(int)*256);

View file

@ -2418,6 +2418,8 @@ void FurnaceGUI::drawInsEdit() {
ImGui::InputScalar("Amount",ImGuiDataType_U8,&ins->ws.param1,&_ONE,&_SEVEN);
ImGui::Checkbox("Global",&ins->ws.global);
ImGui::EndTabItem();
}
}