mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-23 13:05:11 +00:00
N163: implement wave synth
I hope this does not break any songs
This commit is contained in:
parent
0ea6437a86
commit
e77ecfd04b
6 changed files with 118 additions and 29 deletions
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue