Merge #304 - DO NOT COMPILE
Add Namco 163 Support Not final - changes needed
This commit is contained in:
commit
c4fc797578
23 changed files with 1239 additions and 3 deletions
|
|
@ -126,6 +126,19 @@ enum DivDispatchCmds {
|
|||
DIV_CMD_WS_SWEEP_TIME,
|
||||
DIV_CMD_WS_SWEEP_AMOUNT,
|
||||
|
||||
DIV_CMD_N163_WAVE_POSITION,
|
||||
DIV_CMD_N163_WAVE_LENGTH,
|
||||
DIV_CMD_N163_WAVE_MODE,
|
||||
DIV_CMD_N163_WAVE_LOAD,
|
||||
DIV_CMD_N163_WAVE_LOADPOS,
|
||||
DIV_CMD_N163_WAVE_LOADLEN,
|
||||
DIV_CMD_N163_WAVE_LOADMODE,
|
||||
DIV_CMD_N163_CHANNEL_LIMIT,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOAD,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADPOS,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,
|
||||
DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME,
|
||||
|
||||
DIV_CMD_MAX
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "platform/swan.h"
|
||||
#include "platform/lynx.h"
|
||||
#include "platform/bubsyswsg.h"
|
||||
#include "platform/n163.h"
|
||||
#include "platform/pet.h"
|
||||
#include "platform/vic20.h"
|
||||
#include "platform/dummy.h"
|
||||
|
|
@ -277,6 +278,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_BUBSYS_WSG:
|
||||
dispatch=new DivPlatformBubSysWSG;
|
||||
break;
|
||||
case DIV_SYSTEM_N163:
|
||||
dispatch=new DivPlatformN163;
|
||||
break;
|
||||
case DIV_SYSTEM_PET:
|
||||
dispatch=new DivPlatformPET;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -385,6 +385,8 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
w->write(amiga.noteFreq,120*sizeof(unsigned int));
|
||||
w->write(amiga.noteMap,120*sizeof(short));
|
||||
}
|
||||
|
||||
// N163
|
||||
}
|
||||
|
||||
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
||||
|
|
@ -740,6 +742,7 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
}
|
||||
}
|
||||
|
||||
// N163
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -385,6 +385,17 @@ struct DivInstrumentAmiga {
|
|||
}
|
||||
};
|
||||
|
||||
struct DivInstrumentN163 {
|
||||
int wave, wavePos, waveLen;
|
||||
unsigned char waveMode;
|
||||
|
||||
DivInstrumentN163():
|
||||
wave(-1),
|
||||
wavePos(0),
|
||||
waveLen(0),
|
||||
waveMode(0) {}
|
||||
};
|
||||
|
||||
struct DivInstrument {
|
||||
String name;
|
||||
bool mode;
|
||||
|
|
@ -394,6 +405,7 @@ struct DivInstrument {
|
|||
DivInstrumentGB gb;
|
||||
DivInstrumentC64 c64;
|
||||
DivInstrumentAmiga amiga;
|
||||
DivInstrumentN163 n163;
|
||||
|
||||
/**
|
||||
* save the instrument to a SafeWriter.
|
||||
|
|
|
|||
656
src/engine/platform/n163.cpp
Normal file
656
src/engine/platform/n163.cpp
Normal file
|
|
@ -0,0 +1,656 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "n163.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rRead(a,v) n163->addr_w(a); n163->data_r(v);
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define rWriteMask(a,v,m) if (!skipRegisterWrites) {writes.emplace(a,v,m); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWrite(c,a,v) \
|
||||
if (c<=chanMax) { \
|
||||
rWrite(0x78-(c<<3)+(a&7),v) \
|
||||
}
|
||||
|
||||
#define chWriteMask(c,a,v,m) \
|
||||
if (c<=chanMax) { \
|
||||
rWriteMask(0x78-(c<<3)+(a&7),v,m) \
|
||||
}
|
||||
|
||||
#define CHIP_FREQBASE (15*65536)
|
||||
|
||||
const char* regCheatSheetN163[]={
|
||||
"FreqL7", "40",
|
||||
"AccL7", "41",
|
||||
"FreqM7", "42",
|
||||
"AccM7", "43",
|
||||
"WavLen_FreqH7", "44",
|
||||
"AccH7", "45",
|
||||
"WavPos7", "46",
|
||||
"Vol7", "47",
|
||||
"FreqL6", "48",
|
||||
"AccL6", "49",
|
||||
"FreqM6", "4A",
|
||||
"AccM6", "4B",
|
||||
"WavLen_FreqH6", "4C",
|
||||
"AccH6", "4D",
|
||||
"WavPos6", "4E",
|
||||
"Vol6", "4F",
|
||||
"FreqL5", "50",
|
||||
"AccL5", "51",
|
||||
"FreqM5", "52",
|
||||
"AccM5", "53",
|
||||
"WavLen_FreqH5", "54",
|
||||
"AccH5", "55",
|
||||
"WavPos5", "56",
|
||||
"Vol5", "57",
|
||||
"FreqL4", "58",
|
||||
"AccL4", "59",
|
||||
"FreqM4", "5A",
|
||||
"AccM4", "5B",
|
||||
"WavLen_FreqH4", "5C",
|
||||
"AccH4", "5D",
|
||||
"WavPos4", "5E",
|
||||
"Vol4", "5F",
|
||||
"FreqL3", "60",
|
||||
"AccL3", "61",
|
||||
"FreqM3", "62",
|
||||
"AccM3", "63",
|
||||
"WavLen_FreqH3", "64",
|
||||
"AccH3", "65",
|
||||
"WavPos3", "66",
|
||||
"Vol3", "67",
|
||||
"FreqL2", "68",
|
||||
"AccL2", "69",
|
||||
"FreqM2", "6A",
|
||||
"AccM2", "6B",
|
||||
"WavLen_FreqH2", "6C",
|
||||
"AccH2", "6D",
|
||||
"WavPos2", "6E",
|
||||
"Vol2", "6F",
|
||||
"FreqL1", "70",
|
||||
"AccL1", "71",
|
||||
"FreqM1", "72",
|
||||
"AccM1", "73",
|
||||
"WavLen_FreqH1", "74",
|
||||
"AccH1", "75",
|
||||
"WavPos1", "76",
|
||||
"Vol1", "77",
|
||||
"FreqL0", "78",
|
||||
"AccL0", "79",
|
||||
"FreqM0", "7A",
|
||||
"AccM0", "7B",
|
||||
"WavLen_FreqH0", "7C",
|
||||
"AccH0", "7D",
|
||||
"WavPos0", "7E",
|
||||
"ChanMax_Vol0", "7F",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformN163::getRegisterSheet() {
|
||||
return regCheatSheetN163;
|
||||
}
|
||||
|
||||
const char* DivPlatformN163::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x10:
|
||||
return "10xx: Select waveform";
|
||||
break;
|
||||
case 0x11:
|
||||
return "11xx: Set waveform position in RAM (single nibble unit)";
|
||||
break;
|
||||
case 0x12:
|
||||
return "12xx: Set waveform length in RAM (04 to FC, 4 nibble unit)";
|
||||
break;
|
||||
case 0x13:
|
||||
return "130x: Change waveform update mode (0: off, bit 0: update now, bit 1: update when every waveform changes)";
|
||||
break;
|
||||
case 0x14:
|
||||
return "14xx: Select waveform for load to RAM";
|
||||
break;
|
||||
case 0x15:
|
||||
return "15xx: Set waveform position for load to RAM (single nibble unit)";
|
||||
break;
|
||||
case 0x16:
|
||||
return "16xx: Set waveform length for load to RAM (04 to FC, 4 nibble unit)";
|
||||
break;
|
||||
case 0x17:
|
||||
return "170x: Change waveform load mode (0: off, bit 0: load now, bit 1: load when every waveform changes)";
|
||||
break;
|
||||
case 0x18:
|
||||
return "180x: Change channel limits (0 to 7, x + 1)";
|
||||
break;
|
||||
case 0x20:
|
||||
return "20xx: (Global) Select waveform for load to RAM";
|
||||
break;
|
||||
case 0x21:
|
||||
return "21xx: (Global) Set waveform position for load to RAM (single nibble unit)";
|
||||
break;
|
||||
case 0x22:
|
||||
return "22xx: (Global) Set waveform length for load to RAM (04 to FC, 4 nibble unit)";
|
||||
break;
|
||||
case 0x23:
|
||||
return "230x: (Global) Change waveform load mode (0: off, bit 0: load now, bit 1: load when every waveform changes)";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t i=start; i<start+len; i++) {
|
||||
n163->tick();
|
||||
int out=(n163->out()<<6)*(chanMax+1); // scale to 16 bit
|
||||
if (out>32767) out=32767;
|
||||
if (out<-32768) out=-32768;
|
||||
bufL[i]=bufR[i]=out;
|
||||
|
||||
// command queue
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
n163->addr_w(w.addr);
|
||||
n163->data_w((n163->data_r()&~w.mask)|(w.val&w.mask));
|
||||
writes.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformN163::updateWave(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;
|
||||
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);
|
||||
if (chan[ch].active) {
|
||||
chan[ch].volumeChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformN163::tick() {
|
||||
for (int i=0; i<=chanMax; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.hadVol) {
|
||||
chan[i].outVol=(MIN(15,chan[i].std.vol)*(chan[i].vol&15))/15;
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
if (chan[i].outVol>15) chan[i].outVol=15;
|
||||
if (chan[i].resVol!=chan[i].outVol) {
|
||||
chan[i].resVol=chan[i].outVol;
|
||||
if (!isMuted[i]) {
|
||||
chan[i].volumeChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadArp) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arpMode) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadDuty) {
|
||||
if (chan[i].wavePos!=chan[i].std.duty) {
|
||||
chan[i].wavePos=chan[i].std.duty;
|
||||
if (chan[i].waveMode&0x2) {
|
||||
chan[i].waveUpdated=true;
|
||||
}
|
||||
chan[i].waveChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadWave) {
|
||||
if (chan[i].wave!=chan[i].std.wave) {
|
||||
chan[i].wave=chan[i].std.wave;
|
||||
if (chan[i].waveMode&0x2) {
|
||||
chan[i].waveUpdated=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadEx1) {
|
||||
if (chan[i].waveLen!=(chan[i].std.ex1&0xfc)) {
|
||||
chan[i].waveLen=chan[i].std.ex1&0xfc;
|
||||
if (chan[i].waveMode&0x2) {
|
||||
chan[i].waveUpdated=true;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadEx2) {
|
||||
if ((chan[i].waveMode&0x2)!=(chan[i].std.ex2&0x2)) { // update when every waveform changed
|
||||
chan[i].waveMode=(chan[i].waveMode&~0x2)|(chan[i].std.ex2&0x2);
|
||||
if (chan[i].waveMode&0x2) {
|
||||
chan[i].waveUpdated=true;
|
||||
chan[i].waveChanged=true;
|
||||
}
|
||||
}
|
||||
if ((chan[i].waveMode&0x1)!=(chan[i].std.ex2&0x1)) { // update waveform now
|
||||
chan[i].waveMode=(chan[i].waveMode&~0x1)|(chan[i].std.ex2&0x1);
|
||||
if (chan[i].waveMode&0x1) { // rising edge
|
||||
chan[i].waveUpdated=true;
|
||||
chan[i].waveChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadEx3) {
|
||||
if (chan[i].loadWave!=chan[i].std.ex3) {
|
||||
chan[i].loadWave=chan[i].std.ex3;
|
||||
if (chan[i].loadMode&0x2) {
|
||||
updateWave(chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadAlg) {
|
||||
if (chan[i].loadPos!=chan[i].std.alg) {
|
||||
chan[i].loadPos=chan[i].std.alg;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadFb) {
|
||||
if (chan[i].loadLen!=(chan[i].std.fb&0xfc)) {
|
||||
chan[i].loadLen=chan[i].std.fb&0xfc;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadFms) {
|
||||
if ((chan[i].loadMode&0x2)!=(chan[i].std.fms&0x2)) { // load when every waveform changes
|
||||
chan[i].loadMode=(chan[i].loadMode&~0x2)|(chan[i].std.fms&0x2);
|
||||
}
|
||||
if ((chan[i].loadMode&0x1)!=(chan[i].std.fms&0x1)) { // load now
|
||||
chan[i].loadMode=(chan[i].loadMode&~0x1)|(chan[i].std.fms&0x1);
|
||||
if (chan[i].loadMode&0x1) { // rising edge
|
||||
updateWave(chan[i].loadWave,chan[i].loadPos,chan[i].loadLen&0xfc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].volumeChanged) {
|
||||
if ((!chan[i].active) || isMuted[i]) {
|
||||
chWriteMask(i,0x7,0,0xf);
|
||||
} else {
|
||||
chWriteMask(i,0x7,chan[i].resVol&0xf,0xf);
|
||||
}
|
||||
chan[i].volumeChanged=false;
|
||||
}
|
||||
if (chan[i].waveChanged) {
|
||||
chWrite(i,0x6,chan[i].wavePos);
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].waveChanged=false;
|
||||
}
|
||||
if (chan[i].waveUpdated) {
|
||||
updateWaveCh(i);
|
||||
if (!chan[i].keyOff) chan[i].keyOn=true;
|
||||
chan[i].waveUpdated=false;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0);
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].wave<0) {
|
||||
chan[i].wave=0;
|
||||
if (chan[i].waveMode&0x2) {
|
||||
updateWaveCh(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOff && !isMuted[i]) {
|
||||
chWriteMask(i,0x07,0,0xf);
|
||||
}
|
||||
chWrite(i,0x0,chan[i].freq&0xff);
|
||||
chWrite(i,0x2,chan[i].freq>>8);
|
||||
chWrite(i,0x4,((256-chan[i].waveLen)&0xfc)|((chan[i].freq>>16)&3));
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformN163::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
if (chan[c.chan].insChanged) {
|
||||
chan[c.chan].wave=ins->n163.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) {
|
||||
chan[c.chan].waveUpdated=true;
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].resVol=chan[c.chan].vol;
|
||||
if (!isMuted[c.chan]) {
|
||||
chan[c.chan].volumeChanged=true;
|
||||
}
|
||||
chan[c.chan].std.init(ins);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
//chan[c.chan].std.init(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].insChanged=true;
|
||||
chan[c.chan].ins=c.value;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.hasVol) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
chan[c.chan].resVol=chan[c.chan].outVol;
|
||||
if (!isMuted[c.chan]) {
|
||||
chan[c.chan].volumeChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
return chan[c.chan].vol;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
if (chan[c.chan].waveMode&0x2) {
|
||||
chan[c.chan].waveUpdated=true;
|
||||
}
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_POSITION:
|
||||
chan[c.chan].wavePos=c.value;
|
||||
if (chan[c.chan].waveMode&0x2) {
|
||||
chan[c.chan].waveUpdated=true;
|
||||
}
|
||||
chan[c.chan].waveChanged=true;
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_LENGTH:
|
||||
chan[c.chan].waveLen=c.value&0xfc;
|
||||
if (chan[c.chan].waveMode&0x2) {
|
||||
chan[c.chan].waveUpdated=true;
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_MODE:
|
||||
chan[c.chan].waveMode=c.value&0x3;
|
||||
if (chan[c.chan].waveMode&0x3) { // update now
|
||||
chan[c.chan].waveUpdated=true;
|
||||
chan[c.chan].waveChanged=true;
|
||||
}
|
||||
break;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_LOADPOS:
|
||||
chan[c.chan].loadPos=c.value;
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_LOADLEN:
|
||||
chan[c.chan].loadLen=c.value&0xfc;
|
||||
break;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_N163_GLOBAL_WAVE_LOAD:
|
||||
loadWave=c.value;
|
||||
if (loadMode&0x2) { // load when every waveform changes
|
||||
updateWave(loadWave,loadPos,loadLen);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_N163_GLOBAL_WAVE_LOADPOS:
|
||||
loadPos=c.value;
|
||||
break;
|
||||
case DIV_CMD_N163_GLOBAL_WAVE_LOADLEN:
|
||||
loadLen=c.value&0xfc;
|
||||
break;
|
||||
case DIV_CMD_N163_GLOBAL_WAVE_LOADMODE:
|
||||
loadMode=c.value&0x3;
|
||||
if (loadMode&0x3) { // load now
|
||||
updateWave(loadWave,loadPos,loadLen);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_N163_CHANNEL_LIMIT:
|
||||
if (chanMax!=(c.value&0x7)) {
|
||||
chanMax=c.value&0x7;
|
||||
rWriteMask(0x7f,chanMax<<4,0x70);
|
||||
forceIns();
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.willArp && !chan[c.chan].std.arpMode)?(chan[c.chan].std.arp):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) {
|
||||
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||
chan[c.chan].keyOn=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformN163::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
chan[ch].volumeChanged=true;
|
||||
}
|
||||
|
||||
void DivPlatformN163::forceIns() {
|
||||
for (int i=0; i<=chanMax; i++) {
|
||||
chan[i].insChanged=true;
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].volumeChanged=true;
|
||||
chan[i].waveChanged=true;
|
||||
if (chan[i].waveMode&0x2) {
|
||||
chan[i].waveUpdated=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformN163::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
if (chan[i].waveMode&0x2) {
|
||||
chan[i].waveUpdated=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformN163::notifyInsChange(int ins) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformN163::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformN163::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformN163::getRegisterPool() {
|
||||
for (int i=0; i<128; i++) {
|
||||
regPool[i]=n163->reg(i);
|
||||
}
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformN163::getRegisterPoolSize() {
|
||||
return 128;
|
||||
}
|
||||
|
||||
void DivPlatformN163::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i]=DivPlatformN163::Channel();
|
||||
}
|
||||
|
||||
n163->reset();
|
||||
memset(regPool,0,128);
|
||||
|
||||
n163->set_disable(false);
|
||||
rWrite(0x7f,chanMax<<4);
|
||||
loadWave=-1;
|
||||
loadPos=0;
|
||||
loadLen=0;
|
||||
loadMode=0;
|
||||
}
|
||||
|
||||
void DivPlatformN163::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformN163::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformN163::setFlags(unsigned int flags) {
|
||||
switch (flags&0xf) {
|
||||
case 0x0: // NTSC
|
||||
rate=COLOR_NTSC/2.0;
|
||||
break;
|
||||
case 0x1: // PAL
|
||||
rate=COLOR_PAL*3.0/8.0;
|
||||
break;
|
||||
case 0x2: // Dendy
|
||||
rate=COLOR_PAL*2.0/5.0;
|
||||
break;
|
||||
}
|
||||
chanMax=(flags>>4)&7;
|
||||
chipClock=rate;
|
||||
rate/=15;
|
||||
}
|
||||
|
||||
int DivPlatformN163::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<8; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
n163=new n163_core();
|
||||
setFlags(flags);
|
||||
|
||||
reset();
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
void DivPlatformN163::quit() {
|
||||
delete n163;
|
||||
}
|
||||
|
||||
DivPlatformN163::~DivPlatformN163() {
|
||||
}
|
||||
107
src/engine/platform/n163.h
Normal file
107
src/engine/platform/n163.h
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _N163_H
|
||||
#define _N163_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "sound/n163/n163.hpp"
|
||||
|
||||
class DivPlatformN163: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, note;
|
||||
short ins, wave, wavePos, waveLen;
|
||||
unsigned char waveMode;
|
||||
short loadWave, loadPos, loadLen;
|
||||
unsigned char loadMode;
|
||||
bool active, insChanged, freqChanged, volumeChanged, waveChanged, waveUpdated, keyOn, keyOff, inPorta;
|
||||
signed char vol, outVol, resVol;
|
||||
DivMacroInt std;
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
wave(-1),
|
||||
wavePos(0),
|
||||
waveLen(0),
|
||||
waveMode(0),
|
||||
loadWave(-1),
|
||||
loadPos(0),
|
||||
loadLen(0),
|
||||
loadMode(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
volumeChanged(false),
|
||||
waveChanged(false),
|
||||
waveUpdated(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
vol(15),
|
||||
outVol(15),
|
||||
resVol(15) {}
|
||||
};
|
||||
Channel chan[8];
|
||||
bool isMuted[8];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
unsigned char mask;
|
||||
QueuedWrite(unsigned char a, unsigned char v, unsigned char m=~0): addr(a), val(v), mask(m) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
unsigned char chanMax;
|
||||
short loadWave, loadPos, loadLen;
|
||||
unsigned char loadMode;
|
||||
|
||||
n163_core *n163;
|
||||
unsigned char regPool[128];
|
||||
void updateWave(int wave, int pos, int len);
|
||||
void updateWaveCh(int ch);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
void setFlags(unsigned int flags);
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformN163();
|
||||
};
|
||||
|
||||
#endif
|
||||
137
src/engine/platform/sound/n163/n163.cpp
Normal file
137
src/engine/platform/sound/n163/n163.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Namco 163 Sound emulation core
|
||||
|
||||
This chip is one of NES mapper with sound expansion, This one is by Namco.
|
||||
|
||||
It has 1 to 8 wavetable channels, All channel registers and waveforms are stored to internal RAM.
|
||||
4 bit Waveforms are freely allocatable, and its length is variables; its can be stores many short waveforms or few long waveforms in RAM.
|
||||
|
||||
But waveforms are needs to squash, reallocate to avoid conflict with channel register area, each channel register size is 8 bytes per channels.
|
||||
|
||||
Sound output is time division multiplexed, it's can be captured only single channels output at once. in reason, More activated channels are less sound quality.
|
||||
|
||||
Sound register layout
|
||||
|
||||
Address Bit Description
|
||||
7654 3210
|
||||
|
||||
78-7f Channel 0
|
||||
78 xxxx xxxx Channel 0 Pitch input bit 0-7
|
||||
79 xxxx xxxx Channel 0 Accumulator bit 0-7*
|
||||
7a xxxx xxxx Channel 0 Pitch input bit 8-15
|
||||
7b xxxx xxxx Channel 0 Accumulator bit 8-15*
|
||||
7c xxxx xx-- Channel 0 Waveform length, 256 - (x * 4)
|
||||
---- --xx Channel 0 Pitch input bit 16-17
|
||||
7d xxxx xxxx Channel 0 Accumulator bit 16-23*
|
||||
7e xxxx xxxx Channel 0 Waveform base offset
|
||||
xxxx xxx- RAM byte (0 to 127)
|
||||
---- ---x RAM nibble
|
||||
---- ---0 Low nibble
|
||||
---- ---1 High nibble
|
||||
7f ---- xxxx Channel 0 Volume
|
||||
|
||||
7f Number of active channels
|
||||
7f -xxx ---- Number of active channels
|
||||
-000 ---- Channel 0 activated
|
||||
-001 ---- Channel 1 activated
|
||||
-010 ---- Channel 2 activated
|
||||
...
|
||||
-110 ---- Channel 6 activated
|
||||
-111 ---- Channel 7 activated
|
||||
|
||||
70-77 Channel 1 (Optional if activated)
|
||||
68-6f Channel 2 (Optional if activated)
|
||||
...
|
||||
48-4f Channel 6 (Optional if activated)
|
||||
40-47 Channel 7 (Optional if activated)
|
||||
|
||||
Rest of RAM area are for 4 bit Waveform and/or scratchpad.
|
||||
Each waveform byte has 2 nibbles packed, fetches LSB first, MSB next.
|
||||
---- xxxx 4 bit waveform, LSB
|
||||
xxxx ---- Same as above, MSB
|
||||
|
||||
Waveform address: Waveform base offset + Bit 16 to 23 of Accumulator, 1 LSB of result is nibble select, 7 MSB of result is Byte address in RAM.
|
||||
|
||||
Frequency formula:
|
||||
Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536)
|
||||
*/
|
||||
|
||||
#include "n163.hpp"
|
||||
|
||||
void n163_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
// 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.)
|
||||
if (m_disable)
|
||||
return;
|
||||
|
||||
// tick per each clock
|
||||
const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield<u32>(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency
|
||||
u32 accum = m_ram[m_voice_cycle + 1] | (u32(m_ram[m_voice_cycle + 3]) << 8) | ( u32(m_ram[m_voice_cycle + 5]) << 16); // 24 bit accumulator
|
||||
const u16 length = 256 - (m_ram[m_voice_cycle + 4] & 0xfc);
|
||||
const u8 addr = m_ram[m_voice_cycle + 6] + bitfield(accum, 16, 8);
|
||||
const s16 wave = (bitfield(m_ram[bitfield(addr, 1, 7)], bitfield(addr, 0) << 2, 4) - 8);
|
||||
const s16 volume = bitfield(m_ram[m_voice_cycle + 7], 0, 4);
|
||||
|
||||
// accumulate address
|
||||
accum = bitfield(accum + freq, 0, 24);
|
||||
if (bitfield(accum, 16, 8) >= length)
|
||||
accum = bitfield(accum, 0, 18);
|
||||
|
||||
// writeback to register
|
||||
m_ram[m_voice_cycle + 1] = bitfield(accum, 0, 8);
|
||||
m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8);
|
||||
m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8);
|
||||
|
||||
// update voice cycle
|
||||
m_voice_cycle -= 0x8;
|
||||
if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3)))
|
||||
m_voice_cycle = 0x78;
|
||||
|
||||
// output 4 bit waveform and volume, multiplexed
|
||||
m_out = wave * volume;
|
||||
}
|
||||
|
||||
void n163_core::reset()
|
||||
{
|
||||
// reset this chip
|
||||
m_disable = false;
|
||||
std::fill(std::begin(m_ram), std::end(m_ram), 0);
|
||||
m_voice_cycle = 0x78;
|
||||
m_addr_latch.reset();
|
||||
m_out = 0;
|
||||
}
|
||||
|
||||
// accessor
|
||||
void n163_core::addr_w(u8 data)
|
||||
{
|
||||
// 0xf800-0xffff Sound address, increment
|
||||
m_addr_latch.addr = bitfield(data, 0, 7);
|
||||
m_addr_latch.incr = bitfield(data, 7);
|
||||
}
|
||||
|
||||
void n163_core::data_w(u8 data, bool cpu_access)
|
||||
{
|
||||
// 0x4800-0x4fff Sound data write
|
||||
m_ram[m_addr_latch.addr] = data;
|
||||
|
||||
// address latch increment
|
||||
if (cpu_access && m_addr_latch.incr)
|
||||
m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7);
|
||||
}
|
||||
|
||||
u8 n163_core::data_r(bool cpu_access)
|
||||
{
|
||||
// 0x4800-0x4fff Sound data read
|
||||
const u8 ret = m_ram[m_addr_latch.addr];
|
||||
|
||||
// address latch increment
|
||||
if (cpu_access && m_addr_latch.incr)
|
||||
m_addr_latch.addr = bitfield(m_addr_latch.addr + 1, 0, 7);
|
||||
|
||||
return ret;
|
||||
}
|
||||
78
src/engine/platform/sound/n163/n163.hpp
Normal file
78
src/engine/platform/sound/n163/n163.hpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Namco 163 Sound emulation core
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_N163_HPP
|
||||
#define _VGSOUND_EMU_N163_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace n163
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed short s16;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
};
|
||||
|
||||
using namespace n163;
|
||||
class n163_core
|
||||
{
|
||||
public:
|
||||
// accessors, getters, setters
|
||||
void addr_w(u8 data);
|
||||
void data_w(u8 data, bool cpu_access = false);
|
||||
u8 data_r(bool cpu_access = false);
|
||||
|
||||
void set_disable(bool disable) { m_disable = disable; }
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// sound output pin
|
||||
s16 out() { return m_out; }
|
||||
|
||||
// register pool
|
||||
u8 reg(u8 addr) { return m_ram[addr & 0x7f]; }
|
||||
|
||||
private:
|
||||
// Address latch
|
||||
struct addr_latch_t
|
||||
{
|
||||
addr_latch_t()
|
||||
: addr(0)
|
||||
, incr(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
addr = 0;
|
||||
incr = 0;
|
||||
}
|
||||
|
||||
u8 addr : 7;
|
||||
u8 incr : 1;
|
||||
};
|
||||
|
||||
bool m_disable = false;
|
||||
u8 m_ram[0x80] = {0}; // internal 128 byte RAM
|
||||
u8 m_voice_cycle = 0x78; // Voice cycle for processing
|
||||
addr_latch_t m_addr_latch; // address latch
|
||||
s16 m_out = 0; // output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -134,6 +134,18 @@ const char* cmdName[DIV_CMD_MAX]={
|
|||
"WS_SWEEP_TIME",
|
||||
"WS_SWEEP_AMOUNT",
|
||||
|
||||
"N163_WAVE_POSITION",
|
||||
"N163_WAVE_LENGTH",
|
||||
"N163_WAVE_MODE",
|
||||
"N163_WAVE_LOAD",
|
||||
"N163_WAVE_LOADPOS",
|
||||
"N163_WAVE_LOADLEN",
|
||||
"N163_CHANNEL_LIMIT",
|
||||
"N163_GLOBAL_WAVE_LOAD",
|
||||
"N163_GLOBAL_WAVE_LOADPOS",
|
||||
"N163_GLOBAL_WAVE_LOADLEN",
|
||||
"N163_GLOBAL_WAVE_LOADMODE",
|
||||
|
||||
"ALWAYS_SET_VOLUME"
|
||||
};
|
||||
|
||||
|
|
@ -254,6 +266,51 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
|
|||
return false;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_N163:
|
||||
switch (effect) {
|
||||
case 0x10: // select instrument waveform
|
||||
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
|
||||
break;
|
||||
case 0x11: // select instrument waveform position in RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_POSITION,ch,effectVal));
|
||||
break;
|
||||
case 0x12: // select instrument waveform length in RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LENGTH,ch,effectVal));
|
||||
break;
|
||||
case 0x13: // change instrument waveform update mode
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_MODE,ch,effectVal));
|
||||
break;
|
||||
case 0x14: // select waveform for load to RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOAD,ch,effectVal));
|
||||
break;
|
||||
case 0x15: // select waveform position for load to RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADPOS,ch,effectVal));
|
||||
break;
|
||||
case 0x16: // select waveform length for load to RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADLEN,ch,effectVal));
|
||||
break;
|
||||
case 0x17: // change waveform load mode
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_WAVE_LOADMODE,ch,effectVal));
|
||||
break;
|
||||
case 0x18: // change channel limits
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_CHANNEL_LIMIT,ch,effectVal));
|
||||
break;
|
||||
case 0x20: // (global) select waveform for load to RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOAD,ch,effectVal));
|
||||
break;
|
||||
case 0x21: // (global) select waveform position for load to RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADPOS,ch,effectVal));
|
||||
break;
|
||||
case 0x22: // (global) select waveform length for load to RAM
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADLEN,ch,effectVal));
|
||||
break;
|
||||
case 0x23: // (global) change waveform load mode
|
||||
dispatchCmd(DivCommand(DIV_CMD_N163_GLOBAL_WAVE_LOADMODE,ch,effectVal));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_QSOUND:
|
||||
switch (effect) {
|
||||
case 0x10: // echo feedback
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue