N163: implement wave synth

I hope this does not break any songs
This commit is contained in:
tildearrow 2022-04-12 00:17:34 -05:00
parent 0ea6437a86
commit e77ecfd04b
6 changed files with 118 additions and 29 deletions

View file

@ -171,29 +171,43 @@ void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len
} }
} }
void DivPlatformN163::updateWave(int wave, int pos, int len) { void DivPlatformN163::updateWave(int ch, int wave, int pos, int len) {
len&=0xfc; // 4 nibble boundary len&=0xfc; // 4 nibble boundary
DivWavetable* wt=parent->getWave(wave); if (wave<0) {
for (int i=0; i<len; i++) { // load from wave synth
unsigned char addr=(pos+i); // address (nibble each) for (int i=0; i<len; i++) {
if (addr>=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area unsigned char addr=(pos+i); // address (nibble each)
break; if (addr>=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area
} break;
unsigned char mask=(addr&1)?0xf0:0x0f; }
if (wt->max<1 || wt->len<1) { unsigned char mask=(addr&1)?0xf0:0x0f;
rWriteMask(addr>>1,0,mask); int data=chan[ch].ws.output[i];
} else {
int data=wt->data[i*wt->len/len]*15/wt->max;
if (data<0) data=0;
if (data>15) data=15;
rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask); rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask);
} }
} else {
// load from custom
DivWavetable* wt=parent->getWave(wave);
for (int i=0; i<len; i++) {
unsigned char addr=(pos+i); // address (nibble each)
if (addr>=((0x78-(chanMax<<3))<<1)) { // avoid conflict with channel register area
break;
}
unsigned char mask=(addr&1)?0xf0:0x0f;
if (wt->max<1 || wt->len<1) {
rWriteMask(addr>>1,0,mask);
} else {
int data=wt->data[i*wt->len/len]*15/wt->max;
if (data<0) data=0;
if (data>15) data=15;
rWriteMask(addr>>1,(addr&1)?(data<<4):(data&0xf),mask);
}
}
} }
} }
void DivPlatformN163::updateWaveCh(int ch) { void DivPlatformN163::updateWaveCh(int ch) {
if (ch<=chanMax) { if (ch<=chanMax) {
updateWave(chan[ch].wave,chan[ch].wavePos,chan[ch].waveLen); updateWave(ch,-1,chan[ch].wavePos,chan[ch].waveLen);
if (chan[ch].active && !isMuted[ch]) { if (chan[ch].active && !isMuted[ch]) {
chan[ch].volumeChanged=true; chan[ch].volumeChanged=true;
} }
@ -241,6 +255,7 @@ void DivPlatformN163::tick() {
if (chan[i].std.wave.had) { if (chan[i].std.wave.had) {
if (chan[i].wave!=chan[i].std.wave.val) { if (chan[i].wave!=chan[i].std.wave.val) {
chan[i].wave=chan[i].std.wave.val; chan[i].wave=chan[i].std.wave.val;
chan[i].ws.changeWave1(chan[i].wave);
if (chan[i].waveMode&0x2) { if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true; chan[i].waveUpdated=true;
} }
@ -249,6 +264,7 @@ void DivPlatformN163::tick() {
if (chan[i].std.ex1.had) { if (chan[i].std.ex1.had) {
if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) { if (chan[i].waveLen!=(chan[i].std.ex1.val&0xfc)) {
chan[i].waveLen=chan[i].std.ex1.val&0xfc; chan[i].waveLen=chan[i].std.ex1.val&0xfc;
chan[i].ws.setWidth(chan[i].waveLen);
if (chan[i].waveMode&0x2) { if (chan[i].waveMode&0x2) {
chan[i].waveUpdated=true; chan[i].waveUpdated=true;
} }
@ -275,7 +291,7 @@ void DivPlatformN163::tick() {
if (chan[i].loadWave!=chan[i].std.ex3.val) { if (chan[i].loadWave!=chan[i].std.ex3.val) {
chan[i].loadWave=chan[i].std.ex3.val; chan[i].loadWave=chan[i].std.ex3.val;
if (chan[i].loadMode&0x2) { if (chan[i].loadMode&0x2) {
updateWave(chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc); updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
} }
} }
} }
@ -296,7 +312,7 @@ void DivPlatformN163::tick() {
if ((chan[i].loadMode&0x1)!=(chan[i].std.fms.val&0x1)) { // load now if ((chan[i].loadMode&0x1)!=(chan[i].std.fms.val&0x1)) { // load now
chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1); chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1);
if (chan[i].loadMode&0x1) { // rising edge if (chan[i].loadMode&0x1) { // rising edge
updateWave(chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc); updateWave(i,chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
} }
} }
} }
@ -315,6 +331,11 @@ void DivPlatformN163::tick() {
} }
chan[i].waveChanged=false; chan[i].waveChanged=false;
} }
if (chan[i].active) {
if (chan[i].ws.tick()) {
chan[i].waveUpdated=true;
}
}
if (chan[i].waveUpdated) { if (chan[i].waveUpdated) {
updateWaveCh(i); updateWaveCh(i);
if (chan[i].active) { if (chan[i].active) {
@ -353,11 +374,12 @@ int DivPlatformN163::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins); DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (chan[c.chan].insChanged) { if (chan[c.chan].insChanged) {
chan[c.chan].wave=ins->n163.wave; chan[c.chan].wave=ins->n163.wave;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
chan[c.chan].wavePos=ins->n163.wavePos; chan[c.chan].wavePos=ins->n163.wavePos;
chan[c.chan].waveLen=ins->n163.waveLen; chan[c.chan].waveLen=ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode; chan[c.chan].waveMode=ins->n163.waveMode;
chan[c.chan].waveChanged=true; chan[c.chan].waveChanged=true;
if (chan[c.chan].waveMode&0x3) { if (chan[c.chan].waveMode&0x3 || ins->ws.enabled) {
chan[c.chan].waveUpdated=true; chan[c.chan].waveUpdated=true;
} }
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
@ -374,6 +396,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].volumeChanged=true; chan[c.chan].volumeChanged=true;
} }
chan[c.chan].std.init(ins); chan[c.chan].std.init(ins);
chan[c.chan].ws.init(ins,chan[c.chan].waveLen,15,chan[c.chan].insChanged);
break; break;
} }
case DIV_CMD_NOTE_OFF: case DIV_CMD_NOTE_OFF:
@ -472,7 +495,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_N163_WAVE_LOAD: case DIV_CMD_N163_WAVE_LOAD:
chan[c.chan].loadWave=c.value; chan[c.chan].loadWave=c.value;
if (chan[c.chan].loadMode&0x2) { // load when every waveform changes if (chan[c.chan].loadMode&0x2) { // load when every waveform changes
updateWave(chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen); updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
} }
break; break;
case DIV_CMD_N163_WAVE_LOADPOS: case DIV_CMD_N163_WAVE_LOADPOS:
@ -484,13 +507,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_N163_WAVE_LOADMODE: case DIV_CMD_N163_WAVE_LOADMODE:
chan[c.chan].loadMode=c.value&0x3; chan[c.chan].loadMode=c.value&0x3;
if (chan[c.chan].loadMode&0x1) { // load now if (chan[c.chan].loadMode&0x1) { // load now
updateWave(chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen); updateWave(c.chan,chan[c.chan].loadWave,chan[c.chan].loadPos,chan[c.chan].loadLen);
} }
break; break;
case DIV_CMD_N163_GLOBAL_WAVE_LOAD: case DIV_CMD_N163_GLOBAL_WAVE_LOAD:
loadWave=c.value; loadWave=c.value;
if (loadMode&0x2) { // load when every waveform changes if (loadMode&0x2) { // load when every waveform changes
updateWave(loadWave,loadPos,loadLen); updateWave(c.chan,loadWave,loadPos,loadLen);
} }
break; break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS: case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
@ -502,7 +525,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE: case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE:
loadMode=c.value&0x3; loadMode=c.value&0x3;
if (loadMode&0x3) { // load now if (loadMode&0x3) { // load now
updateWave(loadWave,loadPos,loadLen); updateWave(c.chan,loadWave,loadPos,loadLen);
} }
break; break;
case DIV_CMD_N163_CHANNEL_LIMIT: case DIV_CMD_N163_CHANNEL_LIMIT:
@ -562,6 +585,7 @@ void DivPlatformN163::notifyWaveChange(int wave) {
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
if (chan[i].wave==wave) { if (chan[i].wave==wave) {
if (chan[i].waveMode&0x2) { if (chan[i].waveMode&0x2) {
chan[i].ws.changeWave1(wave);
chan[i].waveUpdated=true; chan[i].waveUpdated=true;
} }
} }
@ -601,6 +625,8 @@ void DivPlatformN163::reset() {
while (!writes.empty()) writes.pop(); while (!writes.empty()) writes.pop();
for (int i=0; i<8; i++) { for (int i=0; i<8; i++) {
chan[i]=DivPlatformN163::Channel(); chan[i]=DivPlatformN163::Channel();
chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,15,false);
} }
n163.reset(); n163.reset();

View file

@ -23,6 +23,7 @@
#include "../dispatch.h" #include "../dispatch.h"
#include <queue> #include <queue>
#include "../macroInt.h" #include "../macroInt.h"
#include "../waveSynth.h"
#include "sound/n163/n163.hpp" #include "sound/n163/n163.hpp"
class DivPlatformN163: public DivDispatch { class DivPlatformN163: public DivDispatch {
@ -35,6 +36,7 @@ class DivPlatformN163: public DivDispatch {
bool active, insChanged, freqChanged, volumeChanged, waveChanged, waveUpdated, keyOn, keyOff, inPorta; bool active, insChanged, freqChanged, volumeChanged, waveChanged, waveUpdated, keyOn, keyOff, inPorta;
signed char vol, outVol, resVol; signed char vol, outVol, resVol;
DivMacroInt std; DivMacroInt std;
DivWaveSynth ws;
Channel(): Channel():
freq(0), freq(0),
baseFreq(0), baseFreq(0),
@ -79,7 +81,7 @@ class DivPlatformN163: public DivDispatch {
n163_core n163; n163_core n163;
unsigned char regPool[128]; unsigned char regPool[128];
void updateWave(int wave, int pos, int len); void updateWave(int ch, int wave, int pos, int len);
void updateWaveCh(int ch); void updateWaveCh(int ch);
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);

View file

@ -18,6 +18,7 @@ bool DivWaveSynth::tick() {
bool updated=first; bool updated=first;
first=false; first=false;
if (!state.enabled) return updated; if (!state.enabled) return updated;
if (width<1) return false;
if (--divCounter<=0) { if (--divCounter<=0) {
// run effect // run effect
@ -84,8 +85,15 @@ bool DivWaveSynth::tick() {
return updated; return updated;
} }
void DivWaveSynth::setWidth(int val) {
width=val;
if (width<0) width=0;
if (width>256) width=256;
}
void DivWaveSynth::changeWave1(int num) { void DivWaveSynth::changeWave1(int num) {
DivWavetable* w1=e->getWave(num); DivWavetable* w1=e->getWave(num);
if (width<1) return;
for (int i=0; i<width; i++) { for (int i=0; i<width; i++) {
if (w1->max<1 || w1->len<1) { if (w1->max<1 || w1->len<1) {
wave1[i]=0; wave1[i]=0;
@ -103,6 +111,7 @@ void DivWaveSynth::changeWave1(int num) {
void DivWaveSynth::changeWave2(int num) { void DivWaveSynth::changeWave2(int num) {
DivWavetable* w2=e->getWave(num); DivWavetable* w2=e->getWave(num);
if (width<1) return;
for (int i=0; i<width; i++) { for (int i=0; i<width; i++) {
if (w2->max<1 || w2->len<1) { if (w2->max<1 || w2->len<1) {
wave2[i]=0; wave2[i]=0;

View file

@ -47,6 +47,11 @@ class DivWaveSynth {
* @return whether the wave has changed. * @return whether the wave has changed.
*/ */
bool tick(); bool tick();
/**
* set the wave width.
* @param value the width.
*/
void setWidth(int val);
/** /**
* change the first wave. * change the first wave.
* @param num wavetable number. * @param num wavetable number.

View file

@ -385,8 +385,8 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_OSC_BORDER,"",ImVec4(0.4f,0.6f,0.95f,1.0f)), D(GUI_COLOR_OSC_BORDER,"",ImVec4(0.4f,0.6f,0.95f,1.0f)),
D(GUI_COLOR_OSC_WAVE,"",ImVec4(0.95f,0.95f,1.0f,1.0f)), D(GUI_COLOR_OSC_WAVE,"",ImVec4(0.95f,0.95f,1.0f,1.0f)),
D(GUI_COLOR_OSC_WAVE_PEAK,"",ImVec4(0.95f,0.95f,1.0f,1.0f)), D(GUI_COLOR_OSC_WAVE_PEAK,"",ImVec4(0.95f,0.95f,1.0f,1.0f)),
D(GUI_COLOR_OSC_REF,"",ImVec4(0.3f,0.3f,0.3f,1.0f)), D(GUI_COLOR_OSC_REF,"",ImVec4(0.3,0.65f,1.0f,0.15f)),
D(GUI_COLOR_OSC_GUIDE,"",ImVec4(0.3,0.3f,0.3f,1.0f)), D(GUI_COLOR_OSC_GUIDE,"",ImVec4(0.3,0.65f,1.0f,0.13f)),
D(GUI_COLOR_VOLMETER_LOW,"",ImVec4(0.2f,0.6f,0.2f,1.0f)), D(GUI_COLOR_VOLMETER_LOW,"",ImVec4(0.2f,0.6f,0.2f,1.0f)),
D(GUI_COLOR_VOLMETER_HIGH,"",ImVec4(1.0f,0.9f,0.2f,1.0f)), D(GUI_COLOR_VOLMETER_HIGH,"",ImVec4(1.0f,0.9f,0.2f,1.0f)),

View file

@ -69,9 +69,6 @@ void FurnaceGUI::readOsc() {
e->oscReadPos=readPos; e->oscReadPos=readPos;
} }
// TODO:
// - draw reference level
// - draw guidelines
void FurnaceGUI::drawOsc() { void FurnaceGUI::drawOsc() {
if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) { if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) {
oscOpen=true; oscOpen=true;
@ -115,6 +112,7 @@ void FurnaceGUI::drawOsc() {
ImU32 color=ImGui::GetColorU32(isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE]); ImU32 color=ImGui::GetColorU32(isClipping?uiColors[GUI_COLOR_OSC_WAVE_PEAK]:uiColors[GUI_COLOR_OSC_WAVE]);
ImU32 borderColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BORDER]); ImU32 borderColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BORDER]);
ImU32 refColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_REF]); ImU32 refColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_REF]);
ImU32 guideColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_GUIDE]);
ImGui::ItemSize(size,style.FramePadding.y); ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) { if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) {
// https://github.com/ocornut/imgui/issues/3710 // https://github.com/ocornut/imgui/issues/3710
@ -162,11 +160,60 @@ void FurnaceGUI::drawOsc() {
dl->AddLine( dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)), ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.0f,0.5f)), ImLerp(rect.Min,rect.Max,ImVec2(1.0f,0.5f)),
refColor, refColor,
dpiScale dpiScale
); );
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.48f,0.125f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.52f,0.125f)),
guideColor,
dpiScale
);
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.47f,0.25f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.53f,0.25f)),
guideColor,
dpiScale
);
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.45f,0.375f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.55f,0.375f)),
guideColor,
dpiScale
);
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.45f,0.625f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.55f,0.625f)),
guideColor,
dpiScale
);
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.47f,0.75f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.53f,0.75f)),
guideColor,
dpiScale
);
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.48f,0.875f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.52f,0.875f)),
guideColor,
dpiScale
);
dl->AddLine(
ImLerp(rect.Min,rect.Max,ImVec2(0.5f,0.08f)),
ImLerp(rect.Min,rect.Max,ImVec2(0.5f,0.92f)),
guideColor,
dpiScale
);
for (size_t i=0; i<512; i++) { for (size_t i=0; i<512; i++) {
float x=(float)i/512.0f; float x=(float)i/512.0f;
float y=oscValues[i]*oscZoom; float y=oscValues[i]*oscZoom;