implement several effects
volume slides, vibrato, slides and portamento
This commit is contained in:
parent
775b88dae2
commit
eb692ca9a9
|
@ -15,15 +15,23 @@ enum DivDispatchCmds {
|
||||||
|
|
||||||
struct DivCommand {
|
struct DivCommand {
|
||||||
DivDispatchCmds cmd;
|
DivDispatchCmds cmd;
|
||||||
unsigned char chan, value;
|
unsigned char chan;
|
||||||
DivCommand(DivDispatchCmds c, unsigned char ch, unsigned char val):
|
int value, value2;
|
||||||
|
DivCommand(DivDispatchCmds c, unsigned char ch, int val, int val2):
|
||||||
cmd(c),
|
cmd(c),
|
||||||
chan(ch),
|
chan(ch),
|
||||||
value(val) {}
|
value(val),
|
||||||
|
value2(val2) {}
|
||||||
|
DivCommand(DivDispatchCmds c, unsigned char ch, int val):
|
||||||
|
cmd(c),
|
||||||
|
chan(ch),
|
||||||
|
value(val),
|
||||||
|
value2(0) {}
|
||||||
DivCommand(DivDispatchCmds c, unsigned char ch):
|
DivCommand(DivDispatchCmds c, unsigned char ch):
|
||||||
cmd(c),
|
cmd(c),
|
||||||
chan(ch),
|
chan(ch),
|
||||||
value(0) {}
|
value(0),
|
||||||
|
value2(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DivDelayedCommand {
|
struct DivDelayedCommand {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../audio/sdl.h"
|
#include "../audio/sdl.h"
|
||||||
#include "platform/genesis.h"
|
#include "platform/genesis.h"
|
||||||
#include "platform/dummy.h"
|
#include "platform/dummy.h"
|
||||||
|
#include <math.h>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
void process(void* u, float** in, float** out, int inChans, int outChans, unsigned int size) {
|
void process(void* u, float** in, float** out, int inChans, int outChans, unsigned int size) {
|
||||||
|
@ -662,6 +663,10 @@ bool DivEngine::init() {
|
||||||
bbOut[0]=new short[got.bufsize];
|
bbOut[0]=new short[got.bufsize];
|
||||||
bbOut[1]=new short[got.bufsize];
|
bbOut[1]=new short[got.bufsize];
|
||||||
|
|
||||||
|
for (int i=0; i<60; i++) {
|
||||||
|
vibTable[i]=127*sin(((double)i/60.0)*(2*M_PI));
|
||||||
|
}
|
||||||
|
|
||||||
switch (song.system) {
|
switch (song.system) {
|
||||||
case DIV_SYSTEM_GENESIS:
|
case DIV_SYSTEM_GENESIS:
|
||||||
dispatch=new DivPlatformGenesis;
|
dispatch=new DivPlatformGenesis;
|
||||||
|
|
|
@ -9,8 +9,9 @@ struct DivChannelState {
|
||||||
std::vector<DivDelayedCommand> delayed;
|
std::vector<DivDelayedCommand> delayed;
|
||||||
int note, pitch, portaSpeed, portaNote;
|
int note, pitch, portaSpeed, portaNote;
|
||||||
int volume, volSpeed;
|
int volume, volSpeed;
|
||||||
int vibratoDepth, vibratoRate;
|
int vibratoDepth, vibratoRate, vibratoPos;
|
||||||
int tremoloDepth, tremoloRate;
|
int tremoloDepth, tremoloRate, tremoloPos;
|
||||||
|
bool doNote;
|
||||||
|
|
||||||
DivChannelState():
|
DivChannelState():
|
||||||
note(-1),
|
note(-1),
|
||||||
|
@ -21,8 +22,11 @@ struct DivChannelState {
|
||||||
volSpeed(0),
|
volSpeed(0),
|
||||||
vibratoDepth(0),
|
vibratoDepth(0),
|
||||||
vibratoRate(0),
|
vibratoRate(0),
|
||||||
|
vibratoPos(0),
|
||||||
tremoloDepth(0),
|
tremoloDepth(0),
|
||||||
tremoloRate(0) {}
|
tremoloRate(0),
|
||||||
|
tremoloPos(0),
|
||||||
|
doNote(false) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DivEngine {
|
class DivEngine {
|
||||||
|
@ -36,6 +40,8 @@ class DivEngine {
|
||||||
int changeOrd, changePos;
|
int changeOrd, changePos;
|
||||||
DivChannelState chan[17];
|
DivChannelState chan[17];
|
||||||
|
|
||||||
|
short vibTable[60];
|
||||||
|
|
||||||
blip_buffer_t* bb[2];
|
blip_buffer_t* bb[2];
|
||||||
short temp[2], prevSample[2];
|
short temp[2], prevSample[2];
|
||||||
short* bbOut[2];
|
short* bbOut[2];
|
||||||
|
|
|
@ -160,7 +160,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
rWrite(chanOffs[c.chan]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
||||||
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||||
}
|
}
|
||||||
chan[c.chan].freq=644.0f*pow(2.0f,((float)c.value/12.0f));
|
chan[c.chan].baseFreq=644.0f*pow(2.0f,((float)c.value/12.0f));
|
||||||
|
chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
chan[c.chan].keyOn=true;
|
chan[c.chan].keyOn=true;
|
||||||
chan[c.chan].active=true;
|
chan[c.chan].active=true;
|
||||||
|
@ -206,6 +207,33 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
||||||
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DIV_CMD_PITCH: {
|
||||||
|
chan[c.chan].pitch=c.value;
|
||||||
|
chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DIV_CMD_NOTE_PORTA: {
|
||||||
|
int destFreq=644.0f*pow(2.0f,((float)c.value2/12.0f));
|
||||||
|
bool return2=false;
|
||||||
|
if (destFreq>chan[c.chan].baseFreq) {
|
||||||
|
chan[c.chan].baseFreq=(chan[c.chan].baseFreq*(960+c.value))/960;
|
||||||
|
if (chan[c.chan].baseFreq>=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chan[c.chan].baseFreq=(chan[c.chan].baseFreq*(960-c.value))/960;
|
||||||
|
if (chan[c.chan].baseFreq<=destFreq) {
|
||||||
|
chan[c.chan].baseFreq=destFreq;
|
||||||
|
return2=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chan[c.chan].freq=(chan[c.chan].baseFreq*(2048+chan[c.chan].pitch))>>11;
|
||||||
|
chan[c.chan].freqChanged=true;
|
||||||
|
if (return2) return 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case DIV_CMD_SAMPLE_MODE: {
|
case DIV_CMD_SAMPLE_MODE: {
|
||||||
if (c.chan==5) {
|
if (c.chan==5) {
|
||||||
dacMode=c.value;
|
dacMode=c.value;
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
class DivPlatformGenesis: public DivDispatch {
|
class DivPlatformGenesis: public DivDispatch {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq;
|
int freq, baseFreq, pitch;
|
||||||
unsigned char ins;
|
unsigned char ins;
|
||||||
signed char konCycles;
|
signed char konCycles;
|
||||||
bool active, insChanged, freqChanged, keyOn, keyOff;
|
bool active, insChanged, freqChanged, keyOn, keyOff;
|
||||||
signed char vol;
|
signed char vol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
Channel(): freqH(0), freqL(0), freq(0), ins(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), vol(0), pan(3) {}
|
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(0), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), vol(0), pan(3) {}
|
||||||
};
|
};
|
||||||
Channel chan[10];
|
Channel chan[10];
|
||||||
struct QueuedWrite {
|
struct QueuedWrite {
|
||||||
|
|
|
@ -107,7 +107,7 @@ void DivEngine::nextRow() {
|
||||||
dispatch->dispatch(DivCommand(DIV_CMD_NOTE_OFF,i));
|
dispatch->dispatch(DivCommand(DIV_CMD_NOTE_OFF,i));
|
||||||
} else if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
|
} else if (!(pat->data[curRow][0]==0 && pat->data[curRow][1]==0)) {
|
||||||
chan[i].note=pat->data[curRow][0]+pat->data[curRow][1]*12;
|
chan[i].note=pat->data[curRow][0]+pat->data[curRow][1]*12;
|
||||||
dispatch->dispatch(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note));
|
chan[i].doNote=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// volume
|
// volume
|
||||||
|
@ -118,8 +118,8 @@ void DivEngine::nextRow() {
|
||||||
|
|
||||||
// effects
|
// effects
|
||||||
for (int j=0; j<song.pat[i]->effectRows; j++) {
|
for (int j=0; j<song.pat[i]->effectRows; j++) {
|
||||||
unsigned char effect=pat->data[curRow][4+(j<<1)];
|
short effect=pat->data[curRow][4+(j<<1)];
|
||||||
unsigned char effectVal=pat->data[curRow][5+(j<<1)];
|
short effectVal=pat->data[curRow][5+(j<<1)];
|
||||||
|
|
||||||
if (effectVal==-1) effectVal=0;
|
if (effectVal==-1) effectVal=0;
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ void DivEngine::nextRow() {
|
||||||
chan[i].portaNote=-1;
|
chan[i].portaNote=-1;
|
||||||
chan[i].portaSpeed=-1;
|
chan[i].portaSpeed=-1;
|
||||||
} else {
|
} else {
|
||||||
chan[i].portaNote=0x7f;
|
chan[i].portaNote=0x60;
|
||||||
chan[i].portaSpeed=effectVal;
|
chan[i].portaSpeed=effectVal;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -167,11 +167,13 @@ void DivEngine::nextRow() {
|
||||||
} else {
|
} else {
|
||||||
chan[i].portaNote=chan[i].note;
|
chan[i].portaNote=chan[i].note;
|
||||||
chan[i].portaSpeed=effectVal;
|
chan[i].portaSpeed=effectVal;
|
||||||
|
chan[i].doNote=false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x04: // vibrato
|
case 0x04: // vibrato
|
||||||
chan[i].vibratoDepth=effectVal&15;
|
chan[i].vibratoDepth=effectVal&15;
|
||||||
chan[i].vibratoRate=effectVal>>4;
|
chan[i].vibratoRate=effectVal>>4;
|
||||||
|
dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4));
|
||||||
break;
|
break;
|
||||||
case 0x0a: // volume ramp
|
case 0x0a: // volume ramp
|
||||||
if (effectVal!=0) {
|
if (effectVal!=0) {
|
||||||
|
@ -187,17 +189,24 @@ void DivEngine::nextRow() {
|
||||||
|
|
||||||
case 0xe1: // portamento up
|
case 0xe1: // portamento up
|
||||||
chan[i].portaNote=chan[i].note+(effectVal&15);
|
chan[i].portaNote=chan[i].note+(effectVal&15);
|
||||||
chan[i].portaSpeed=effectVal>>4;
|
chan[i].portaSpeed=(effectVal>>4)*3;
|
||||||
break;
|
break;
|
||||||
case 0xe2: // portamento down
|
case 0xe2: // portamento down
|
||||||
chan[i].portaNote=chan[i].note-(effectVal&15);
|
chan[i].portaNote=chan[i].note-(effectVal&15);
|
||||||
chan[i].portaSpeed=effectVal>>4;
|
chan[i].portaSpeed=(effectVal>>4)*3;
|
||||||
break;
|
break;
|
||||||
case 0xe5: // pitch
|
case 0xe5: // pitch
|
||||||
chan[i].pitch=effectVal-0x80;
|
chan[i].pitch=effectVal-0x80;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chan[i].doNote) {
|
||||||
|
chan[i].vibratoPos=0;
|
||||||
|
dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>2));
|
||||||
|
dispatch->dispatch(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note));
|
||||||
|
chan[i].doNote=false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +237,16 @@ void DivEngine::nextTick() {
|
||||||
if (chan[i].volume<0) chan[i].volume=0;
|
if (chan[i].volume<0) chan[i].volume=0;
|
||||||
dispatch->dispatch(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
dispatch->dispatch(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
||||||
}
|
}
|
||||||
|
if (chan[i].vibratoDepth>0) {
|
||||||
|
chan[i].vibratoPos+=chan[i].vibratoRate;
|
||||||
|
if (chan[i].vibratoPos>=60) chan[i].vibratoPos-=60;
|
||||||
|
dispatch->dispatch(DivCommand(DIV_CMD_PITCH,i,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos])>>4));
|
||||||
|
}
|
||||||
|
if (chan[i].portaSpeed>0) {
|
||||||
|
if (dispatch->dispatch(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed,chan[i].portaNote))==2) {
|
||||||
|
chan[i].portaSpeed=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// system tick
|
// system tick
|
||||||
|
|
Loading…
Reference in New Issue