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
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;
if (wave<0) {
// load from wave synth
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;
int data=chan[ch].ws.output[i];
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) {
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]) {
chan[ch].volumeChanged=true;
}
@ -241,6 +255,7 @@ void DivPlatformN163::tick() {
if (chan[i].std.wave.had) {
if (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) {
chan[i].waveUpdated=true;
}
@ -249,6 +264,7 @@ void DivPlatformN163::tick() {
if (chan[i].std.ex1.had) {
if (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) {
chan[i].waveUpdated=true;
}
@ -275,7 +291,7 @@ void DivPlatformN163::tick() {
if (chan[i].loadWave!=chan[i].std.ex3.val) {
chan[i].loadWave=chan[i].std.ex3.val;
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
chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms.val&0x1);
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;
}
if (chan[i].active) {
if (chan[i].ws.tick()) {
chan[i].waveUpdated=true;
}
}
if (chan[i].waveUpdated) {
updateWaveCh(i);
if (chan[i].active) {
@ -353,11 +374,12 @@ int DivPlatformN163::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (chan[c.chan].insChanged) {
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].waveLen=ins->n163.waveLen;
chan[c.chan].waveMode=ins->n163.waveMode;
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].insChanged=false;
@ -374,6 +396,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
chan[c.chan].volumeChanged=true;
}
chan[c.chan].std.init(ins);
chan[c.chan].ws.init(ins,chan[c.chan].waveLen,15,chan[c.chan].insChanged);
break;
}
case DIV_CMD_NOTE_OFF:
@ -472,7 +495,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_N163_WAVE_LOAD:
chan[c.chan].loadWave=c.value;
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;
case DIV_CMD_N163_WAVE_LOADPOS:
@ -484,13 +507,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_N163_WAVE_LOADMODE:
chan[c.chan].loadMode=c.value&0x3;
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;
case DIV_CMD_N163_GLOBAL_WAVE_LOAD:
loadWave=c.value;
if (loadMode&0x2) { // load when every waveform changes
updateWave(loadWave,loadPos,loadLen);
updateWave(c.chan,loadWave,loadPos,loadLen);
}
break;
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
@ -502,7 +525,7 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE:
loadMode=c.value&0x3;
if (loadMode&0x3) { // load now
updateWave(loadWave,loadPos,loadLen);
updateWave(c.chan,loadWave,loadPos,loadLen);
}
break;
case DIV_CMD_N163_CHANNEL_LIMIT:
@ -562,6 +585,7 @@ void DivPlatformN163::notifyWaveChange(int wave) {
for (int i=0; i<8; i++) {
if (chan[i].wave==wave) {
if (chan[i].waveMode&0x2) {
chan[i].ws.changeWave1(wave);
chan[i].waveUpdated=true;
}
}
@ -601,6 +625,8 @@ void DivPlatformN163::reset() {
while (!writes.empty()) writes.pop();
for (int i=0; i<8; i++) {
chan[i]=DivPlatformN163::Channel();
chan[i].ws.setEngine(parent);
chan[i].ws.init(NULL,32,15,false);
}
n163.reset();

View file

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

View file

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

View file

@ -47,6 +47,11 @@ class DivWaveSynth {
* @return whether the wave has changed.
*/
bool tick();
/**
* set the wave width.
* @param value the width.
*/
void setWidth(int val);
/**
* change the first wave.
* @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_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_REF,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_OSC_GUIDE,"",ImVec4(0.3,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.65f,1.0f,0.13f)),
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)),

View file

@ -69,9 +69,6 @@ void FurnaceGUI::readOsc() {
e->oscReadPos=readPos;
}
// TODO:
// - draw reference level
// - draw guidelines
void FurnaceGUI::drawOsc() {
if (nextWindow==GUI_WINDOW_OSCILLOSCOPE) {
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 borderColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_BORDER]);
ImU32 refColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_REF]);
ImU32 guideColor=ImGui::GetColorU32(uiColors[GUI_COLOR_OSC_GUIDE]);
ImGui::ItemSize(size,style.FramePadding.y);
if (ImGui::ItemAdd(rect,ImGui::GetID("wsDisplay"))) {
// https://github.com/ocornut/imgui/issues/3710
@ -162,11 +160,60 @@ void FurnaceGUI::drawOsc() {
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(1.0f,0.5f)),
refColor,
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++) {
float x=(float)i/512.0f;
float y=oscValues[i]*oscZoom;