Merge branch 'master' into opz-improvements
This commit is contained in:
commit
dc47194e3d
236 changed files with 89738 additions and 71185 deletions
|
|
@ -364,6 +364,7 @@ bool DivCSPlayer::tick() {
|
|||
}
|
||||
|
||||
if (sendVolume || chan[i].volSpeed!=0) {
|
||||
int preSpeedVol=chan[i].volume;
|
||||
chan[i].volume+=chan[i].volSpeed;
|
||||
if (chan[i].volSpeedTarget!=-1) {
|
||||
bool atTarget=false;
|
||||
|
|
@ -377,7 +378,11 @@ bool DivCSPlayer::tick() {
|
|||
}
|
||||
|
||||
if (atTarget) {
|
||||
chan[i].volume=chan[i].volSpeedTarget;
|
||||
if (chan[i].volSpeed>0) {
|
||||
chan[i].volume=MAX(preSpeedVol,chan[i].volSpeedTarget);
|
||||
} else if (chan[i].volSpeed<0) {
|
||||
chan[i].volume=MIN(preSpeedVol,chan[i].volSpeedTarget);
|
||||
}
|
||||
chan[i].volSpeed=0;
|
||||
chan[i].volSpeedTarget=-1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -268,6 +268,46 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_FM_OPMASK, // (mask)
|
||||
|
||||
DIV_CMD_MULTIPCM_MIX_FM, // (value)
|
||||
DIV_CMD_MULTIPCM_MIX_PCM, // (value)
|
||||
DIV_CMD_MULTIPCM_LFO, // (value)
|
||||
DIV_CMD_MULTIPCM_VIB, // (value)
|
||||
DIV_CMD_MULTIPCM_AM, // (value)
|
||||
DIV_CMD_MULTIPCM_AR, // (value)
|
||||
DIV_CMD_MULTIPCM_D1R, // (value)
|
||||
DIV_CMD_MULTIPCM_DL, // (value)
|
||||
DIV_CMD_MULTIPCM_D2R, // (value)
|
||||
DIV_CMD_MULTIPCM_RC, // (value)
|
||||
DIV_CMD_MULTIPCM_RR, // (value)
|
||||
DIV_CMD_MULTIPCM_DAMP, // (value)
|
||||
DIV_CMD_MULTIPCM_PSEUDO_REVERB, // (value)
|
||||
DIV_CMD_MULTIPCM_LFO_RESET, // (value)
|
||||
DIV_CMD_MULTIPCM_LEVEL_DIRECT, // (value)
|
||||
|
||||
DIV_CMD_SID3_SPECIAL_WAVE,
|
||||
DIV_CMD_SID3_RING_MOD_SRC,
|
||||
DIV_CMD_SID3_HARD_SYNC_SRC,
|
||||
DIV_CMD_SID3_PHASE_MOD_SRC,
|
||||
DIV_CMD_SID3_WAVE_MIX,
|
||||
DIV_CMD_SID3_LFSR_FEEDBACK_BITS,
|
||||
DIV_CMD_SID3_1_BIT_NOISE,
|
||||
DIV_CMD_SID3_FILTER_DISTORTION,
|
||||
DIV_CMD_SID3_FILTER_OUTPUT_VOLUME,
|
||||
DIV_CMD_SID3_CHANNEL_INVERSION,
|
||||
DIV_CMD_SID3_FILTER_CONNECTION,
|
||||
DIV_CMD_SID3_FILTER_MATRIX,
|
||||
DIV_CMD_SID3_FILTER_ENABLE,
|
||||
|
||||
DIV_CMD_C64_PW_SLIDE,
|
||||
DIV_CMD_C64_CUTOFF_SLIDE,
|
||||
|
||||
DIV_CMD_SID3_PHASE_RESET,
|
||||
DIV_CMD_SID3_NOISE_PHASE_RESET,
|
||||
DIV_CMD_SID3_ENVELOPE_RESET,
|
||||
|
||||
DIV_CMD_SID3_CUTOFF_SCALING,
|
||||
DIV_CMD_SID3_RESONANCE_SCALING,
|
||||
|
||||
DIV_CMD_MAX
|
||||
};
|
||||
|
||||
|
|
@ -585,10 +625,10 @@ class DivDispatch {
|
|||
|
||||
/**
|
||||
* get "paired" channels.
|
||||
* @param chan the channel to query.
|
||||
* @return a DivChannelPair.
|
||||
* @param ch the channel to query.
|
||||
* @param ret the DivChannelPair vector of pairs.
|
||||
*/
|
||||
virtual DivChannelPair getPaired(int chan);
|
||||
virtual void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
|
||||
/**
|
||||
* get channel mode hints.
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
#include "platform/vb.h"
|
||||
#include "platform/k007232.h"
|
||||
#include "platform/ga20.h"
|
||||
#include "platform/supervision.h"
|
||||
#include "platform/upd1771c.h"
|
||||
#include "platform/sm8521.h"
|
||||
#include "platform/pv1000.h"
|
||||
#include "platform/k053260.h"
|
||||
|
|
@ -90,6 +92,7 @@
|
|||
#include "platform/nds.h"
|
||||
#include "platform/bifurcator.h"
|
||||
#include "platform/sid2.h"
|
||||
#include "platform/sid3.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
#include "song.h"
|
||||
|
|
@ -684,6 +687,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_GA20:
|
||||
dispatch=new DivPlatformGA20;
|
||||
break;
|
||||
case DIV_SYSTEM_SUPERVISION:
|
||||
dispatch=new DivPlatformSupervision;
|
||||
break;
|
||||
case DIV_SYSTEM_UPD1771C:
|
||||
dispatch=new DivPlatformUPD1771c;
|
||||
break;
|
||||
case DIV_SYSTEM_SM8521:
|
||||
dispatch=new DivPlatformSM8521;
|
||||
if (isRender) {
|
||||
|
|
@ -760,6 +769,27 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_SID2:
|
||||
dispatch=new DivPlatformSID2;
|
||||
break;
|
||||
case DIV_SYSTEM_SID3:
|
||||
dispatch=new DivPlatformSID3;
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
dispatch=new DivPlatformOPL;
|
||||
((DivPlatformOPL*)dispatch)->setOPLType(4,false);
|
||||
if (isRender) {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
|
||||
} else {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
dispatch=new DivPlatformOPL;
|
||||
((DivPlatformOPL*)dispatch)->setOPLType(4,true);
|
||||
if (isRender) {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4CoreRender",0));
|
||||
} else {
|
||||
((DivPlatformOPL*)dispatch)->setCore(eng->getConfInt("opl4Core",0));
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_DUMMY:
|
||||
dispatch=new DivPlatformDummy;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -137,9 +137,9 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0xf0:
|
||||
return _("F0xx: Set tick rate (bpm)");
|
||||
case 0xf1:
|
||||
return _("F1xx: Single tick note slide up");
|
||||
return _("F1xx: Single tick pitch up");
|
||||
case 0xf2:
|
||||
return _("F2xx: Single tick note slide down");
|
||||
return _("F2xx: Single tick pitch down");
|
||||
case 0xf3:
|
||||
return _("F3xx: Fine volume slide up");
|
||||
case 0xf4:
|
||||
|
|
@ -151,9 +151,9 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0xf7:
|
||||
return _("F7xx: Restart macro (see manual)");
|
||||
case 0xf8:
|
||||
return _("F8xx: Single tick volume slide up");
|
||||
return _("F8xx: Single tick volume up");
|
||||
case 0xf9:
|
||||
return _("F9xx: Single tick volume slide down");
|
||||
return _("F9xx: Single tick volume down");
|
||||
case 0xfa:
|
||||
return _("FAxx: Fast volume slide (0y: down; x0: up)");
|
||||
case 0xfc:
|
||||
|
|
@ -520,6 +520,15 @@ void DivEngine::initSongWithDesc(const char* description, bool inBase64, bool ol
|
|||
if (song.subsong[0]->hz<1.0) song.subsong[0]->hz=1.0;
|
||||
if (song.subsong[0]->hz>999.0) song.subsong[0]->hz=999.0;
|
||||
|
||||
curChanMask=c.getIntList("chanMask",{});
|
||||
for (unsigned char i:curChanMask) {
|
||||
int j=i-1;
|
||||
if (j<0) j=0;
|
||||
if (j>DIV_MAX_CHANS) j=DIV_MAX_CHANS-1;
|
||||
curSubSong->chanShow[j]=false;
|
||||
curSubSong->chanShowChanOsc[j]=false;
|
||||
}
|
||||
|
||||
song.author=getConfString("defaultAuthorName","");
|
||||
}
|
||||
|
||||
|
|
@ -754,6 +763,13 @@ int DivEngine::addSubSong() {
|
|||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.subsong.push_back(new DivSubSong);
|
||||
for (unsigned char i:curChanMask) {
|
||||
int j=i-1;
|
||||
if (j<0) j=0;
|
||||
if (j>DIV_MAX_CHANS) j=DIV_MAX_CHANS-1;
|
||||
song.subsong.back()->chanShow[j]=false;
|
||||
song.subsong.back()->chanShowChanOsc[j]=false;
|
||||
}
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.subsong.size()-1;
|
||||
|
|
@ -1559,9 +1575,9 @@ void* DivEngine::getDispatchChanState(int ch) {
|
|||
return disCont[dispatchOfChan[ch]].dispatch->getChanState(dispatchChanOfChan[ch]);
|
||||
}
|
||||
|
||||
DivChannelPair DivEngine::getChanPaired(int ch) {
|
||||
if (ch<0 || ch>=chans) return DivChannelPair();
|
||||
return disCont[dispatchOfChan[ch]].dispatch->getPaired(dispatchChanOfChan[ch]);
|
||||
void DivEngine::getChanPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (ch<0 || ch>=chans) return;
|
||||
disCont[dispatchOfChan[ch]].dispatch->getPaired(dispatchChanOfChan[ch],ret);
|
||||
}
|
||||
|
||||
DivChannelModeHints DivEngine::getChanModeHints(int ch) {
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ class DivWorkPool;
|
|||
|
||||
#define DIV_UNSTABLE
|
||||
|
||||
#define DIV_VERSION "dev217"
|
||||
#define DIV_ENGINE_VERSION 217
|
||||
#define DIV_VERSION "dev222"
|
||||
#define DIV_ENGINE_VERSION 222
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -518,6 +518,7 @@ class DivEngine {
|
|||
std::vector<DivCommand> cmdStream;
|
||||
std::vector<DivInstrumentType> possibleInsTypes;
|
||||
std::vector<DivEffectContainer> effectInst;
|
||||
std::vector<int> curChanMask;
|
||||
static DivSysDef* sysDefs[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapFur[DIV_MAX_CHIP_DEFS];
|
||||
static DivSystem sysFileMapDMF[DIV_MAX_CHIP_DEFS];
|
||||
|
|
@ -1150,7 +1151,7 @@ class DivEngine {
|
|||
void* getDispatchChanState(int chan);
|
||||
|
||||
// get channel pairs
|
||||
DivChannelPair getChanPaired(int chan);
|
||||
void getChanPaired(int chan, std::vector<DivChannelPair>& ret);
|
||||
|
||||
// get channel mode hints
|
||||
DivChannelModeHints getChanModeHints(int chan);
|
||||
|
|
|
|||
|
|
@ -127,7 +127,6 @@ struct TiunaMatches {
|
|||
static void writeCmd(std::vector<TiunaBytes>& cmds, TiunaCmd& cmd, unsigned char ch, int& lastWait, int fromTick, int toTick) {
|
||||
while (fromTick<toTick) {
|
||||
int val=MIN(toTick-fromTick,256);
|
||||
assert(val>0);
|
||||
if (lastWait!=val) {
|
||||
cmd.wait=val;
|
||||
lastWait=val;
|
||||
|
|
@ -504,12 +503,6 @@ void DivExportTiuna::run() {
|
|||
running=false;
|
||||
return;
|
||||
}
|
||||
SafeWriter dbg;
|
||||
dbg.init();
|
||||
dbg.writeText(fmt::format("renderedCmds size={}\n",renderedCmds.size()));
|
||||
for (const auto& i: confirmedMatches) {
|
||||
dbg.writeText(fmt::format("pos={},end={},id={}\n",i.pos,i.endPos,i.id,i.size));
|
||||
}
|
||||
|
||||
// write commands
|
||||
int totalSize=0;
|
||||
|
|
|
|||
|
|
@ -674,7 +674,10 @@ void DivExportZSM::run() {
|
|||
if (writes.size()>0)
|
||||
logD("zsmOps: Writing %d messages to chip %d",writes.size(),i);
|
||||
for (DivRegWrite& write: writes) {
|
||||
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
|
||||
if (i==YM) {
|
||||
if (done && write.addr==0x08 && (write.val&0x78)>0) continue; // don't process keydown on lookahead
|
||||
zsm.writeYM(write.addr&0xff,write.val);
|
||||
}
|
||||
if (i==VERA) {
|
||||
if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead
|
||||
zsm.writePSG(write.addr&0xff,write.val);
|
||||
|
|
|
|||
|
|
@ -2102,6 +2102,16 @@ bool DivEngine::loadFur(unsigned char* file, size_t len, int variantID) {
|
|||
}
|
||||
}
|
||||
|
||||
// SNES no anti-click
|
||||
if (ds.version<220) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_SNES) {
|
||||
ds.systemFlags[i].set("antiClick",false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ bool DivInstrumentC64::operator==(const DivInstrumentC64& other) {
|
|||
_C(dutyIsAbs) &&
|
||||
_C(filterIsAbs) &&
|
||||
_C(noTest) &&
|
||||
_C(resetDuty) &&
|
||||
_C(res) &&
|
||||
_C(cut) &&
|
||||
_C(hp) &&
|
||||
|
|
@ -174,7 +175,11 @@ bool DivInstrumentMultiPCM::operator==(const DivInstrumentMultiPCM& other) {
|
|||
_C(rc) &&
|
||||
_C(lfo) &&
|
||||
_C(vib) &&
|
||||
_C(am)
|
||||
_C(am) &&
|
||||
_C(damp) &&
|
||||
_C(pseudoReverb) &&
|
||||
_C(lfoReset) &&
|
||||
_C(levelDirect)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -254,6 +259,65 @@ bool DivInstrumentESFM::Operator::operator==(const DivInstrumentESFM::Operator&
|
|||
);
|
||||
}
|
||||
|
||||
bool DivInstrumentSID3::operator==(const DivInstrumentSID3& other) {
|
||||
return (
|
||||
_C(triOn) &&
|
||||
_C(sawOn) &&
|
||||
_C(pulseOn) &&
|
||||
_C(noiseOn) &&
|
||||
_C(a) &&
|
||||
_C(d) &&
|
||||
_C(s) &&
|
||||
_C(r) &&
|
||||
_C(sr) &&
|
||||
_C(duty) &&
|
||||
_C(ringMod) &&
|
||||
_C(oscSync) &&
|
||||
_C(phase_mod) &&
|
||||
_C(phase_mod_source) &&
|
||||
_C(ring_mod_source) &&
|
||||
_C(sync_source) &&
|
||||
_C(specialWaveOn) &&
|
||||
_C(oneBitNoise) &&
|
||||
_C(separateNoisePitch) &&
|
||||
_C(special_wave) &&
|
||||
_C(doWavetable) &&
|
||||
_C(dutyIsAbs) &&
|
||||
_C(resetDuty) &&
|
||||
_C(phaseInv) &&
|
||||
_C(feedback) &&
|
||||
_C(mixMode) &&
|
||||
_C(filt[0]) &&
|
||||
_C(filt[1]) &&
|
||||
_C(filt[2]) &&
|
||||
_C(filt[3])
|
||||
);
|
||||
}
|
||||
|
||||
bool DivInstrumentSID3::Filter::operator==(const DivInstrumentSID3::Filter& other) {
|
||||
return (
|
||||
_C(cutoff) &&
|
||||
_C(resonance) &&
|
||||
_C(output_volume) &&
|
||||
_C(distortion_level) &&
|
||||
_C(mode) &&
|
||||
_C(enabled) &&
|
||||
_C(init) &&
|
||||
_C(filter_matrix) &&
|
||||
_C(absoluteCutoff) &&
|
||||
_C(bindCutoffToNote) &&
|
||||
_C(bindCutoffToNoteStrength) &&
|
||||
_C(bindCutoffToNoteCenter) &&
|
||||
_C(bindCutoffToNoteDir) &&
|
||||
_C(bindCutoffOnNote) &&
|
||||
_C(bindResonanceToNote) &&
|
||||
_C(bindResonanceToNoteStrength) &&
|
||||
_C(bindResonanceToNoteCenter) &&
|
||||
_C(bindResonanceToNoteDir) &&
|
||||
_C(bindResonanceOnNote)
|
||||
);
|
||||
}
|
||||
|
||||
bool DivInstrumentPowerNoise::operator==(const DivInstrumentPowerNoise& other) {
|
||||
return _C(octave);
|
||||
}
|
||||
|
|
@ -618,7 +682,7 @@ void DivInstrument::writeFeature64(SafeWriter* w) {
|
|||
w->writeS(c64.duty);
|
||||
w->writeS((unsigned short)((c64.cut&4095)|((c64.res&15)<<12)));
|
||||
|
||||
w->writeC((c64.res>>4)&15);
|
||||
w->writeC(((c64.res>>4)&15)|(c64.resetDuty?0x10:0));
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
|
@ -892,6 +956,14 @@ void DivInstrument::writeFeatureMP(SafeWriter* w) {
|
|||
w->writeC(multipcm.vib);
|
||||
w->writeC(multipcm.am);
|
||||
|
||||
unsigned char next=(
|
||||
(multipcm.damp?1:0)&
|
||||
(multipcm.pseudoReverb?2:0)&
|
||||
(multipcm.lfoReset?4:0)&
|
||||
(multipcm.levelDirect?8:0)
|
||||
);
|
||||
w->writeC(next);
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
||||
|
|
@ -983,6 +1055,80 @@ void DivInstrument::writeFeatureS2(SafeWriter* w) {
|
|||
FEATURE_END;
|
||||
}
|
||||
|
||||
void DivInstrument::writeFeatureS3(SafeWriter* w) {
|
||||
FEATURE_BEGIN("S3");
|
||||
|
||||
w->writeC(
|
||||
(sid3.dutyIsAbs?0x80:0)|
|
||||
(sid3.noiseOn?8:0)|
|
||||
(sid3.pulseOn?4:0)|
|
||||
(sid3.sawOn?2:0)|
|
||||
(sid3.triOn?1:0)
|
||||
);
|
||||
|
||||
w->writeC(sid3.a);
|
||||
w->writeC(sid3.d);
|
||||
w->writeC(sid3.s);
|
||||
w->writeC(sid3.sr);
|
||||
w->writeC(sid3.r);
|
||||
|
||||
w->writeC(sid3.mixMode);
|
||||
|
||||
w->writeS(sid3.duty);
|
||||
|
||||
w->writeC(
|
||||
(sid3.phase_mod?0x80:0)|
|
||||
(sid3.specialWaveOn?0x40:0)|
|
||||
(sid3.oneBitNoise?0x20:0)|
|
||||
(sid3.separateNoisePitch?0x10:0)|
|
||||
(sid3.doWavetable?8:0)|
|
||||
(sid3.resetDuty?4:0)|
|
||||
(sid3.oscSync?2:0)|
|
||||
(sid3.ringMod?1:0)
|
||||
);
|
||||
|
||||
w->writeC(sid3.phase_mod_source);
|
||||
w->writeC(sid3.ring_mod_source);
|
||||
w->writeC(sid3.sync_source);
|
||||
w->writeC(sid3.special_wave);
|
||||
w->writeC(sid3.phaseInv);
|
||||
w->writeC(sid3.feedback);
|
||||
|
||||
w->writeC(4); // number of filters
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
w->writeC(
|
||||
(sid3.filt[i].enabled?0x80:0)|
|
||||
(sid3.filt[i].init?0x40:0)|
|
||||
(sid3.filt[i].absoluteCutoff?0x20:0)|
|
||||
(sid3.filt[i].bindCutoffToNote?0x10:0)|
|
||||
(sid3.filt[i].bindCutoffToNoteDir?8:0)|
|
||||
(sid3.filt[i].bindCutoffOnNote?4:0)|
|
||||
(sid3.filt[i].bindResonanceToNote?2:0)|
|
||||
(sid3.filt[i].bindResonanceToNoteDir?1:0)
|
||||
);
|
||||
|
||||
w->writeC(
|
||||
(sid3.filt[i].bindResonanceOnNote?0x80:0)
|
||||
);
|
||||
|
||||
w->writeS(sid3.filt[i].cutoff);
|
||||
|
||||
w->writeC(sid3.filt[i].resonance);
|
||||
w->writeC(sid3.filt[i].output_volume);
|
||||
w->writeC(sid3.filt[i].distortion_level);
|
||||
w->writeC(sid3.filt[i].mode);
|
||||
w->writeC(sid3.filt[i].filter_matrix);
|
||||
|
||||
w->writeC(sid3.filt[i].bindCutoffToNoteStrength);
|
||||
w->writeC(sid3.filt[i].bindCutoffToNoteCenter);
|
||||
w->writeC(sid3.filt[i].bindResonanceToNoteStrength);
|
||||
w->writeC(sid3.filt[i].bindResonanceToNoteCenter);
|
||||
}
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
||||
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) {
|
||||
size_t blockStartSeek=0;
|
||||
size_t blockEndSeek=0;
|
||||
|
|
@ -1029,6 +1175,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
bool featureEF=false;
|
||||
bool featurePN=false;
|
||||
bool featureS2=false;
|
||||
bool featureS3=false;
|
||||
|
||||
bool checkForWL=false;
|
||||
|
||||
|
|
@ -1272,6 +1419,17 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
feature64=true;
|
||||
featureS2=true;
|
||||
break;
|
||||
case DIV_INS_SID3:
|
||||
feature64=true;
|
||||
featureS2=true;
|
||||
featureS3=true;
|
||||
break;
|
||||
case DIV_INS_SUPERVISION:
|
||||
featureSM=true;
|
||||
if (amiga.useSample) featureSL=true;
|
||||
break;
|
||||
case DIV_INS_UPD1771C:
|
||||
break;
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
case DIV_INS_NULL:
|
||||
|
|
@ -1328,6 +1486,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
if (sid2!=defaultIns.sid2) {
|
||||
featureS2=true;
|
||||
}
|
||||
if (sid3!=defaultIns.sid3) {
|
||||
featureS3=true;
|
||||
}
|
||||
}
|
||||
|
||||
// check ins name
|
||||
|
|
@ -1479,6 +1640,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
|
|||
if (featureS2) {
|
||||
writeFeatureS2(w);
|
||||
}
|
||||
if (featureS3) {
|
||||
writeFeatureS3(w);
|
||||
}
|
||||
|
||||
if (fui && (featureSL || featureWL)) {
|
||||
w->write("EN",2);
|
||||
|
|
@ -1794,7 +1958,12 @@ void DivInstrument::readFeature64(SafeReader& reader, bool& volIsCutoff, short v
|
|||
c64.res=cr>>12;
|
||||
|
||||
if (version>=199) {
|
||||
c64.res|=((unsigned char)reader.readC())<<4;
|
||||
next=(unsigned char)reader.readC();
|
||||
c64.res|=(next&15)<<4;
|
||||
|
||||
if (version>=222) {
|
||||
c64.resetDuty=next&0x10;
|
||||
}
|
||||
}
|
||||
|
||||
READ_FEAT_END;
|
||||
|
|
@ -2212,6 +2381,14 @@ void DivInstrument::readFeatureMP(SafeReader& reader, short version) {
|
|||
multipcm.vib=reader.readC();
|
||||
multipcm.am=reader.readC();
|
||||
|
||||
if (version>=221) {
|
||||
unsigned char next=reader.readC();
|
||||
multipcm.damp=next&1;
|
||||
multipcm.pseudoReverb=next&2;
|
||||
multipcm.lfoReset=next&4;
|
||||
multipcm.levelDirect=next&8;
|
||||
}
|
||||
|
||||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
|
|
@ -2319,6 +2496,82 @@ void DivInstrument::readFeatureS2(SafeReader& reader, short version) {
|
|||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
void DivInstrument::readFeatureS3(SafeReader& reader, short version) {
|
||||
READ_FEAT_BEGIN;
|
||||
|
||||
unsigned char next=reader.readC();
|
||||
|
||||
sid3.dutyIsAbs=next&0x80;
|
||||
sid3.noiseOn=next&8;
|
||||
sid3.pulseOn=next&4;
|
||||
sid3.sawOn=next&2;
|
||||
sid3.triOn=next&1;
|
||||
|
||||
sid3.a=reader.readC();
|
||||
sid3.d=reader.readC();
|
||||
sid3.s=reader.readC();
|
||||
sid3.sr=reader.readC();
|
||||
sid3.r=reader.readC();
|
||||
|
||||
sid3.mixMode=reader.readC();
|
||||
|
||||
sid3.duty=reader.readS();
|
||||
|
||||
next=reader.readC();
|
||||
|
||||
sid3.phase_mod=next&0x80;
|
||||
sid3.specialWaveOn=next&0x40;
|
||||
sid3.oneBitNoise=next&0x20;
|
||||
sid3.separateNoisePitch=next&0x10;
|
||||
sid3.doWavetable=next&8;
|
||||
sid3.resetDuty=next&4;
|
||||
sid3.oscSync=next&2;
|
||||
sid3.ringMod=next&1;
|
||||
|
||||
sid3.phase_mod_source=reader.readC();
|
||||
sid3.ring_mod_source=reader.readC();
|
||||
sid3.sync_source=reader.readC();
|
||||
sid3.special_wave=reader.readC();
|
||||
sid3.phaseInv=reader.readC();
|
||||
sid3.feedback=reader.readC();
|
||||
|
||||
unsigned char numFilters=reader.readC();
|
||||
|
||||
for (int i=0; i<numFilters; i++) {
|
||||
if (i>=4) break;
|
||||
|
||||
next=reader.readC();
|
||||
|
||||
sid3.filt[i].enabled=next&0x80;
|
||||
sid3.filt[i].init=next&0x40;
|
||||
sid3.filt[i].absoluteCutoff=next&0x20;
|
||||
sid3.filt[i].bindCutoffToNote=next&0x10;
|
||||
sid3.filt[i].bindCutoffToNoteDir=next&8;
|
||||
sid3.filt[i].bindCutoffOnNote=next&4;
|
||||
sid3.filt[i].bindResonanceToNote=next&2;
|
||||
sid3.filt[i].bindResonanceToNoteDir=next&1;
|
||||
|
||||
next=reader.readC();
|
||||
|
||||
sid3.filt[i].bindResonanceOnNote=next&0x80;
|
||||
|
||||
sid3.filt[i].cutoff=reader.readS();
|
||||
|
||||
sid3.filt[i].resonance=reader.readC();
|
||||
sid3.filt[i].output_volume=reader.readC();
|
||||
sid3.filt[i].distortion_level=reader.readC();
|
||||
sid3.filt[i].mode=reader.readC();
|
||||
sid3.filt[i].filter_matrix=reader.readC();
|
||||
|
||||
sid3.filt[i].bindCutoffToNoteStrength=reader.readC();
|
||||
sid3.filt[i].bindCutoffToNoteCenter=reader.readC();
|
||||
sid3.filt[i].bindResonanceToNoteStrength=reader.readC();
|
||||
sid3.filt[i].bindResonanceToNoteCenter=reader.readC();
|
||||
}
|
||||
|
||||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
|
||||
unsigned char featCode[2];
|
||||
bool volIsCutoff=false;
|
||||
|
|
@ -2393,6 +2646,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
|
|||
readFeaturePN(reader,version);
|
||||
} else if (memcmp(featCode,"S2",2)==0) { // SID2
|
||||
readFeatureS2(reader,version);
|
||||
} else if (memcmp(featCode,"S3",2)==0) { // SID3
|
||||
readFeatureS3(reader,version);
|
||||
} else {
|
||||
if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) {
|
||||
// nothing
|
||||
|
|
@ -3486,4 +3741,4 @@ DivInstrument& DivInstrument::operator=( const DivInstrument& ins ) {
|
|||
*(DivInstrumentPOD*)this=ins;
|
||||
name=ins.name;
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_GBA_MINMOD=61,
|
||||
DIV_INS_BIFURCATOR=62,
|
||||
DIV_INS_SID2=63, // coincidence!
|
||||
DIV_INS_SUPERVISION=64,
|
||||
DIV_INS_UPD1771C=65,
|
||||
DIV_INS_SID3=66,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
|
@ -427,7 +430,7 @@ struct DivInstrumentC64 {
|
|||
unsigned char a, d, s, r;
|
||||
unsigned short duty;
|
||||
unsigned char ringMod, oscSync;
|
||||
bool toFilter, initFilter, dutyIsAbs, filterIsAbs, noTest;
|
||||
bool toFilter, initFilter, dutyIsAbs, filterIsAbs, noTest, resetDuty;
|
||||
unsigned char res;
|
||||
unsigned short cut;
|
||||
bool hp, lp, bp, ch3off;
|
||||
|
|
@ -454,6 +457,7 @@ struct DivInstrumentC64 {
|
|||
dutyIsAbs(false),
|
||||
filterIsAbs(false),
|
||||
noTest(false),
|
||||
resetDuty(true),
|
||||
res(0),
|
||||
cut(0),
|
||||
hp(false),
|
||||
|
|
@ -610,6 +614,7 @@ struct DivInstrumentFDS {
|
|||
struct DivInstrumentMultiPCM {
|
||||
unsigned char ar, d1r, dl, d2r, rr, rc;
|
||||
unsigned char lfo, vib, am;
|
||||
bool damp, pseudoReverb, lfoReset, levelDirect;
|
||||
|
||||
bool operator==(const DivInstrumentMultiPCM& other);
|
||||
bool operator!=(const DivInstrumentMultiPCM& other) {
|
||||
|
|
@ -618,7 +623,11 @@ struct DivInstrumentMultiPCM {
|
|||
|
||||
DivInstrumentMultiPCM():
|
||||
ar(15), d1r(15), dl(0), d2r(0), rr(15), rc(15),
|
||||
lfo(0), vib(0), am(0) {
|
||||
lfo(0), vib(0), am(0),
|
||||
damp(false),
|
||||
pseudoReverb(false),
|
||||
lfoReset(false),
|
||||
levelDirect(true) {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -862,6 +871,111 @@ struct DivInstrumentSID2 {
|
|||
noiseMode(0) {}
|
||||
};
|
||||
|
||||
struct DivInstrumentSID3 {
|
||||
bool triOn, sawOn, pulseOn, noiseOn;
|
||||
unsigned char a, d, s, r;
|
||||
unsigned char sr;
|
||||
unsigned short duty;
|
||||
unsigned char ringMod, oscSync;
|
||||
bool phase_mod;
|
||||
unsigned char phase_mod_source, ring_mod_source, sync_source;
|
||||
bool specialWaveOn;
|
||||
bool oneBitNoise;
|
||||
bool separateNoisePitch;
|
||||
unsigned char special_wave;
|
||||
bool doWavetable;
|
||||
bool dutyIsAbs;
|
||||
bool resetDuty;
|
||||
unsigned char phaseInv;
|
||||
unsigned char feedback;
|
||||
unsigned char mixMode;
|
||||
|
||||
struct Filter {
|
||||
unsigned short cutoff;
|
||||
unsigned char resonance;
|
||||
unsigned char output_volume;
|
||||
unsigned char distortion_level;
|
||||
unsigned char mode;
|
||||
bool enabled;
|
||||
bool init;
|
||||
unsigned char filter_matrix;
|
||||
|
||||
// this is done purely in software
|
||||
bool absoluteCutoff;
|
||||
bool bindCutoffToNote;
|
||||
unsigned char bindCutoffToNoteStrength; // how much cutoff changes over e.g. 1 semitone
|
||||
unsigned char bindCutoffToNoteCenter; // central note of the cutoff change
|
||||
bool bindCutoffToNoteDir; // if we decrease or increase cutoff if e.g. we go upper in note space
|
||||
bool bindCutoffOnNote; // only do cutoff scaling once, on new note
|
||||
|
||||
bool bindResonanceToNote;
|
||||
unsigned char bindResonanceToNoteStrength; // how much resonance changes over e.g. 1 semitone
|
||||
unsigned char bindResonanceToNoteCenter; // central note of the resonance change
|
||||
bool bindResonanceToNoteDir; // if we decrease or increase resonance if e.g. we go upper in note space
|
||||
bool bindResonanceOnNote; // only do resonance scaling once, on new note
|
||||
|
||||
bool operator==(const Filter& other);
|
||||
bool operator!=(const Filter& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
Filter():
|
||||
cutoff(0),
|
||||
resonance(0),
|
||||
output_volume(0),
|
||||
distortion_level(0),
|
||||
mode(0),
|
||||
enabled(false),
|
||||
init(false),
|
||||
filter_matrix(0),
|
||||
absoluteCutoff(false),
|
||||
bindCutoffToNote(false),
|
||||
bindCutoffToNoteStrength(0),
|
||||
bindCutoffToNoteCenter(0),
|
||||
bindCutoffToNoteDir(false),
|
||||
bindCutoffOnNote(false),
|
||||
bindResonanceToNote(false),
|
||||
bindResonanceToNoteStrength(0),
|
||||
bindResonanceToNoteCenter(0),
|
||||
bindResonanceToNoteDir(false),
|
||||
bindResonanceOnNote(false) {}
|
||||
} filt[4];
|
||||
|
||||
bool operator==(const DivInstrumentSID3& other);
|
||||
bool operator!=(const DivInstrumentSID3& other) {
|
||||
return !(*this==other);
|
||||
}
|
||||
DivInstrumentSID3():
|
||||
triOn(false),
|
||||
sawOn(true),
|
||||
pulseOn(false),
|
||||
noiseOn(false),
|
||||
a(0),
|
||||
d(64),
|
||||
s(0),
|
||||
r(0),
|
||||
sr(0),
|
||||
duty(32768),
|
||||
ringMod(0),
|
||||
oscSync(0),
|
||||
phase_mod(false),
|
||||
phase_mod_source(0),
|
||||
ring_mod_source(0),
|
||||
sync_source(0),
|
||||
specialWaveOn(false),
|
||||
oneBitNoise(false),
|
||||
separateNoisePitch(false),
|
||||
special_wave(0),
|
||||
doWavetable(false),
|
||||
dutyIsAbs(true),
|
||||
resetDuty(false),
|
||||
phaseInv(0),
|
||||
feedback(0),
|
||||
mixMode(0) {
|
||||
filt[0].mode=16|32; // default settings so filter just works, connect to input and channel output
|
||||
filt[0].output_volume=0xff;
|
||||
}
|
||||
};
|
||||
|
||||
struct DivInstrumentPOD {
|
||||
DivInstrumentType type;
|
||||
DivInstrumentFM fm;
|
||||
|
|
@ -880,6 +994,7 @@ struct DivInstrumentPOD {
|
|||
DivInstrumentESFM esfm;
|
||||
DivInstrumentPowerNoise powernoise;
|
||||
DivInstrumentSID2 sid2;
|
||||
DivInstrumentSID3 sid3;
|
||||
|
||||
DivInstrumentPOD() :
|
||||
type(DIV_INS_FM) {
|
||||
|
|
@ -978,6 +1093,7 @@ struct DivInstrument : DivInstrumentPOD {
|
|||
void writeFeatureEF(SafeWriter* w);
|
||||
void writeFeaturePN(SafeWriter* w);
|
||||
void writeFeatureS2(SafeWriter* w);
|
||||
void writeFeatureS3(SafeWriter* w);
|
||||
|
||||
void readFeatureNA(SafeReader& reader, short version);
|
||||
void readFeatureFM(SafeReader& reader, short version);
|
||||
|
|
@ -1001,6 +1117,7 @@ struct DivInstrument : DivInstrumentPOD {
|
|||
void readFeatureEF(SafeReader& reader, short version);
|
||||
void readFeaturePN(SafeReader& reader, short version);
|
||||
void readFeatureS2(SafeReader& reader, short version);
|
||||
void readFeatureS3(SafeReader& reader, short version);
|
||||
|
||||
DivDataErrors readInsDataOld(SafeReader& reader, short version);
|
||||
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ unsigned short DivDispatch::getPan(int chan) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
DivChannelPair DivDispatch::getPaired(int chan) {
|
||||
return DivChannelPair();
|
||||
void DivDispatch::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
}
|
||||
|
||||
DivChannelModeHints DivDispatch::getModeHints(int chan) {
|
||||
|
|
|
|||
|
|
@ -762,7 +762,20 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,14 +113,15 @@ const unsigned char dacLogTableAY[256]={
|
|||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
|
||||
};
|
||||
|
||||
void DivPlatformAY8910::runDAC() {
|
||||
void DivPlatformAY8910::runDAC(int runRate) {
|
||||
if (runRate==0) runRate=dacRate;
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].active && (chan[i].curPSGMode.val&8) && chan[i].dac.sample!=-1) {
|
||||
chan[i].dac.period+=chan[i].dac.rate;
|
||||
bool end=false;
|
||||
bool changed=false;
|
||||
int prevOut=chan[i].dac.out;
|
||||
while (chan[i].dac.period>dacRate && !end) {
|
||||
while (chan[i].dac.period>runRate && !end) {
|
||||
DivSample* s=parent->getSample(chan[i].dac.sample);
|
||||
if (s->samples<=0 || chan[i].dac.pos<0 || chan[i].dac.pos>=(int)s->samples) {
|
||||
chan[i].dac.sample=-1;
|
||||
|
|
@ -143,7 +144,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
end=true;
|
||||
break;
|
||||
}
|
||||
chan[i].dac.period-=dacRate;
|
||||
chan[i].dac.period-=runRate;
|
||||
}
|
||||
if (changed && !end) {
|
||||
if (!isMuted[i]) {
|
||||
|
|
@ -154,13 +155,15 @@ void DivPlatformAY8910::runDAC() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::runTFX() {
|
||||
void DivPlatformAY8910::runTFX(int runRate) {
|
||||
/*
|
||||
developer's note: if you are checking for intellivision
|
||||
make sure to add "&& selCore"
|
||||
because for some reason, the register remap doesn't work
|
||||
when the user uses AtomicSSG core
|
||||
*/
|
||||
float counterRatio=1.0;
|
||||
if (runRate!=0) counterRatio=(double)rate/(double)runRate;
|
||||
int timerPeriod, output;
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) {
|
||||
|
|
@ -182,9 +185,9 @@ void DivPlatformAY8910::runTFX() {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
chan[i].tfx.counter += 1;
|
||||
chan[i].tfx.counter += counterRatio;
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
chan[i].tfx.out ^= 1;
|
||||
output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol)));
|
||||
// TODO: fix this stupid crackling noise that happens
|
||||
|
|
@ -201,7 +204,7 @@ void DivPlatformAY8910::runTFX() {
|
|||
}
|
||||
}
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
if (!isMuted[i]) {
|
||||
if (intellivision && selCore) {
|
||||
immWrite(0xa, ayEnvMode);
|
||||
|
|
@ -211,7 +214,7 @@ void DivPlatformAY8910::runTFX() {
|
|||
}
|
||||
}
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
}
|
||||
}
|
||||
if (chan[i].tfx.num > 0) {
|
||||
|
|
@ -327,12 +330,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
|
||||
void DivPlatformAY8910::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
writes.clear();
|
||||
int rate=(int)(chipClock/sRate);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
for (int h=0; h<rate; h++) {
|
||||
runDAC();
|
||||
runTFX();
|
||||
}
|
||||
runDAC(sRate);
|
||||
runTFX(sRate);
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
stream.push_back(DivDelayedWrite(i,w.addr,w.val));
|
||||
|
|
|
|||
|
|
@ -78,7 +78,9 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
} dac;
|
||||
|
||||
struct TFX {
|
||||
int period, counter, offset, den, num, mode, lowBound, out;
|
||||
int period;
|
||||
float counter;
|
||||
int offset, den, num, mode, lowBound, out;
|
||||
TFX():
|
||||
period(0),
|
||||
counter(0),
|
||||
|
|
@ -156,8 +158,8 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void runDAC();
|
||||
void runTFX();
|
||||
void runDAC(int runRate=0);
|
||||
void runTFX(int runRate=0);
|
||||
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
|
|
|
|||
|
|
@ -561,12 +561,11 @@ float DivPlatformC140::getPostAmp() {
|
|||
return 3.0f;
|
||||
}
|
||||
|
||||
DivChannelPair DivPlatformC140::getPaired(int ch) {
|
||||
if (!is219) return DivChannelPair();
|
||||
void DivPlatformC140::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (!is219) return;
|
||||
if ((ch&3)==0) {
|
||||
return DivChannelPair(bankLabel[ch>>2],ch+1,ch+2,ch+3,-1,-1,-1,-1,-1);
|
||||
ret.push_back(DivChannelPair(bankLabel[ch>>2],ch+1,ch+2,ch+3,-1,-1,-1,-1,-1));
|
||||
}
|
||||
return DivChannelPair();
|
||||
}
|
||||
|
||||
const void* DivPlatformC140::getSampleMem(int index) {
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ class DivPlatformC140: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
|
|
|
|||
|
|
@ -158,6 +158,21 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
int i=chanOrder[_i];
|
||||
|
||||
chan[i].std.next();
|
||||
|
||||
if (sysTick) {
|
||||
if (chan[i].pw_slide!=0) {
|
||||
chan[i].duty-=chan[i].pw_slide;
|
||||
chan[i].duty=CLAMP(chan[i].duty,0,0xfff);
|
||||
rWrite(i*7+2,chan[i].duty&0xff);
|
||||
rWrite(i*7+3,(chan[i].duty>>8)|(chan[i].outVol<<4));
|
||||
}
|
||||
if (cutoff_slide!=0) {
|
||||
filtCut+=cutoff_slide;
|
||||
filtCut=CLAMP(filtCut,0,0x7ff);
|
||||
updateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].std.vol.had) {
|
||||
vol=MIN(15,chan[i].std.vol.val);
|
||||
willUpdateFilter=true;
|
||||
|
|
@ -305,7 +320,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].test=false;
|
||||
if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) {
|
||||
if (((chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) && ins->c64.resetDuty) || chan[c.chan].resetDuty) {
|
||||
chan[c.chan].duty=ins->c64.duty;
|
||||
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
|
||||
rWrite(c.chan*7+3,chan[c.chan].duty>>8);
|
||||
|
|
@ -535,6 +550,12 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
chan[c.chan].release=c.value&15;
|
||||
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
|
||||
break;
|
||||
case DIV_CMD_C64_PW_SLIDE:
|
||||
chan[c.chan].pw_slide=c.value*c.value2;
|
||||
break;
|
||||
case DIV_CMD_C64_CUTOFF_SLIDE:
|
||||
cutoff_slide=c.value*c.value2;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
|
|
@ -600,6 +621,23 @@ DivMacroInt* DivPlatformC64::getChanMacroInt(int ch) {
|
|||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
void DivPlatformC64::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (chan[ch].ring) {
|
||||
if (ch==0){
|
||||
ret.push_back(DivChannelPair(_("ring"),2));
|
||||
} else {
|
||||
ret.push_back(DivChannelPair(_("ring"),(ch-1)%3));
|
||||
}
|
||||
}
|
||||
if (chan[ch].sync) {
|
||||
if (ch==0) {
|
||||
ret.push_back(DivChannelPair(_("sync"),2));
|
||||
} else {
|
||||
ret.push_back(DivChannelPair(_("sync"),(ch-1)%3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformC64::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
ret.count=1;
|
||||
|
|
@ -653,8 +691,11 @@ void DivPlatformC64::reset() {
|
|||
chan[i].std.setEngine(parent);
|
||||
fakeLow[i]=0;
|
||||
fakeBand[i]=0;
|
||||
chan[i].pw_slide=0;
|
||||
}
|
||||
|
||||
cutoff_slide=0;
|
||||
|
||||
if (sidCore==2) {
|
||||
dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,needInitTables);
|
||||
dSID_setMuteMask(
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
short duty;
|
||||
bool sweepChanged, filter;
|
||||
bool resetMask, resetFilter, resetDuty, gate, ring, sync, test;
|
||||
short pw_slide;
|
||||
Channel():
|
||||
SharedChannel<signed char>(15),
|
||||
prevFreq(65535),
|
||||
|
|
@ -56,7 +57,8 @@ class DivPlatformC64: public DivDispatch {
|
|||
gate(true),
|
||||
ring(false),
|
||||
sync(false),
|
||||
test(false) {}
|
||||
test(false),
|
||||
pw_slide(0) {}
|
||||
};
|
||||
Channel chan[3];
|
||||
DivDispatchOscBuffer* oscBuf[3];
|
||||
|
|
@ -76,6 +78,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
unsigned char writeOscBuf;
|
||||
unsigned char sidCore;
|
||||
int filtCut, resetTime, initResetTime;
|
||||
short cutoff_slide;
|
||||
|
||||
bool keyPriority, sidIs6581, needInitTables, no1EUpdate, multiplyRel, macroRace;
|
||||
unsigned char chanOrder[3];
|
||||
|
|
@ -114,6 +117,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
bool isVolGlobal();
|
||||
float getPostAmp();
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
|
|
|
|||
|
|
@ -524,12 +524,16 @@ unsigned short DivPlatformDave::getPan(int ch) {
|
|||
return (chan[ch].panL<<8)|chan[ch].panR;
|
||||
}
|
||||
|
||||
// TODO: the rest
|
||||
DivChannelPair DivPlatformDave::getPaired(int ch) {
|
||||
void DivPlatformDave::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (chan[ch].highPass) {
|
||||
DivChannelPair("high",(ch+1)&3);
|
||||
ret.push_back(DivChannelPair(_("high"),(ch+1)&3));
|
||||
}
|
||||
if (chan[ch].ringMod) {
|
||||
ret.push_back(DivChannelPair(_("ring"),(ch+2)&3));
|
||||
}
|
||||
if (chan[ch].lowPass && ch==3) {
|
||||
ret.push_back(DivChannelPair(_("low"),2));
|
||||
}
|
||||
return DivChannelPair();
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformDave::getModeHints(int ch) {
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ class DivPlatformDave: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
|
|
|
|||
|
|
@ -771,6 +771,8 @@ int DivPlatformGBAMinMod::init(DivEngine* p, int channels, int sugRate, const Di
|
|||
setFlags(flags);
|
||||
reset();
|
||||
|
||||
maxCPU=0.0; // initialize!
|
||||
|
||||
return 16;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1465,7 +1465,20 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,6 +175,13 @@ void DivPlatformNES::acquire_NSFPlayE(short** buf, size_t len) {
|
|||
int out2[2];
|
||||
for (size_t i=0; i<len; i++) {
|
||||
doPCM;
|
||||
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
doWrite(w.addr,w.val);
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
e1_NP->Tick(8);
|
||||
e2_NP->TickFrameSequence(8);
|
||||
|
|
@ -436,7 +443,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
// https://www.youtube.com/watch?v=vB4P8x2Am6Y
|
||||
|
||||
if (lsamp->loopEnd>lsamp->loopStart && goingToLoop) {
|
||||
int loopStartAddr=(sampleOffDPCM[dacSample]+lsamp->loopStart)>>3;
|
||||
int loopStartAddr=sampleOffDPCM[dacSample]+(lsamp->loopStart>>3);
|
||||
int loopLen=(lsamp->loopEnd-lsamp->loopStart)>>3;
|
||||
|
||||
rWrite(0x4012,(loopStartAddr>>6)&0xff);
|
||||
|
|
@ -492,14 +499,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
}
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
dacSample=(int)ins->amiga.getSample(c.value);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
dacSample=(int)ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -29,24 +29,41 @@ extern "C" {
|
|||
}
|
||||
#include "sound/ymfm/ymfm_adpcm.h"
|
||||
#include "sound/ymfm/ymfm_opl.h"
|
||||
#include "sound/ymfm/ymfm_pcm.h"
|
||||
#include "sound/ymf278b/ymf278.h"
|
||||
|
||||
class DivOPLAInterface: public ymfm::ymfm_interface {
|
||||
public:
|
||||
unsigned char* adpcmBMem;
|
||||
unsigned char* pcmMem;
|
||||
int sampleBank;
|
||||
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
|
||||
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
|
||||
DivOPLAInterface(): adpcmBMem(NULL), sampleBank(0) {}
|
||||
DivOPLAInterface(): adpcmBMem(NULL), pcmMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivYMF278MemoryInterface: public MemoryInterface {
|
||||
public:
|
||||
unsigned char* memory;
|
||||
DivYMF278MemoryInterface(unsigned size_) : memory(NULL), size(size_) {};
|
||||
byte operator[](unsigned address) const override;
|
||||
unsigned getSize() const override { return size; };
|
||||
void write(unsigned address, byte value) override {};
|
||||
void clear(byte value) override {};
|
||||
private:
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
class DivPlatformOPL: public DivDispatch {
|
||||
protected:
|
||||
struct Channel: public SharedChannel<int> {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
unsigned int freqH, freqL;
|
||||
int sample, fixedFreq;
|
||||
bool furnacePCM, fourOp, hardReset;
|
||||
unsigned char pan;
|
||||
bool furnacePCM, fourOp, hardReset, writeCtrl;
|
||||
bool levelDirect, damp, pseudoReverb, lfoReset, ch;
|
||||
int lfo, vib, am, ar, d1r, d2r, dl, rc, rr;
|
||||
int pan;
|
||||
int macroVolMul;
|
||||
Channel():
|
||||
SharedChannel<int>(0),
|
||||
|
|
@ -57,14 +74,29 @@ class DivPlatformOPL: public DivDispatch {
|
|||
furnacePCM(false),
|
||||
fourOp(false),
|
||||
hardReset(false),
|
||||
writeCtrl(false),
|
||||
levelDirect(true),
|
||||
damp(false),
|
||||
pseudoReverb(false),
|
||||
lfoReset(false),
|
||||
ch(false),
|
||||
lfo(0),
|
||||
vib(0),
|
||||
am(0),
|
||||
ar(15),
|
||||
d1r(15),
|
||||
d2r(0),
|
||||
dl(0),
|
||||
rc(15),
|
||||
rr(15),
|
||||
pan(3),
|
||||
macroVolMul(64) {
|
||||
state.ops=2;
|
||||
}
|
||||
};
|
||||
Channel chan[20];
|
||||
DivDispatchOscBuffer* oscBuf[20];
|
||||
bool isMuted[20];
|
||||
Channel chan[44];
|
||||
DivDispatchOscBuffer* oscBuf[44];
|
||||
bool isMuted[44];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
|
|
@ -72,7 +104,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
QueuedWrite(): addr(0), val(0), addrOrVal(false) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,2048> writes;
|
||||
FixedQueue<QueuedWrite,4096> writes;
|
||||
|
||||
unsigned int dacVal;
|
||||
unsigned int dacVal2;
|
||||
|
|
@ -86,8 +118,12 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
unsigned char* adpcmBMem;
|
||||
size_t adpcmBMemLen;
|
||||
unsigned char* pcmMem;
|
||||
size_t pcmMemLen;
|
||||
DivOPLAInterface iface;
|
||||
DivYMF278MemoryInterface pcmMemory;
|
||||
unsigned int sampleOffB[256];
|
||||
unsigned int sampleOffPCM[256];
|
||||
bool sampleLoaded[256];
|
||||
|
||||
ymfm::adpcm_b_engine* adpcmB;
|
||||
|
|
@ -97,12 +133,13 @@ class DivPlatformOPL: public DivDispatch {
|
|||
const unsigned short* chanMap;
|
||||
const unsigned char* outChanMap;
|
||||
int chipFreqBase, chipRateBase;
|
||||
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank, totalOutputs;
|
||||
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan=-1, pcmChanOffs=-1, sampleBank, totalOutputs, ramSize;
|
||||
int fmMixL=7, fmMixR=7, pcmMixL=7, pcmMixR=7;
|
||||
unsigned char lastBusy;
|
||||
unsigned char drumState;
|
||||
unsigned char drumVol[5];
|
||||
|
||||
unsigned char regPool[512];
|
||||
unsigned char regPool[768];
|
||||
|
||||
bool properDrums, properDrumsSys, dam, dvb;
|
||||
|
||||
|
|
@ -115,15 +152,17 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
bool update4OpMask, pretendYMU, downsample, compatPan;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
short oldWrites[768];
|
||||
short pendingWrites[768];
|
||||
|
||||
// chips
|
||||
opl3_chip fm;
|
||||
YMF278 pcm;
|
||||
ymfm::ym3526* fm_ymfm1;
|
||||
ymfm::ym3812* fm_ymfm2;
|
||||
ymfm::y8950* fm_ymfm8950;
|
||||
ymfm::ymf262* fm_ymfm3;
|
||||
ymfm::ymf278b* fm_ymfm4;
|
||||
fmopl2_t fm_lle2;
|
||||
fmopl3_t fm_lle3;
|
||||
|
||||
|
|
@ -141,6 +180,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
void acquire_nukedLLE3(short** buf, size_t len);
|
||||
void acquire_nuked(short** buf, size_t len);
|
||||
void acquire_ymfm3(short** buf, size_t len);
|
||||
void acquire_ymfm4(short** buf, size_t len);
|
||||
void acquire_ymfm8950(short** buf, size_t len);
|
||||
void acquire_ymfm2(short** buf, size_t len);
|
||||
void acquire_ymfm1(short** buf, size_t len);
|
||||
|
|
@ -151,7 +191,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
int mapVelocity(int ch, float vel);
|
||||
float getGain(int ch, int vol);
|
||||
|
|
@ -182,6 +222,9 @@ class DivPlatformOPL: public DivDispatch {
|
|||
void renderSamples(int chipID);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
DivPlatformOPL():
|
||||
pcmMemory(0x400000),
|
||||
pcm(pcmMemory) {}
|
||||
~DivPlatformOPL();
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
|||
return 0;
|
||||
}
|
||||
return adpcmBMem[address&0x3ffff];
|
||||
case ymfm::ACCESS_PCM:
|
||||
if (pcmMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pcmMem[address&0x3fffff];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -36,3 +41,10 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
|||
|
||||
void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
||||
}
|
||||
|
||||
byte DivYMF278MemoryInterface::operator[](unsigned address) const {
|
||||
if (memory && address<size) {
|
||||
return memory[address];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -538,11 +538,10 @@ unsigned short DivPlatformPCE::getPan(int ch) {
|
|||
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
|
||||
}
|
||||
|
||||
DivChannelPair DivPlatformPCE::getPaired(int ch) {
|
||||
void DivPlatformPCE::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (ch==1 && lfoMode>0) {
|
||||
return DivChannelPair("mod",0);
|
||||
ret.push_back(DivChannelPair(_("mod"),0));
|
||||
}
|
||||
return DivChannelPair();
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformPCE::getModeHints(int ch) {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class DivPlatformPCE: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
|
|
|
|||
|
|
@ -407,22 +407,21 @@ DivMacroInt* DivPlatformPOKEY::getChanMacroInt(int ch) {
|
|||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivChannelPair DivPlatformPOKEY::getPaired(int ch) {
|
||||
void DivPlatformPOKEY::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
switch (ch) {
|
||||
case 0:
|
||||
if (audctl&4) return DivChannelPair("filter",2);
|
||||
if (audctl&4) ret.push_back(DivChannelPair(_("filter"),2));
|
||||
break;
|
||||
case 1:
|
||||
if (audctl&16) return DivChannelPair("16-bit",0);
|
||||
if (audctl&16) ret.push_back(DivChannelPair(_("16-bit"),0));
|
||||
break;
|
||||
case 2:
|
||||
if (audctl&8) return DivChannelPair("16-bit",3);
|
||||
if (audctl&8) ret.push_back(DivChannelPair(_("16-bit"),3));
|
||||
break;
|
||||
case 3:
|
||||
if (audctl&2) return DivChannelPair("filter",1);
|
||||
if (audctl&2) ret.push_back(DivChannelPair(_("filter"),1));
|
||||
break;
|
||||
}
|
||||
return DivChannelPair();
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformPOKEY::getOscBuffer(int ch) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class DivPlatformPOKEY: public DivDispatch {
|
|||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivChannelPair getPaired(int chan);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
|
|
|
|||
|
|
@ -93,7 +93,10 @@ void DivPlatformSID2::acquire(short** buf, size_t len)
|
|||
|
||||
for(int j = 0; j < 3; j++)
|
||||
{
|
||||
oscBuf[j]->data[oscBuf[j]->needle++] = sid2->chan_out[j] / 4;
|
||||
int co=sid2->chan_out[j]>>2;
|
||||
if (co<-32768) co=-32768;
|
||||
if (co>32767) co=32767;
|
||||
oscBuf[j]->data[oscBuf[j]->needle++]=co;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,6 +116,21 @@ void DivPlatformSID2::tick(bool sysTick) {
|
|||
bool willUpdateFilter = false;
|
||||
|
||||
chan[i].std.next();
|
||||
|
||||
if (sysTick) {
|
||||
if (chan[i].pw_slide!=0) {
|
||||
chan[i].duty-=chan[i].pw_slide;
|
||||
chan[i].duty=CLAMP(chan[i].duty,0,0xfff);
|
||||
rWrite(i*7+2,chan[i].duty&0xff);
|
||||
rWrite(i*7+3,(chan[i].duty>>8)|(chan[i].outVol<<4));
|
||||
}
|
||||
if (chan[i].cutoff_slide!=0) {
|
||||
chan[i].filtCut+=chan[i].cutoff_slide;
|
||||
chan[i].filtCut=CLAMP(chan[i].filtCut,0,0xfff);
|
||||
updateFilter(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
rWrite(i*7+3,(chan[i].duty>>8) | (chan[i].outVol << 4));
|
||||
|
|
@ -297,7 +315,7 @@ int DivPlatformSID2::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].test=false;
|
||||
|
||||
if (chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) {
|
||||
if (((chan[c.chan].insChanged || chan[c.chan].resetDuty || ins->std.waveMacro.len>0) && ins->c64.resetDuty) || chan[c.chan].resetDuty) {
|
||||
chan[c.chan].duty=ins->c64.duty;
|
||||
rWrite(c.chan*7+2,chan[c.chan].duty&0xff);
|
||||
rWrite(c.chan*7+3,(chan[c.chan].duty>>8) | (chan[c.chan].outVol << 4));
|
||||
|
|
@ -521,6 +539,12 @@ int DivPlatformSID2::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_C64_PW_SLIDE:
|
||||
chan[c.chan].pw_slide=c.value*c.value2;
|
||||
break;
|
||||
case DIV_CMD_C64_CUTOFF_SLIDE:
|
||||
chan[c.chan].cutoff_slide=c.value*c.value2;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
|
|
@ -574,15 +598,31 @@ DivMacroInt* DivPlatformSID2::getChanMacroInt(int ch) {
|
|||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
void DivPlatformSID2::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (chan[ch].ring) {
|
||||
if (ch==0) {
|
||||
ret.push_back(DivChannelPair(_("ring"),2));
|
||||
} else {
|
||||
ret.push_back(DivChannelPair(_("ring"),(ch-1)%3));
|
||||
}
|
||||
}
|
||||
if (chan[ch].sync) {
|
||||
if (ch==0) {
|
||||
ret.push_back(DivChannelPair(_("sync"),2));
|
||||
} else {
|
||||
ret.push_back(DivChannelPair(_("sync"),(ch-1)%3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformSID2::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
ret.count=1;
|
||||
ret.hint[0]=ICON_FA_BELL_SLASH_O;
|
||||
ret.type[0]=0;
|
||||
if (ch == 2 && (chan[ch].filtControl & 8)) {
|
||||
ret.type[0] = 7;
|
||||
}
|
||||
else if (!chan[ch].gate) {
|
||||
if (ch==2 && (chan[ch].filtControl & 8)) {
|
||||
ret.type[0]=7;
|
||||
} else if (!chan[ch].gate) {
|
||||
ret.type[0]=4;
|
||||
}
|
||||
|
||||
|
|
@ -633,6 +673,9 @@ void DivPlatformSID2::reset() {
|
|||
chan[i].noise_mode = 0;
|
||||
|
||||
rWrite(0x3 + 7 * i,0xf0);
|
||||
|
||||
chan[i].cutoff_slide = 0;
|
||||
chan[i].pw_slide = 0;
|
||||
}
|
||||
|
||||
sid2->reset();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class DivPlatformSID2: public DivDispatch {
|
|||
unsigned char noise_mode;
|
||||
unsigned char mix_mode;
|
||||
int filtCut;
|
||||
bool do_pw_sweep_writes, do_cutoff_sweep_writes;
|
||||
short cutoff_slide;
|
||||
short pw_slide;
|
||||
Channel():
|
||||
SharedChannel<signed char>(15),
|
||||
prevFreq(0x1ffff),
|
||||
|
|
@ -60,8 +61,8 @@ class DivPlatformSID2: public DivDispatch {
|
|||
noise_mode(0),
|
||||
mix_mode(0),
|
||||
filtCut(0),
|
||||
do_pw_sweep_writes(false),
|
||||
do_cutoff_sweep_writes(false) {}
|
||||
cutoff_slide(0),
|
||||
pw_slide(0) {}
|
||||
};
|
||||
Channel chan[3];
|
||||
DivDispatchOscBuffer* oscBuf[3];
|
||||
|
|
@ -108,6 +109,7 @@ class DivPlatformSID2: public DivDispatch {
|
|||
bool isVolGlobal();
|
||||
float getPostAmp();
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
|
|
|
|||
1411
src/engine/platform/sid3.cpp
Normal file
1411
src/engine/platform/sid3.cpp
Normal file
File diff suppressed because it is too large
Load diff
276
src/engine/platform/sid3.h
Normal file
276
src/engine/platform/sid3.h
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 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 _SID3_H
|
||||
#define _SID3_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../fixedQueue.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/sid3.h"
|
||||
|
||||
class DivPlatformSID3: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed short> {
|
||||
int prevFreq;
|
||||
unsigned char wave, special_wave, attack, decay, sustain, sr, release;
|
||||
int duty;
|
||||
bool resetMask, resetFilter, resetDuty, gate, ring, sync, phase, oneBitNoise;
|
||||
bool phaseReset, envReset, phaseResetNoise;
|
||||
bool noiseFreqChanged;
|
||||
unsigned char vol;
|
||||
unsigned char noise_mode;
|
||||
unsigned char mix_mode;
|
||||
unsigned char ringSrc, syncSrc, phaseSrc;
|
||||
unsigned char panLeft, panRight;
|
||||
int noiseFreq;
|
||||
bool independentNoiseFreq;
|
||||
unsigned int noiseLFSRMask;
|
||||
|
||||
struct Filter
|
||||
{
|
||||
int cutoff;
|
||||
unsigned char resonance;
|
||||
unsigned char output_volume;
|
||||
unsigned char distortion_level;
|
||||
unsigned char mode;
|
||||
bool enabled;
|
||||
unsigned char filter_matrix;
|
||||
|
||||
short cutoff_slide;
|
||||
|
||||
bool bindCutoffToNote; //cutoff scaling
|
||||
unsigned char bindCutoffToNoteStrength; //how much cutoff changes over e.g. 1 semitone
|
||||
unsigned char bindCutoffToNoteCenter; //central note of the cutoff change
|
||||
bool bindCutoffToNoteDir; //if we decrease or increase cutoff if e.g. we go upper in note space
|
||||
|
||||
bool bindResonanceToNote;
|
||||
unsigned char bindResonanceToNoteStrength; //how much resonance changes over e.g. 1 semitone
|
||||
unsigned char bindResonanceToNoteCenter; //central note of the resonance change
|
||||
bool bindResonanceToNoteDir; //if we decrease or increase resonance if e.g. we go upper in note space
|
||||
|
||||
Filter():
|
||||
cutoff(0),
|
||||
resonance(0),
|
||||
output_volume(0),
|
||||
distortion_level(0),
|
||||
mode(0),
|
||||
enabled(false),
|
||||
filter_matrix(0),
|
||||
cutoff_slide(0),
|
||||
bindCutoffToNote(false),
|
||||
bindCutoffToNoteStrength(0),
|
||||
bindCutoffToNoteCenter(0),
|
||||
bindCutoffToNoteDir(false),
|
||||
bindResonanceToNote(false),
|
||||
bindResonanceToNoteStrength(0),
|
||||
bindResonanceToNoteCenter(0),
|
||||
bindResonanceToNoteDir(false) {}
|
||||
} filt[SID3_NUM_FILTERS];
|
||||
|
||||
int noise_baseNoteOverride;
|
||||
bool noise_fixedArp;
|
||||
int noise_arpOff;
|
||||
int noise_pitch2;
|
||||
bool noise_hasArp;
|
||||
bool noise_hasPitch;
|
||||
|
||||
bool pcm;
|
||||
int wavetable;
|
||||
|
||||
long long dacPeriod, dacRate, dacOut;
|
||||
unsigned long long dacPos;
|
||||
int dacSample;
|
||||
|
||||
unsigned char phaseInv;
|
||||
unsigned char feedback;
|
||||
|
||||
short pw_slide;
|
||||
|
||||
short phase_reset_counter;
|
||||
short noise_phase_reset_counter;
|
||||
short envelope_reset_counter;
|
||||
|
||||
void handleArpNoise(int offset=0)
|
||||
{
|
||||
DivMacroStruct& m = this->std.op[3].am;
|
||||
|
||||
if (m.had) {
|
||||
noise_hasArp=true;
|
||||
|
||||
if (m.val<0) {
|
||||
if (!(m.val&0x40000000)) {
|
||||
noise_baseNoteOverride=(m.val|0x40000000)+offset;
|
||||
noise_fixedArp=true;
|
||||
} else {
|
||||
noise_arpOff=m.val;
|
||||
noise_fixedArp=false;
|
||||
}
|
||||
} else {
|
||||
if (m.val&0x40000000) {
|
||||
noise_baseNoteOverride=(m.val&(~0x40000000))+offset;
|
||||
noise_fixedArp=true;
|
||||
} else {
|
||||
noise_arpOff=m.val;
|
||||
noise_fixedArp=false;
|
||||
}
|
||||
}
|
||||
noiseFreqChanged=true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
noise_hasArp=false;
|
||||
}
|
||||
}
|
||||
|
||||
void handlePitchNoise()
|
||||
{
|
||||
DivMacroStruct& m = this->std.op[0].ar;
|
||||
|
||||
if (m.had) {
|
||||
noise_hasPitch=true;
|
||||
|
||||
if (m.mode) {
|
||||
noise_pitch2+=m.val;
|
||||
CLAMP_VAR(noise_pitch2,-131071,131071);
|
||||
} else {
|
||||
noise_pitch2=m.val;
|
||||
}
|
||||
noiseFreqChanged=true;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
noise_hasPitch=false;
|
||||
}
|
||||
}
|
||||
|
||||
Channel():
|
||||
SharedChannel<signed short>(SID3_MAX_VOL),
|
||||
prevFreq(0xffffff),
|
||||
wave(0),
|
||||
special_wave(0),
|
||||
attack(0),
|
||||
decay(0),
|
||||
sustain(0),
|
||||
sr(0),
|
||||
release(0),
|
||||
duty(0),
|
||||
resetMask(false),
|
||||
resetFilter(false),
|
||||
resetDuty(false),
|
||||
gate(true),
|
||||
ring(false),
|
||||
sync(false),
|
||||
phase(false),
|
||||
oneBitNoise(false),
|
||||
phaseReset(false),
|
||||
envReset(false),
|
||||
phaseResetNoise(false),
|
||||
noiseFreqChanged(false),
|
||||
vol(SID3_MAX_VOL),
|
||||
noise_mode(0),
|
||||
mix_mode(0),
|
||||
ringSrc(0),
|
||||
syncSrc(0),
|
||||
phaseSrc(0),
|
||||
panLeft(0xff),
|
||||
panRight(0xff),
|
||||
noiseFreq(0),
|
||||
independentNoiseFreq(false),
|
||||
noiseLFSRMask((1 << 29) | (1 << 5) | (1 << 3) | 1), //https://docs.amd.com/v/u/en-US/xapp052 for 30 bits: 30, 6, 4, 1
|
||||
pcm(false),
|
||||
wavetable(-1),
|
||||
dacPeriod(0),
|
||||
dacRate(0),
|
||||
dacOut(0),
|
||||
dacPos(0),
|
||||
dacSample(-1),
|
||||
phaseInv(0),
|
||||
feedback(0),
|
||||
pw_slide(0),
|
||||
phase_reset_counter(-1),
|
||||
noise_phase_reset_counter(-1),
|
||||
envelope_reset_counter(-1) {}
|
||||
};
|
||||
Channel chan[SID3_NUM_CHANNELS];
|
||||
DivDispatchOscBuffer* oscBuf[SID3_NUM_CHANNELS];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(0) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,SID3_NUM_REGISTERS * 4> writes;
|
||||
DivWaveSynth ws;
|
||||
|
||||
unsigned char writeOscBuf;
|
||||
|
||||
SID3* sid3;
|
||||
unsigned char regPool[SID3_NUM_REGISTERS];
|
||||
|
||||
bool isMuted[SID3_NUM_CHANNELS];
|
||||
|
||||
unsigned char sampleTick; //used to update streamed sample and not clash with other reg writes at high rate samples
|
||||
bool updateSample;
|
||||
bool quarterClock;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
void updateFlags(int channel, bool gate);
|
||||
void updateFilter(int channel, int filter);
|
||||
void updateFreq(int channel);
|
||||
void updateNoiseFreq(int channel);
|
||||
void updateNoiseLFSRMask(int channel);
|
||||
void updateDuty(int channel);
|
||||
void updateEnvelope(int channel);
|
||||
void updatePanning(int channel);
|
||||
void updateWave();
|
||||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyWaveChange(int wave);
|
||||
float getPostAmp();
|
||||
bool getDCOffRequired();
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
int getOutputCount();
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
void quit();
|
||||
~DivPlatformSID3();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWrite(c,a,v) {rWrite((a)+(c)*16,v)}
|
||||
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,d)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWriteDelay(c,a,v,d) {rWrite((a)+(c)*16,v,d)}
|
||||
#define sampleTableAddr(c) (sampleTableBase+(c)*4)
|
||||
#define waveTableAddr(c) (sampleTableBase+8*4+(c)*9*16)
|
||||
|
||||
|
|
@ -77,7 +79,7 @@ void DivPlatformSNES::acquire(short** buf, size_t len) {
|
|||
dsp.write(w.addr,w.val);
|
||||
regPool[w.addr&0x7f]=w.val;
|
||||
writes.pop();
|
||||
delay=(w.addr==0x5c)?8:1;
|
||||
delay=w.delay;
|
||||
}
|
||||
}
|
||||
dsp.set_output(out,1);
|
||||
|
|
@ -253,7 +255,18 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (koff!=0) {
|
||||
rWrite(0x5c,koff);
|
||||
// TODO: improve
|
||||
if (antiClick) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (koff&(1<<i)) {
|
||||
chWrite(i,5,0);
|
||||
chWrite(i,7,0x9f);
|
||||
chan[i].shallWriteEnv=true;
|
||||
}
|
||||
}
|
||||
rWriteDelay(0x7e,0,64);
|
||||
}
|
||||
rWriteDelay(0x5c,koff,8);
|
||||
}
|
||||
if (writeControl) {
|
||||
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
|
||||
|
|
@ -314,10 +327,7 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (koff!=0) {
|
||||
rWrite(0x5c,0);
|
||||
}
|
||||
if (kon!=0) {
|
||||
rWrite(0x4c,kon);
|
||||
rWriteDelay(0x5c,0,8);
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].shallWriteVol) {
|
||||
|
|
@ -325,6 +335,9 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
chan[i].shallWriteVol=false;
|
||||
}
|
||||
}
|
||||
if (kon!=0) {
|
||||
rWrite(0x4c,kon);
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSNES::dispatch(DivCommand c) {
|
||||
|
|
@ -710,11 +723,10 @@ unsigned short DivPlatformSNES::getPan(int ch) {
|
|||
return (chan[ch].panL<<8)|chan[ch].panR;
|
||||
}
|
||||
|
||||
DivChannelPair DivPlatformSNES::getPaired(int ch) {
|
||||
void DivPlatformSNES::getPaired(int ch, std::vector<DivChannelPair>& ret) {
|
||||
if (chan[ch].pitchMod) {
|
||||
return DivChannelPair("mod",(ch-1)&7);
|
||||
ret.push_back(DivChannelPair(_("mod"),(ch-1)&7));
|
||||
}
|
||||
return DivChannelPair();
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformSNES::getModeHints(int ch) {
|
||||
|
|
@ -1027,6 +1039,7 @@ void DivPlatformSNES::setFlags(const DivConfig& flags) {
|
|||
initEchoMask=flags.getInt("echoMask",0);
|
||||
|
||||
interpolationOff=flags.getBool("interpolationOff",false);
|
||||
antiClick=flags.getBool("antiClick",true);
|
||||
}
|
||||
|
||||
int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
bool writeDryVol;
|
||||
bool echoOn;
|
||||
bool interpolationOff;
|
||||
bool antiClick;
|
||||
|
||||
bool initEchoOn;
|
||||
signed char initEchoVolL;
|
||||
|
|
@ -82,8 +83,10 @@ class DivPlatformSNES: public DivDispatch {
|
|||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
unsigned char delay;
|
||||
unsigned char padding;
|
||||
QueuedWrite(): addr(0), val(0), delay(0), padding(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v, unsigned char d=0): addr(a), val(v), delay(d), padding(0) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,256> writes;
|
||||
|
||||
|
|
@ -103,7 +106,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
void getPaired(int ch, std::vector<DivChannelPair>& ret);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
|
|
|
|||
3856
src/engine/platform/sound/sid3.c
Normal file
3856
src/engine/platform/sound/sid3.c
Normal file
File diff suppressed because it is too large
Load diff
285
src/engine/platform/sound/sid3.h
Normal file
285
src/engine/platform/sound/sid3.h
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
#ifndef SID3_H
|
||||
#define SID3_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SID3_NUM_CHANNELS 7
|
||||
#define SID3_NUM_FILTERS 4
|
||||
#define SID3_REGISTERS_PER_CHANNEL 64
|
||||
#define SID3_REGISTERS_PER_FILTER 8
|
||||
#define SID3_NUM_REGISTERS (SID3_NUM_CHANNELS * SID3_REGISTERS_PER_CHANNEL)
|
||||
#define SID3_MAX_VOL 255
|
||||
|
||||
#define SID3_WAVETABLE_LENGTH 256
|
||||
|
||||
#define SID3_NUM_WAVEFORM_BITS 5
|
||||
|
||||
#define SID3_NUM_UNIQUE_SPECIAL_WAVES 28
|
||||
#define SID3_NUM_SPECIAL_WAVES (SID3_NUM_UNIQUE_SPECIAL_WAVES * 2 + 2) /* usual and 2x vol clipped + 2x vol clipped tri and saw... */
|
||||
#define SID3_SPECIAL_WAVE_LENGTH 16384
|
||||
|
||||
#define SID3_EXPONENTIAL_LUT_LENGTH ((0xff0000 >> 8) + 1)
|
||||
|
||||
#define SID3_ACC_BITS 30
|
||||
#define SID3_ACC_MASK ((1UL << SID3_ACC_BITS) - 1)
|
||||
|
||||
enum Flags
|
||||
{
|
||||
SID3_CHAN_ENABLE_GATE = 1,
|
||||
SID3_CHAN_ENABLE_RING_MOD = 2,
|
||||
SID3_CHAN_ENABLE_HARD_SYNC = 4,
|
||||
SID3_CHAN_ENABLE_PHASE_MOD = 8,
|
||||
SID3_CHAN_PHASE_RESET = 16,
|
||||
SID3_CHAN_ENV_RESET = 32,
|
||||
SID3_CHAN_NOISE_PHASE_RESET = 64,
|
||||
SID3_CHAN_1_BIT_NOISE = 128,
|
||||
};
|
||||
|
||||
enum PhaseInversion
|
||||
{
|
||||
SID3_INV_SIGNAL_RIGHT = 1,
|
||||
SID3_INV_SIGNAL_LEFT = 2,
|
||||
};
|
||||
|
||||
enum Waveforms
|
||||
{
|
||||
SID3_WAVE_TRIANGLE = 1,
|
||||
SID3_WAVE_SAW = 2,
|
||||
SID3_WAVE_PULSE = 4,
|
||||
SID3_WAVE_NOISE = 8,
|
||||
SID3_WAVE_SPECIAL = 16,
|
||||
};
|
||||
|
||||
enum Mixmodes
|
||||
{
|
||||
SID3_MIX_8580 = 0,
|
||||
SID3_MIX_AND = 1,
|
||||
SID3_MIX_OR = 2,
|
||||
SID3_MIX_XOR = 3,
|
||||
SID3_MIX_SUM = 4,
|
||||
};
|
||||
|
||||
enum Filter_modes
|
||||
{
|
||||
SID3_FILTER_LP = 1,
|
||||
SID3_FILTER_HP = 2,
|
||||
SID3_FILTER_BP = 4,
|
||||
|
||||
SID3_FILTER_MODES_MASK = 7,
|
||||
|
||||
SID3_FILTER_ENABLE = 8,
|
||||
SID3_FILTER_CHANNEL_INPUT = 16, //add channel input to filter input
|
||||
SID3_FILTER_OUTPUT = 32, //output sound from this filter to channel output
|
||||
};
|
||||
|
||||
enum Registers
|
||||
{
|
||||
SID3_REGISTER_FLAGS = 0,
|
||||
|
||||
SID3_REGISTER_ADSR_A = 1,
|
||||
SID3_REGISTER_ADSR_D = 2,
|
||||
SID3_REGISTER_ADSR_S = 3,
|
||||
SID3_REGISTER_ADSR_SR = 4,
|
||||
SID3_REGISTER_ADSR_R = 5,
|
||||
|
||||
SID3_REGISTER_WAVEFORM = 6,
|
||||
|
||||
SID3_REGISTER_PW_HIGH = 7,
|
||||
SID3_REGISTER_PW_LOW = 8,
|
||||
|
||||
SID3_REGISTER_SPECIAL_WAVE = 9,
|
||||
|
||||
SID3_REGISTER_FREQ_HIGH = 10,
|
||||
SID3_REGISTER_FREQ_MID = 11,
|
||||
SID3_REGISTER_FREQ_LOW = 12,
|
||||
|
||||
SID3_REGISTER_ADSR_VOL = 13,
|
||||
|
||||
SID3_REGISTER_MIXMODE = 14,
|
||||
|
||||
SID3_REGISTER_RING_MOD_SRC = 15,
|
||||
SID3_REGISTER_SYNC_SRC = 16,
|
||||
|
||||
SID3_REGISTER_FILT_BASE = 17,
|
||||
SID3_REGISTER_FILT_MODE = 17,
|
||||
SID3_REGISTER_FILT_CUTOFF_HIGH = 18,
|
||||
SID3_REGISTER_FILT_CUTOFF_LOW = 19,
|
||||
SID3_REGISTER_FILT_RESONANCE = 20,
|
||||
SID3_REGISTER_FILT_DISTORTION = 21,
|
||||
SID3_REGISTER_FILT_CONNECTION = 22,
|
||||
SID3_REGISTER_FILT_OUTPUT_VOLUME = 23,
|
||||
|
||||
SID3_REGISTER_AFTER_FILT_1ST_REG = SID3_REGISTER_FILT_BASE + SID3_REGISTERS_PER_FILTER * SID3_NUM_FILTERS,
|
||||
SID3_REGISTER_PHASE_MOD_SRC = SID3_REGISTER_AFTER_FILT_1ST_REG,
|
||||
|
||||
SID3_REGISTER_PAN_LEFT = SID3_REGISTER_AFTER_FILT_1ST_REG + 1,
|
||||
SID3_REGISTER_PAN_RIGHT = SID3_REGISTER_AFTER_FILT_1ST_REG + 2,
|
||||
|
||||
SID3_REGISTER_NOISE_FREQ_HIGH = SID3_REGISTER_AFTER_FILT_1ST_REG + 3,
|
||||
SID3_REGISTER_NOISE_FREQ_MID = SID3_REGISTER_AFTER_FILT_1ST_REG + 4,
|
||||
SID3_REGISTER_NOISE_FREQ_LOW = SID3_REGISTER_AFTER_FILT_1ST_REG + 5,
|
||||
|
||||
SID3_REGISTER_NOISE_LFSR_HIGHEST = SID3_REGISTER_AFTER_FILT_1ST_REG + 6,
|
||||
SID3_REGISTER_NOISE_LFSR_HIGH = SID3_REGISTER_AFTER_FILT_1ST_REG + 7,
|
||||
SID3_REGISTER_NOISE_LFSR_MID = SID3_REGISTER_AFTER_FILT_1ST_REG + 8,
|
||||
SID3_REGISTER_NOISE_LFSR_LOW = SID3_REGISTER_AFTER_FILT_1ST_REG + 9,
|
||||
|
||||
SID3_REGISTER_STREAMED_SAMPLE_HIGH = SID3_REGISTER_AFTER_FILT_1ST_REG + 10,
|
||||
SID3_REGISTER_STREAMED_SAMPLE_LOW = SID3_REGISTER_AFTER_FILT_1ST_REG + 11,
|
||||
|
||||
SID3_REGISTER_PHASE_INVERSION = SID3_REGISTER_AFTER_FILT_1ST_REG + 12,
|
||||
|
||||
SID3_REGISTER_FEEDBACK = SID3_REGISTER_AFTER_FILT_1ST_REG + 13,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int32_t input;
|
||||
int32_t output;
|
||||
|
||||
// State of filter.
|
||||
float Vhp; // highpass
|
||||
float Vbp; // bandpass
|
||||
float Vlp; // lowpass
|
||||
|
||||
// Cutoff frequency, resonance.
|
||||
float w0, w0_ceil_1;
|
||||
float _1024_div_Q;
|
||||
|
||||
double distortion_multiplier; //pre-calculated distortion multiplier for tanh() simple distortion
|
||||
double tanh_distortion_multiplier; //pre-calculated tanh(distortion multiplier)
|
||||
|
||||
uint16_t cutoff;
|
||||
uint8_t resonance;
|
||||
uint8_t distortion_level;
|
||||
|
||||
uint8_t mode;
|
||||
|
||||
uint8_t output_volume;
|
||||
} sid3_filter;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
sid3_filter filt[SID3_NUM_FILTERS];
|
||||
uint8_t connection_matrix[SID3_NUM_FILTERS];
|
||||
} sid3_filters_block;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t a, d, s, sr, r, vol, state;
|
||||
|
||||
uint32_t envelope_counter;
|
||||
uint32_t envelope_speed;
|
||||
bool hold_zero;
|
||||
} sid3_channel_adsr;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t accumulator;
|
||||
uint8_t sync_bit;
|
||||
uint32_t frequency;
|
||||
|
||||
uint32_t noise_accumulator;
|
||||
uint32_t noise_frequency;
|
||||
|
||||
uint32_t lfsr, lfsr_taps;
|
||||
|
||||
uint8_t waveform;
|
||||
uint8_t special_wave;
|
||||
|
||||
uint16_t pw;
|
||||
|
||||
uint8_t mix_mode;
|
||||
|
||||
sid3_channel_adsr adsr;
|
||||
|
||||
uint8_t flags;
|
||||
|
||||
uint8_t ring_mod_src;
|
||||
uint8_t hard_sync_src;
|
||||
uint8_t phase_mod_source;
|
||||
|
||||
sid3_filters_block filt;
|
||||
uint8_t clock_filter;
|
||||
|
||||
uint8_t panning_left, panning_right;
|
||||
|
||||
int32_t output_before_filter;
|
||||
|
||||
uint8_t phase_inv;
|
||||
uint32_t feedback;
|
||||
|
||||
int32_t prev_output, prev_output2;
|
||||
} sid3_channel;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t accumulator;
|
||||
uint8_t sync_bit;
|
||||
uint32_t frequency;
|
||||
|
||||
uint16_t streamed_sample;
|
||||
uint8_t wavetable[SID3_WAVETABLE_LENGTH];
|
||||
|
||||
uint8_t wave_address;
|
||||
|
||||
sid3_channel_adsr adsr;
|
||||
|
||||
uint8_t flags;
|
||||
uint8_t mode;
|
||||
|
||||
uint8_t ring_mod_src;
|
||||
uint8_t hard_sync_src;
|
||||
uint8_t phase_mod_source;
|
||||
|
||||
sid3_filters_block filt;
|
||||
uint8_t clock_filter;
|
||||
|
||||
uint8_t panning_left, panning_right;
|
||||
|
||||
int32_t output_before_filter;
|
||||
|
||||
uint8_t phase_inv;
|
||||
} sid3_wavetable_chan;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t special_waves[SID3_NUM_SPECIAL_WAVES][SID3_SPECIAL_WAVE_LENGTH]; //sine wave and OPL3-ish waves + OPZ and YMF825 waves!
|
||||
|
||||
uint16_t env_counter_to_exponential_output[SID3_EXPONENTIAL_LUT_LENGTH];
|
||||
uint32_t exponential_output_to_envelope_counter[SID3_EXPONENTIAL_LUT_LENGTH];
|
||||
|
||||
sid3_channel chan[SID3_NUM_CHANNELS - 1];
|
||||
sid3_wavetable_chan wave_chan;
|
||||
|
||||
int32_t output_l, output_r;
|
||||
|
||||
int32_t channel_signals_before_ADSR[SID3_NUM_CHANNELS];
|
||||
int32_t channel_output[SID3_NUM_CHANNELS];
|
||||
int32_t wave_channel_signal_before_ADSR;
|
||||
int32_t wave_channel_output;
|
||||
|
||||
//emulation-only helpers
|
||||
bool muted[SID3_NUM_CHANNELS];
|
||||
uint32_t clock_rate; //in Hz. 1 MHz default
|
||||
} SID3;
|
||||
|
||||
SID3* sid3_create();
|
||||
void sid3_reset(SID3* sid3);
|
||||
void sid3_write(SID3* sid3, uint16_t address, uint8_t data);
|
||||
void sid3_clock(SID3* sid3);
|
||||
void sid3_set_is_muted(SID3* sid3, uint8_t ch, bool mute);
|
||||
void sid3_set_clock_rate(SID3* sid3, uint32_t clock);
|
||||
void sid3_free(SID3* sid3);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -146,7 +146,8 @@ void SoundUnit::NextSample(short* l, short* r) {
|
|||
}
|
||||
}
|
||||
}
|
||||
fns[i]=ns[i]*chan[i].vol*((chan[i].flags0&8)?4:2);
|
||||
fns[i]=ns[i]*chan[i].vol;
|
||||
if (!(chan[i].flags0&8)) fns[i]>>=1;
|
||||
if ((chan[i].flags0&0xe0)!=0) {
|
||||
int ff=chan[i].cutoff;
|
||||
nslow[i]=nslow[i]+(((ff)*nsband[i])>>16);
|
||||
|
|
@ -259,12 +260,12 @@ void SoundUnit::NextSample(short* l, short* r) {
|
|||
|
||||
// mix
|
||||
if (dsOut) {
|
||||
tnsL=nsL[dsChannel]<<1;
|
||||
tnsR=nsR[dsChannel]<<1;
|
||||
tnsL=nsL[dsChannel]<<3;
|
||||
tnsR=nsR[dsChannel]<<3;
|
||||
dsChannel=(dsChannel+1)&7;
|
||||
} else {
|
||||
tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7])>>2;
|
||||
tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7])>>2;
|
||||
tnsL=(nsL[0]+nsL[1]+nsL[2]+nsL[3]+nsL[4]+nsL[5]+nsL[6]+nsL[7]);
|
||||
tnsR=(nsR[0]+nsR[1]+nsR[2]+nsR[3]+nsR[4]+nsR[5]+nsR[6]+nsR[7]);
|
||||
|
||||
IL1=minval(32767,maxval(-32767,tnsL))>>8;
|
||||
IL2=minval(32767,maxval(-32767,tnsR))>>8;
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ class SoundUnit {
|
|||
int rcycle[8];
|
||||
unsigned int lfsr[8];
|
||||
signed char ns[8];
|
||||
int fns[8];
|
||||
int nsL[8];
|
||||
int nsR[8];
|
||||
int nslow[8];
|
||||
int nshigh[8];
|
||||
int nsband[8];
|
||||
short fns[8];
|
||||
short nsL[8];
|
||||
short nsR[8];
|
||||
short nslow[8];
|
||||
short nshigh[8];
|
||||
short nsband[8];
|
||||
int tnsL, tnsR;
|
||||
unsigned char ilBufPeriod;
|
||||
unsigned short ilBufPos;
|
||||
|
|
|
|||
282
src/engine/platform/sound/supervision.c
Normal file
282
src/engine/platform/sound/supervision.c
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// THIS IS A MODIFIED VERSION OF POTATOR'S SOUND EMULATION CORE
|
||||
// MODIFIED BY AART1256 IN 2024
|
||||
|
||||
#include "supervision.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define SV_SAMPLE_RATE ((svision->UNSCALED_CLOCK)/64)
|
||||
#define SV_DEC_TICK ((SV_SAMPLE_RATE)/60)
|
||||
|
||||
void supervision_sound_set_clock(struct svision_t *svision, uint32 clock) {
|
||||
svision->UNSCALED_CLOCK = clock;
|
||||
}
|
||||
|
||||
void supervision_memorymap_registers_write(struct svision_t *svision, uint32 Addr, uint8 Value)
|
||||
{
|
||||
switch (Addr & 0x1fff) {
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14: case 0x15: case 0x16: case 0x17:
|
||||
supervision_sound_wave_write(svision, ((Addr & 0x4) >> 2), Addr & 3, Value);
|
||||
break;
|
||||
case 0x18:
|
||||
case 0x19:
|
||||
case 0x1a:
|
||||
case 0x1b:
|
||||
case 0x1c:
|
||||
supervision_sound_dma_write(svision, Addr & 0x07, Value);
|
||||
break;
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2a:
|
||||
supervision_sound_noise_write(svision, Addr & 0x07, Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_set_mute_mask(struct svision_t *svision, uint8 mask) {
|
||||
svision->ch_mask = mask;
|
||||
}
|
||||
|
||||
void supervision_sound_set_flags(struct svision_t *svision, uint8 flags_set)
|
||||
{
|
||||
svision->flags = flags_set;
|
||||
}
|
||||
|
||||
void supervision_sound_reset(struct svision_t *svision)
|
||||
{
|
||||
memset(svision->m_channel, 0, sizeof(svision->m_channel));
|
||||
memset(&svision->m_noise, 0, sizeof(svision->m_noise) );
|
||||
memset(&svision->m_dma, 0, sizeof(svision->m_dma) );
|
||||
|
||||
memset(svision->ch, 0, sizeof(svision->ch) );
|
||||
svision->decrement_tick = 0;
|
||||
svision->ch_mask = 15;
|
||||
}
|
||||
|
||||
void supervision_sound_stream_update(struct svision_t *svision, uint8 *stream, uint32 len)
|
||||
{
|
||||
size_t i, j;
|
||||
SVISION_CHANNEL *channel;
|
||||
uint8 s = 0;
|
||||
uint8 *left = stream + 0;
|
||||
uint8 *right = stream + 1;
|
||||
uint8 *chout = stream + 2;
|
||||
|
||||
for (i = 0; i < len >> 1; i++, left += 2, right += 2) {
|
||||
*left = *right = 0;
|
||||
for (channel = svision->m_channel, j = 0; j < 2; j++, channel++) {
|
||||
chout[j] = 0;
|
||||
if (svision->ch[j].size != 0) {
|
||||
if (svision->ch[j].on || channel->count != 0) {
|
||||
BOOL on = 0;
|
||||
switch (svision->ch[j].waveform) {
|
||||
case 0: // 12.5%
|
||||
on = svision->ch[j].pos < (28 * svision->ch[j].size) >> 5;
|
||||
break;
|
||||
case 1: // 25%
|
||||
on = svision->ch[j].pos < (24 * svision->ch[j].size) >> 5;
|
||||
break;
|
||||
case 2: // 50%
|
||||
on = svision->ch[j].pos < svision->ch[j].size / 2;
|
||||
break;
|
||||
case 3: // 75%
|
||||
on = svision->ch[j].pos < svision->ch[j].size / 4;
|
||||
// MESS/MAME: <= (9 * svision->ch[j].size) >> 5;
|
||||
break;
|
||||
}
|
||||
s = on ? (svision->ch[j].volume)<<2 : 0;
|
||||
s = ((svision->ch_mask>>(3-j))&1)?s:0;
|
||||
if (svision->flags&1) {
|
||||
if (j == 0)
|
||||
*right += s;
|
||||
else
|
||||
*left += s;
|
||||
} else {
|
||||
*left += s;
|
||||
*right += s;
|
||||
}
|
||||
chout[j] = s;
|
||||
}
|
||||
svision->ch[j].pos++;
|
||||
if (svision->ch[j].pos >= svision->ch[j].size) {
|
||||
svision->ch[j].pos = 0;
|
||||
// Transition from off to on
|
||||
if (channel->on) {
|
||||
memcpy(&svision->ch[j], channel, sizeof(svision->ch[j]));
|
||||
channel->on = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (svision->m_noise.on && (svision->m_noise.play || svision->m_noise.count != 0)) {
|
||||
s = (svision->m_noise.value * svision->m_noise.volume) << 2;
|
||||
s = svision->ch_mask&1?s:0;
|
||||
chout[3] = 0;
|
||||
if (svision->m_noise.left) {
|
||||
*left += s;
|
||||
chout[3] = s;
|
||||
}
|
||||
if (svision->m_noise.right) {
|
||||
*right += s;
|
||||
chout[3] = s;
|
||||
}
|
||||
svision->m_noise.pos += svision->m_noise.step;
|
||||
while (svision->m_noise.pos >= 1.0) { // if/while difference - Pacific Battle
|
||||
// LFSR: x^2 + x + 1
|
||||
uint16 feedback;
|
||||
svision->m_noise.value = svision->m_noise.state & 1;
|
||||
feedback = ((svision->m_noise.state >> 1) ^ svision->m_noise.state) & 0x0001;
|
||||
feedback <<= svision->m_noise.type;
|
||||
svision->m_noise.state = (svision->m_noise.state >> 1) | feedback;
|
||||
svision->m_noise.pos -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
chout[2] = 0;
|
||||
if (svision->m_dma.on) {
|
||||
uint8 sample;
|
||||
uint16 addr = svision->m_dma.start + (uint16)svision->m_dma.pos / 2;
|
||||
if (addr >= 0x8000 && addr < 0xc000) {
|
||||
sample = svision->supervision_dma_mem[(addr & 0x3fff) | svision->m_dma.ca14to16];
|
||||
}
|
||||
if (((uint16)svision->m_dma.pos) & 1)
|
||||
s = (sample & 0xf);
|
||||
else
|
||||
s = (sample & 0xf0) >> 4;
|
||||
s <<= 2;
|
||||
s = ((svision->ch_mask>>1)&1)?s:0;
|
||||
chout[2] = 0;
|
||||
if (svision->m_dma.left) {
|
||||
*left += s;
|
||||
chout[2] = s;
|
||||
}
|
||||
if (svision->m_dma.right) {
|
||||
*right += s;
|
||||
chout[2] = s;
|
||||
}
|
||||
svision->m_dma.pos += svision->m_dma.step;
|
||||
if (svision->m_dma.pos >= svision->m_dma.size) {
|
||||
svision->m_dma.on = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (svision->decrement_tick > SV_DEC_TICK) {
|
||||
svision->decrement_tick = 0;
|
||||
supervision_sound_decrement(svision);
|
||||
}
|
||||
svision->decrement_tick++;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_sound_decrement(struct svision_t *svision)
|
||||
{
|
||||
if (svision->m_channel[0].count > 0)
|
||||
svision->m_channel[0].count--;
|
||||
if (svision->m_channel[1].count > 0)
|
||||
svision->m_channel[1].count--;
|
||||
if (svision->m_noise.count > 0)
|
||||
svision->m_noise.count--;
|
||||
}
|
||||
|
||||
void supervision_sound_wave_write(struct svision_t *svision, int which, int offset, uint8 data)
|
||||
{
|
||||
SVISION_CHANNEL *channel = &svision->m_channel[which];
|
||||
|
||||
channel->reg[offset] = data;
|
||||
switch (offset) {
|
||||
case 0:
|
||||
case 1: {
|
||||
uint16 size;
|
||||
size = channel->reg[0] | ((channel->reg[1] & 7) << 8);
|
||||
// if size == 0 then channel->size == 0
|
||||
if (size)
|
||||
channel->size = (uint16)(((real)SV_SAMPLE_RATE) * ((real)((size + 1) << 5)) / ((real)svision->UNSCALED_CLOCK));
|
||||
else
|
||||
channel->size = 0;
|
||||
channel->pos = 0;
|
||||
// Popo Team
|
||||
if (channel->count != 0 || svision->ch[which].size == 0 || channel->size == 0) {
|
||||
svision->ch[which].size = channel->size;
|
||||
if (channel->count == 0)
|
||||
svision->ch[which].pos = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
channel->on = data & 0x40;
|
||||
channel->waveform = (data & 0x30) >> 4;
|
||||
channel->volume = data & 0x0f;
|
||||
if (!channel->on || svision->ch[which].size == 0 || channel->size == 0) {
|
||||
uint16 pos = svision->ch[which].pos;
|
||||
memcpy(&svision->ch[which], channel, sizeof(svision->ch[which]));
|
||||
if (channel->count != 0) // Journey to the West
|
||||
svision->ch[which].pos = pos;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
channel->count = data + 1;
|
||||
svision->ch[which].size = channel->size; // Sonny Xpress!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_sound_dma_write(struct svision_t *svision, int offset, uint8 data)
|
||||
{
|
||||
svision->m_dma.reg[offset] = data;
|
||||
switch (offset) {
|
||||
case 0:
|
||||
case 1:
|
||||
svision->m_dma.start = (svision->m_dma.reg[0] | (svision->m_dma.reg[1] << 8));
|
||||
break;
|
||||
case 2:
|
||||
svision->m_dma.size = (data ? data : 0x100) * 32; // Number of 4-bit samples
|
||||
break;
|
||||
case 3:
|
||||
// Test games: Classic Casino, SSSnake
|
||||
svision->m_dma.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * (256 << (data & 3)));
|
||||
// MESS/MAME. Wrong
|
||||
//svision->m_dma.step = svision->UNSCALED_CLOCK / (256.0 * SV_SAMPLE_RATE * (1 + (data & 3)));
|
||||
svision->m_dma.right = data & 4;
|
||||
svision->m_dma.left = data & 8;
|
||||
svision->m_dma.ca14to16 = ((data & 0x70) >> 4) << 14;
|
||||
break;
|
||||
case 4:
|
||||
svision->m_dma.on = data & 0x80;
|
||||
if (svision->m_dma.on) {
|
||||
svision->m_dma.pos = 0.0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data)
|
||||
{
|
||||
svision->m_noise.reg[offset] = data;
|
||||
switch (offset) {
|
||||
case 0: {
|
||||
uint32 divisor = 8 << (data >> 4);
|
||||
if (divisor)
|
||||
svision->m_noise.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * divisor);
|
||||
else
|
||||
svision->m_noise.step = 0;
|
||||
|
||||
svision->m_noise.step = ((real)svision->UNSCALED_CLOCK) / ((real)SV_SAMPLE_RATE * divisor);
|
||||
svision->m_noise.volume = data & 0xf;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
svision->m_noise.count = data + 1;
|
||||
break;
|
||||
case 2:
|
||||
svision->m_noise.type = (data & 1) ? 14 : 6;
|
||||
svision->m_noise.play = data & 2;
|
||||
svision->m_noise.right = data & 4;
|
||||
svision->m_noise.left = data & 8;
|
||||
svision->m_noise.on = data & 0x10; /* honey bee start */
|
||||
svision->m_noise.state = 1;
|
||||
break;
|
||||
}
|
||||
svision->m_noise.pos = 0.0;
|
||||
}
|
||||
76
src/engine/platform/sound/supervision.h
Normal file
76
src/engine/platform/sound/supervision.h
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef __SUPERVISION_SOUND_H__
|
||||
#define __SUPERVISION_SOUND_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef uint16_t uint16;
|
||||
typedef uint32_t uint32;
|
||||
typedef float real;
|
||||
typedef uint8_t BOOL;
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8 reg[4];
|
||||
int on;
|
||||
uint8 waveform, volume;
|
||||
uint16 pos, size;
|
||||
uint16 count;
|
||||
} SVISION_CHANNEL;
|
||||
|
||||
typedef struct {
|
||||
uint8 reg[3];
|
||||
int on, right, left, play;
|
||||
uint8 type; // 6 - 7-Bit, 14 - 15-Bit
|
||||
uint16 state;
|
||||
uint8 value, volume;
|
||||
uint16 count;
|
||||
real pos, step;
|
||||
} SVISION_NOISE;
|
||||
|
||||
typedef struct {
|
||||
uint8 reg[5];
|
||||
int on, right, left;
|
||||
uint32 ca14to16;
|
||||
uint16 start;
|
||||
uint16 size;
|
||||
real pos, step;
|
||||
} SVISION_DMA;
|
||||
|
||||
struct svision_t {
|
||||
SVISION_CHANNEL m_channel[2];
|
||||
// For clear sound (no grating), sync with m_channel
|
||||
SVISION_CHANNEL ch[2];
|
||||
SVISION_NOISE m_noise;
|
||||
SVISION_DMA m_dma;
|
||||
uint8 supervision_dma_mem[65536];
|
||||
uint32 decrement_tick;
|
||||
uint32 UNSCALED_CLOCK;
|
||||
uint8 ch_mask, flags;
|
||||
};
|
||||
|
||||
void supervision_sound_reset(struct svision_t *svision);
|
||||
void supervision_sound_set_clock(struct svision_t *svision, uint32 clock);
|
||||
void supervision_sound_stream_update(struct svision_t *svision, uint8 *stream, uint32 len);
|
||||
void supervision_sound_decrement(struct svision_t *svision);
|
||||
void supervision_sound_wave_write(struct svision_t *svision, int which, int offset, uint8 data);
|
||||
void supervision_sound_dma_write(struct svision_t *svision,int offset, uint8 data);
|
||||
void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data);
|
||||
void supervision_sound_noise_write(struct svision_t *svision, int offset, uint8 data);
|
||||
void supervision_memorymap_registers_write(struct svision_t *svision, uint32 Addr, uint8 Value);
|
||||
// 12SN
|
||||
void supervision_set_mute_mask(struct svision_t *svision, uint8 mask);
|
||||
void supervision_sound_set_flags(struct svision_t *svision, uint8 flags_set);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
300
src/engine/platform/sound/upd1771c.c
Normal file
300
src/engine/platform/sound/upd1771c.c
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
// SOME CODE IS TAKEN FROM MAME'S EMULATION OF THE UPD1771C
|
||||
// MADE BY AART1256 IN 2024
|
||||
|
||||
#include "upd1771c.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
Each of the 8 waveforms have been extracted from the uPD1771c-017 internal
|
||||
ROM, from offset 0x1fd (start of first waveform) to offset 0x2fc (end of
|
||||
last waveform).
|
||||
(note: given test mode dumping offset non-clarity it may be 0x200-0x2ff)
|
||||
The waveforms are stored in an 8-bit sign-magnitude format, so if in ROM the
|
||||
upper bit is 0x80, invert the lower 7 bits to get the 2's complement result
|
||||
seen here.
|
||||
Note that only the last 4 waveforms appear to have been intended for use as
|
||||
waveforms; the first four look as if they're playing back a piece of code as
|
||||
wave data.
|
||||
*/
|
||||
const signed char WAVEFORMS[8][32]={
|
||||
{ 0, 0,-123,-123, -61, -23, 125, 107, 94, 83,-128,-128,-128, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,-128,-128,-128, 0, 0, 0, 0, 0, 0},
|
||||
{ 37, 16, 32, -21, 32, 52, 4, 4, 33, 18, 60, 56, 0, 8, 5, 16, 65, 19, 69, 16, -2, 19, 37, 16, 97, 19, 0, 87, 127, -3, 1, 2},
|
||||
{ 0, 8, 1, 52, 4, 0, 0, 77, 81,-109, 47, 97, -83,-109, 38, 97, 0, 52, 4, 0, 1, 4, 1, 22, 2, -46, 33, 97, 0, 8, -85, -99},
|
||||
{ 47, 97, 40, 97, -3, 25, 64, 17, 0, 52, 12, 5, 12, 5, 12, 5, 12, 5, 12, 5, 8, 4,-114, 19, 0, 52,-122, 21, 2, 5, 0, 8},
|
||||
{ -52, -96,-118,-128,-111, -74, -37, -5, 31, 62, 89, 112, 127, 125, 115, 93, 57, 23, 0, -16, -8, 15, 37, 54, 65, 70, 62, 54, 43, 31, 19, 0},
|
||||
{ -81,-128, -61, 13, 65, 93, 127, 47, 41, 44, 52, 55, 56, 58, 58, 34, 0, 68, 76, 72, 61, 108, 55, 29, 32, 39, 43, 49, 50, 51, 51, 0},
|
||||
{ -21, -45, -67, -88,-105,-114,-122,-128,-123,-116,-103, -87, -70, -53, -28, -9, 22, 46, 67, 86, 102, 114, 123, 125, 127, 117, 104, 91, 72, 51, 28, 0},
|
||||
{ -78,-118,-128,-102, -54, -3, 40, 65, 84, 88, 84, 80, 82, 88, 94, 103, 110, 119, 122, 125, 122, 122, 121, 123, 125, 126, 127, 127, 125, 118, 82, 0}
|
||||
};
|
||||
|
||||
|
||||
#define NOISE_SIZE 255
|
||||
|
||||
|
||||
/*
|
||||
const unsigned char noise_tbl[]=
|
||||
{
|
||||
0x1c,0x86,0x8a,0x8f,0x98,0xa1,0xad,0xbe,0xd9,0x8a,0x66,0x4d,0x40,0x33,0x2b,0x23,
|
||||
0x1e,0x8a,0x90,0x97,0xa4,0xae,0xb8,0xd6,0xec,0xe9,0x69,0x4a,0x3e,0x34,0x2d,0x27,
|
||||
0x24,0x24,0x89,0x8e,0x93,0x9c,0xa5,0xb0,0xc1,0xdd,0x40,0x36,0x30,0x29,0x27,0x24,
|
||||
0x8b,0x90,0x96,0x9e,0xa7,0xb3,0xc4,0xe1,0x25,0x21,0x8a,0x8f,0x93,0x9d,0xa5,0xb2,
|
||||
0xc2,0xdd,0xdd,0x98,0xa2,0xaf,0xbf,0xd8,0xfd,0x65,0x4a,0x3c,0x31,0x2b,0x24,0x22,
|
||||
0x1e,0x87,0x8c,0x91,0x9a,0xa3,0xaf,0xc0,0xdb,0xbe,0xd9,0x8c,0x66,0x4d,0x40,0x34,
|
||||
0x2c,0x24,0x1f,0x88,0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4d,0x3d,0x34,0x2d,
|
||||
0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0xc1,0xda,0xff,0x67,0x4d,
|
||||
0x3d,0x33,0x2d,0x26,0x24,0x20,0x89,0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xdd,0xa3,0xb0,
|
||||
0xc0,0xd9,0xfe,0x66,0x4b,0x3c,0x32,0x2b,0x24,0x23,0x1e,0x88,0x8d,0x92,0x9b,0xa4,
|
||||
0xb0,0xc1,0xdc,0xad,0xbe,0xda,0x22,0x20,0x1c,0x85,0x8a,0x8f,0x98,0xa1,0xad,0xbe,
|
||||
0xda,0x20,0x1b,0x85,0x8d,0x97,0xa1,0xaf,0xbf,0xd8,0xfd,0x64,0x49,0x3a,0x30,0x2a,
|
||||
0x23,0x21,0x1d,0x86,0x8b,0x91,0x9a,0xa2,0xae,0xc0,0xdb,0x33,0x2b,0x24,0x1f,0x88,
|
||||
0x90,0x9a,0xa4,0xb2,0xc2,0xda,0xff,0x67,0x4c,0x3e,0x33,0x2d,0x25,0x24,0x1f,0x89,
|
||||
0x8e,0x93,0x9c,0xa5,0xb1,0xc2,0xde,0x85,0x8e,0x98,0xa2,0xb0,0xc0,0xd9,0xfe,0x64,
|
||||
0x4b,0x3b,0x31,0x2a,0x23,0x22,0x1e,0x88,0x8c,0x91,0x9b,0xa3,0xaf,0xc1,0xdc,0xdc
|
||||
};
|
||||
*/
|
||||
|
||||
const unsigned char noise_tbl[8][256] = {
|
||||
{
|
||||
0x84, 0x87, 0x8c, 0x93, 0x9a, 0xa4, 0xb2, 0xc8, 0x8a, 0x6c, 0x58, 0x4e, 0x43, 0x3d, 0x37, 0x33,
|
||||
0x86, 0x8d, 0x95, 0x9d, 0xa8, 0xb5, 0xc9, 0xe7, 0x6d, 0x58, 0x4b, 0x43, 0x3e, 0x38, 0x37, 0x34,
|
||||
0x87, 0x8b, 0x8f, 0x96, 0x9d, 0xa7, 0xb5, 0xcb, 0x4d, 0x45, 0x40, 0x3a, 0x39, 0x35, 0x89, 0x8d,
|
||||
0x91, 0x98, 0x9f, 0xa9, 0xb7, 0xcd, 0x37, 0x34, 0x87, 0x8c, 0x90, 0x97, 0x9e, 0xa8, 0xb5, 0xcb,
|
||||
0xca, 0x93, 0x9b, 0xa6, 0xb3, 0xc7, 0xe5, 0x6b, 0x56, 0x49, 0x41, 0x3c, 0x36, 0x35, 0x32, 0x86,
|
||||
0x89, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, 0xc9, 0xb2, 0xc8, 0x8a, 0x6c, 0x58, 0x4d, 0x43, 0x3d, 0x37,
|
||||
0x33, 0x86, 0x8d, 0x95, 0x9d, 0xa8, 0xb5, 0xc9, 0xe7, 0x6d, 0x58, 0x4b, 0x43, 0x3e, 0x38, 0x37,
|
||||
0x34, 0x87, 0x8b, 0x8f, 0x97, 0x9e, 0xa8, 0xb5, 0xcb, 0xb5, 0xc9, 0xe6, 0x6d, 0x58, 0x4b, 0x43,
|
||||
0x3d, 0x38, 0x37, 0x33, 0x87, 0x8b, 0x8f, 0x96, 0x9e, 0xa7, 0xb5, 0xcb, 0x9c, 0xa7, 0xb3, 0xc8,
|
||||
0xe6, 0x6c, 0x56, 0x4a, 0x42, 0x3d, 0x37, 0x36, 0x33, 0x86, 0x8a, 0x8e, 0x95, 0x9c, 0xa6, 0xb4,
|
||||
0xca, 0xa4, 0xb2, 0xc8, 0x35, 0x33, 0x30, 0x84, 0x88, 0x8c, 0x93, 0x9a, 0xa5, 0xb2, 0xc8, 0x34,
|
||||
0x30, 0x84, 0x8a, 0x93, 0x9a, 0xa5, 0xb2, 0xc6, 0xe4, 0x6a, 0x55, 0x49, 0x41, 0x3b, 0x36, 0x34,
|
||||
0x31, 0x85, 0x89, 0x8d, 0x94, 0x9b, 0xa5, 0xb3, 0xc9, 0x43, 0x3d, 0x37, 0x33, 0x86, 0x8d, 0x95,
|
||||
0x9d, 0xa8, 0xb4, 0xc9, 0xe7, 0x6d, 0x57, 0x4b, 0x43, 0x3e, 0x38, 0x37, 0x33, 0x87, 0x8b, 0x8f,
|
||||
0x96, 0x9d, 0xa7, 0xb5, 0xcb, 0x85, 0x8b, 0x93, 0x9b, 0xa6, 0xb3, 0xc7, 0xe5, 0x6b, 0x56, 0x4a,
|
||||
0x41, 0x3c, 0x36, 0x35, 0x32, 0x86, 0x89, 0x8e, 0x95, 0x9c, 0xa6, 0xb4, 0xca, 0x30
|
||||
},
|
||||
|
||||
{
|
||||
0x95, 0x95, 0x98, 0x9c, 0x9e, 0xa3, 0xaa, 0xb4, 0xb3, 0x98, 0x9c, 0xa1, 0xa7, 0xb1, 0xc0, 0x57,
|
||||
0x4f, 0x49, 0x48, 0x44, 0x42, 0x40, 0x40, 0x94, 0x95, 0x96, 0x9a, 0x9d, 0xa2, 0xa9, 0xb3, 0xa8,
|
||||
0xb1, 0x66, 0x59, 0x50, 0x4c, 0x4a, 0x46, 0x44, 0x41, 0x94, 0x97, 0x9b, 0x9e, 0xa4, 0xaa, 0xb3,
|
||||
0xc2, 0x59, 0x52, 0x4c, 0x4a, 0x46, 0x45, 0x43, 0x43, 0x96, 0x97, 0x99, 0x9d, 0x9f, 0xa5, 0xab,
|
||||
0xb5, 0xaa, 0xb4, 0xc2, 0x5a, 0x52, 0x4c, 0x4b, 0x47, 0x45, 0x43, 0x43, 0x96, 0x97, 0x99, 0x9d,
|
||||
0xa0, 0xa5, 0xab, 0xb5, 0x9d, 0xa3, 0xa9, 0xb3, 0xc2, 0x59, 0x51, 0x4b, 0x4a, 0x46, 0x44, 0x42,
|
||||
0x42, 0x96, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0xb4, 0xa2, 0xa9, 0xb3, 0x41, 0x40, 0x40, 0x93,
|
||||
0x94, 0x96, 0x9a, 0x9d, 0xa2, 0xa8, 0xb2, 0x41, 0x3e, 0x92, 0x94, 0x99, 0x9b, 0xa2, 0xa7, 0xb1,
|
||||
0xc0, 0x57, 0x4f, 0x4a, 0x48, 0x44, 0x42, 0x40, 0x40, 0x94, 0x95, 0x97, 0x9a, 0x9d, 0xa2, 0xa9,
|
||||
0xb3, 0x48, 0x45, 0x43, 0x41, 0x94, 0x97, 0x9b, 0x9d, 0xa3, 0xa9, 0xb3, 0xc1, 0x59, 0x51, 0x4b,
|
||||
0x4a, 0x45, 0x44, 0x42, 0x42, 0x95, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0xb4, 0x92, 0x95, 0x99,
|
||||
0x9c, 0xa2, 0xa8, 0xb1, 0xc0, 0x57, 0x4f, 0x4a, 0x48, 0x44, 0x42, 0x41, 0x41, 0x94, 0x95, 0x97,
|
||||
0x9b, 0x9e, 0xa3, 0xa9, 0xb3, 0x3e, 0x92, 0x93, 0x95, 0x98, 0x9b, 0xa0, 0xa7, 0xb1, 0x65, 0x58,
|
||||
0x50, 0x4b, 0x49, 0x45, 0x43, 0x41, 0x94, 0x97, 0x9b, 0x9e, 0xa4, 0xa9, 0xb3, 0xc2, 0x59, 0x51,
|
||||
0x4b, 0x4a, 0x46, 0x45, 0x42, 0x42, 0x96, 0x96, 0x99, 0x9c, 0x9f, 0xa4, 0xab, 0xb5, 0x4d, 0x4b,
|
||||
0x47, 0x45, 0x43, 0x43, 0x97, 0x97, 0x9a, 0x9d, 0xa0, 0xa5, 0xac, 0xb6, 0x41, 0x42
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0x93, 0x93, 0x93, 0x94, 0x95, 0x99, 0x9c, 0xa1, 0xa8, 0x4e, 0x48, 0x45, 0x43, 0x44, 0x42, 0x42,
|
||||
0x95, 0x95, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x43, 0x41, 0x41, 0x95, 0x94, 0x94, 0x95,
|
||||
0x97, 0x9b, 0x9d, 0xa3, 0xa9, 0x4f, 0x4a, 0x46, 0x45, 0x45, 0x43, 0x43, 0x97, 0x96, 0x96, 0x97,
|
||||
0x99, 0x9a, 0x9c, 0x9f, 0xa7, 0x94, 0x93, 0x94, 0x96, 0x9a, 0x9d, 0xa2, 0xa9, 0x4f, 0x49, 0x45,
|
||||
0x44, 0x45, 0x43, 0x43, 0x96, 0x96, 0x96, 0x96, 0x98, 0x99, 0x9c, 0x9f, 0xa7, 0x93, 0x93, 0x93,
|
||||
0x93, 0x95, 0x96, 0x99, 0x9c, 0xa4, 0x50, 0x4b, 0x49, 0x45, 0x44, 0x42, 0x42, 0x95, 0x95, 0x95,
|
||||
0x96, 0x97, 0x9b, 0x9e, 0xa3, 0xaa, 0x50, 0x4b, 0x46, 0x46, 0x46, 0x44, 0x44, 0x97, 0x97, 0x97,
|
||||
0x97, 0x99, 0x9a, 0x9d, 0x9f, 0xa8, 0x47, 0x46, 0x46, 0x44, 0x44, 0x98, 0x98, 0x97, 0x98, 0x9a,
|
||||
0x9b, 0x9d, 0xa0, 0xa8, 0x42, 0x96, 0x95, 0x95, 0x96, 0x98, 0x98, 0x9b, 0x9e, 0xa6, 0xa6, 0x93,
|
||||
0x95, 0x99, 0x9c, 0xa1, 0xa7, 0x4e, 0x48, 0x44, 0x43, 0x44, 0x41, 0x42, 0x95, 0x95, 0x95, 0x95,
|
||||
0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x9d, 0xa5, 0x52, 0x4c, 0x4b, 0x46, 0x45, 0x42, 0x43, 0x97, 0x96,
|
||||
0x96, 0x97, 0x98, 0x9c, 0x9f, 0xa4, 0xab, 0x51, 0x4b, 0x48, 0x46, 0x47, 0x44, 0x45, 0x98, 0x98,
|
||||
0x98, 0x98, 0x9a, 0x9b, 0x9e, 0xa0, 0xa8, 0x9f, 0xa4, 0xab, 0x51, 0x4c, 0x48, 0x47, 0x47, 0x45,
|
||||
0x45, 0x99, 0x98, 0x98, 0x99, 0x9a, 0x9b, 0x9e, 0xa1, 0xa9, 0x98, 0x9d, 0x9f, 0xa4, 0xab, 0x51,
|
||||
0x4b, 0x47, 0x47, 0x47, 0x45, 0x45, 0x98, 0x98, 0x98, 0x98, 0x9a, 0x9b, 0x9e, 0xa1, 0xa8, 0x9c,
|
||||
0x9f, 0xa7, 0x42, 0x42, 0x95, 0x95, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9e, 0xa6, 0x3f
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0x94, 0x93, 0x92, 0x92, 0x93, 0x95, 0x96, 0x99, 0x9c, 0x43, 0x43, 0x43, 0x41, 0x41, 0x42, 0x95,
|
||||
0x95, 0x95, 0x94, 0x94, 0x94, 0x95, 0x97, 0x97, 0x9a, 0x40, 0x40, 0x41, 0x95, 0x95, 0x94, 0x95,
|
||||
0x95, 0x97, 0x97, 0x9a, 0x9e, 0x45, 0x44, 0x44, 0x42, 0x42, 0x43, 0x97, 0x96, 0x96, 0x96, 0x96,
|
||||
0x95, 0x96, 0x98, 0x99, 0x9c, 0x94, 0x94, 0x94, 0x94, 0x96, 0x97, 0x9a, 0x9d, 0x44, 0x43, 0x44,
|
||||
0x42, 0x42, 0x43, 0x96, 0x96, 0x96, 0x95, 0x95, 0x95, 0x96, 0x98, 0x98, 0x9b, 0x93, 0x93, 0x93,
|
||||
0x92, 0x92, 0x93, 0x95, 0x95, 0x98, 0x49, 0x44, 0x43, 0x44, 0x42, 0x42, 0x43, 0x96, 0x96, 0x96,
|
||||
0x95, 0x96, 0x98, 0x99, 0x9b, 0x9f, 0x46, 0x45, 0x45, 0x43, 0x44, 0x44, 0x98, 0x98, 0x97, 0x97,
|
||||
0x97, 0x97, 0x97, 0x99, 0x9a, 0x9d, 0x45, 0x43, 0x43, 0x44, 0x98, 0x98, 0x98, 0x97, 0x97, 0x97,
|
||||
0x97, 0x99, 0x9a, 0x9d, 0x95, 0x95, 0x94, 0x94, 0x94, 0x94, 0x94, 0x96, 0x97, 0x9a, 0x9a, 0x92,
|
||||
0x93, 0x94, 0x95, 0x98, 0x9c, 0x43, 0x42, 0x43, 0x40, 0x40, 0x41, 0x95, 0x95, 0x94, 0x94, 0x94,
|
||||
0x93, 0x94, 0x96, 0x97, 0x9a, 0x96, 0x99, 0x4a, 0x45, 0x44, 0x44, 0x42, 0x42, 0x43, 0x97, 0x96,
|
||||
0x96, 0x96, 0x97, 0x98, 0x99, 0x9c, 0xa0, 0x47, 0x46, 0x46, 0x44, 0x44, 0x45, 0x98, 0x98, 0x98,
|
||||
0x98, 0x97, 0x97, 0x98, 0x99, 0x9a, 0x9d, 0x9a, 0x9c, 0xa0, 0x47, 0x46, 0x46, 0x44, 0x44, 0x45,
|
||||
0x99, 0x98, 0x98, 0x98, 0x97, 0x97, 0x98, 0x9a, 0x9a, 0x9e, 0x97, 0x98, 0x99, 0x9c, 0xa0, 0x47,
|
||||
0x46, 0x46, 0x44, 0x44, 0x45, 0x98, 0x98, 0x98, 0x98, 0x98, 0x97, 0x98, 0x9a, 0x9a, 0x9d, 0x98,
|
||||
0x99, 0x9c, 0x42, 0x95, 0x95, 0x95, 0x94, 0x94, 0x94, 0x95, 0x96, 0x97, 0x9a, 0x40
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xe6, 0x1c, 0x87, 0xcb, 0x42, 0x9b, 0x87, 0x35, 0xd8, 0x1c, 0xa3, 0x58, 0x84, 0xa4, 0x28, 0x71,
|
||||
0xd9, 0x3e, 0x82, 0xbb, 0x28, 0xd2, 0x80, 0x63, 0xe0, 0x38, 0x71, 0xcb, 0x22, 0x81, 0x9b, 0x83,
|
||||
0xa4, 0x28, 0x71, 0xd8, 0x3e, 0x81, 0xbb, 0x28, 0xd2, 0x80, 0x63, 0xe0, 0x37, 0x71, 0xcb, 0x22,
|
||||
0x81, 0x9b, 0xa3, 0x57, 0x83, 0xa3, 0x28, 0x70, 0xd8, 0x3e, 0x81, 0xba, 0x28, 0xd1, 0x80, 0x63,
|
||||
0xe0, 0x37, 0x70, 0xcb, 0x22, 0x81, 0x9b, 0x23, 0x82, 0x9b, 0x28, 0xd2, 0x80, 0x63, 0xe1, 0x38,
|
||||
0x71, 0xcc, 0x23, 0x81, 0x9b, 0x9c, 0x88, 0x36, 0xd9, 0x1d, 0xa4, 0x58, 0x84, 0xa5, 0x28, 0x72,
|
||||
0xd9, 0x3f, 0x82, 0xbb, 0x29, 0xd2, 0x81, 0x64, 0xe1, 0x38, 0x72, 0xcc, 0x23, 0x82, 0x9b, 0xcc,
|
||||
0x43, 0x9c, 0x88, 0x36, 0xd8, 0x1d, 0xa4, 0x58, 0x84, 0xa5, 0x29, 0x71, 0xd9, 0x3f, 0x82, 0xbc,
|
||||
0x29, 0xd2, 0x81, 0x64, 0xe1, 0x39, 0x72, 0xcc, 0x23, 0x82, 0x9c, 0x37, 0xd9, 0x1d, 0xa4, 0x59,
|
||||
0x85, 0xa6, 0x29, 0x72, 0xda, 0x40, 0x83, 0xbc, 0x2a, 0xd3, 0x82, 0x64, 0xe2, 0x39, 0x72, 0xcc,
|
||||
0x24, 0x82, 0x9c, 0x82, 0x64, 0xe1, 0x3a, 0x72, 0xcc, 0x24, 0x82, 0x9c, 0x40, 0xe8, 0x1e, 0x89,
|
||||
0xcd, 0x44, 0x9d, 0x89, 0x37, 0xd9, 0x1e, 0xa5, 0x59, 0x85, 0xa6, 0x2a, 0x73, 0xda, 0x40, 0x84,
|
||||
0xbd, 0x2a, 0xd3, 0x82, 0x65, 0xe2, 0x39, 0x73, 0xcd, 0x24, 0x83, 0x9c, 0x40, 0x83, 0xbd, 0x2a,
|
||||
0xd3, 0x82, 0x65, 0xe2, 0x39, 0x73, 0xcd, 0x24, 0x83, 0x9c, 0xd2, 0x81, 0x63, 0xe0, 0x39, 0x71,
|
||||
0xcc, 0x23, 0x82, 0x9b, 0x9b, 0x1d, 0xa4, 0x59, 0x84, 0xa5, 0x29, 0x72, 0xd9, 0x3f, 0x83, 0xbc,
|
||||
0x29, 0xd3, 0x81, 0x64, 0xe1, 0x39, 0x72, 0xcc, 0x24, 0x82, 0x9c, 0x82, 0x9c, 0x3f
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xe7, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x88, 0x1b, 0x8a,
|
||||
0xe6, 0x88, 0x1a, 0x8a, 0x1c, 0x8b, 0xe7, 0x89, 0x1b, 0x8b, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x89,
|
||||
0x1b, 0x8a, 0xe6, 0x89, 0x1b, 0x8a, 0xe6, 0x89, 0x1a, 0x8a, 0xe6, 0x88, 0x1a, 0x89, 0xe5, 0x88,
|
||||
0x1a, 0x89, 0xe5, 0x88, 0x1a, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe4, 0x87, 0x19,
|
||||
0x89, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x1a, 0x8a, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x88,
|
||||
0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe4, 0x87, 0x19, 0x89, 0xe4, 0x87, 0x19, 0xe4, 0x87, 0x19,
|
||||
0xe4, 0x87, 0x1a, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0x1a, 0x89, 0xe5, 0x88,
|
||||
0x1a, 0x89, 0xe5, 0x88, 0x1a, 0x89, 0xe5, 0x87, 0x1a, 0x89, 0xe4, 0x88, 0x19, 0x89, 0xe4, 0x87,
|
||||
0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87,
|
||||
0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x88, 0xe3, 0x86,
|
||||
0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86,
|
||||
0x18, 0x87, 0xe2, 0x85, 0x18, 0x87, 0xe3, 0x85, 0x17, 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe4,
|
||||
0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3,
|
||||
0x86, 0x18, 0x87, 0xe3, 0x85, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18, 0x87, 0xe3,
|
||||
0x85, 0x18, 0x87, 0x19, 0x88, 0xe3, 0x86, 0x19, 0x88, 0xe3, 0x86, 0x18, 0x87, 0xe3, 0x86, 0x18,
|
||||
0x87, 0x1a, 0x89, 0xe4, 0x87, 0x19, 0x88, 0xe4, 0x87, 0x19, 0x1b, 0x89, 0x1c, 0x8b
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xa1, 0xe6, 0xcf, 0x79, 0x58, 0x4a, 0x78, 0x7a, 0x53, 0x7b, 0xc2, 0xc3, 0x82, 0x36, 0x48, 0x84,
|
||||
0xcb, 0xa6, 0x67, 0x2e, 0x49, 0x7c, 0xaa, 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xaa, 0x71, 0xa7,
|
||||
0x67, 0x2f, 0x4a, 0x7c, 0xab, 0x94, 0x87, 0x74, 0x62, 0x6d, 0xa4, 0xab, 0x72, 0x7b, 0xaa, 0x93,
|
||||
0x87, 0x72, 0x61, 0x6c, 0xa4, 0xaa, 0x71, 0x72, 0x7b, 0xc2, 0xc3, 0x81, 0x36, 0x48, 0x84, 0xcb,
|
||||
0xa6, 0x67, 0x2e, 0x49, 0x7b, 0xaa, 0x93, 0x87, 0x72, 0x61, 0x6c, 0xa4, 0xaa, 0x71, 0xaa, 0x71,
|
||||
0x59, 0xa0, 0xe5, 0xce, 0x78, 0x57, 0x49, 0x77, 0x79, 0x52, 0x7a, 0xc1, 0xc2, 0x81, 0x35, 0x47,
|
||||
0x83, 0xca, 0xa6, 0x66, 0x2d, 0x48, 0x7b, 0xa9, 0x92, 0x86, 0x72, 0x61, 0x6b, 0xa3, 0xa9, 0x70,
|
||||
0x82, 0x36, 0x49, 0x84, 0xcc, 0xa7, 0x67, 0x2e, 0x4a, 0x7c, 0xab, 0x94, 0x87, 0x73, 0x62, 0x6c,
|
||||
0xa4, 0xab, 0x72, 0xc2, 0xc3, 0x82, 0x36, 0x48, 0x84, 0xcb, 0xa6, 0x67, 0x2e, 0x4a, 0x7c, 0xaa,
|
||||
0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xaa, 0x71, 0xa3, 0xaa, 0x70, 0x49, 0x7b, 0xa9, 0x92, 0x86,
|
||||
0x72, 0x61, 0x6b, 0xa3, 0xaa, 0x70, 0x4b, 0x78, 0x7a, 0x53, 0x7c, 0xc2, 0xc3, 0x82, 0x36, 0x49,
|
||||
0x84, 0xcc, 0xa6, 0x67, 0x2f, 0x4a, 0x7c, 0xaa, 0x93, 0x87, 0x73, 0x62, 0x6c, 0xa4, 0xab, 0x72,
|
||||
0x7b, 0x5a, 0x4c, 0x7a, 0x7c, 0x55, 0x7e, 0xc4, 0xc5, 0x83, 0x38, 0x4a, 0x85, 0xcd, 0xa8, 0x69,
|
||||
0x30, 0x4b, 0x7d, 0xac, 0x95, 0x88, 0x74, 0x63, 0x6e, 0xa5, 0xac, 0x73, 0x7c, 0x55, 0x7d, 0xc4,
|
||||
0xc5, 0x83, 0x37, 0x4a, 0x85, 0xcd, 0xa8, 0x68, 0x30, 0x4b, 0x7d, 0xac, 0x95, 0x88, 0x74, 0x63,
|
||||
0x6e, 0xa6, 0xac, 0x73, 0xab, 0x94, 0x87, 0x73, 0x62, 0x6d, 0xa5, 0xab, 0x72, 0x5a
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
0xbf, 0xdc, 0xd4, 0xaa, 0x82, 0x81, 0x35, 0x34, 0x4d, 0x75, 0x95, 0xcb, 0xde, 0xc9, 0x91, 0x72,
|
||||
0x4a, 0x30, 0x6b, 0x57, 0x69, 0x7d, 0x65, 0x3f, 0x33, 0x4f, 0x69, 0x81, 0xc1, 0xde, 0xd6, 0xac,
|
||||
0x83, 0x82, 0x37, 0x36, 0x4f, 0x77, 0x96, 0xcd, 0xe0, 0xca, 0x93, 0x73, 0x4c, 0x31, 0x6d, 0x58,
|
||||
0x6a, 0x4f, 0x68, 0x80, 0xc0, 0xdd, 0xd5, 0xab, 0x82, 0x82, 0x36, 0x35, 0x4e, 0x76, 0x95, 0xcc,
|
||||
0xdf, 0xca, 0x92, 0x73, 0x4b, 0x31, 0x6c, 0x57, 0x6a, 0xe0, 0xca, 0x92, 0x73, 0x4c, 0x31, 0x6c,
|
||||
0x58, 0x6a, 0xad, 0xd5, 0xdb, 0xbc, 0x7b, 0x63, 0x3c, 0x31, 0x4e, 0x66, 0x7e, 0xbe, 0xdb, 0xd3,
|
||||
0xaa, 0x81, 0x80, 0x34, 0x33, 0x4c, 0x75, 0x94, 0xcb, 0xde, 0xc8, 0x91, 0x71, 0x4a, 0x2f, 0x6b,
|
||||
0x56, 0x68, 0x35, 0x4e, 0x76, 0x95, 0xcc, 0xdf, 0xca, 0x92, 0x72, 0x4b, 0x30, 0x6c, 0x57, 0x6a,
|
||||
0xcc, 0xdf, 0xca, 0x91, 0x72, 0x4b, 0x30, 0x6c, 0x57, 0x69, 0x69, 0x7e, 0xbe, 0xdb, 0xd3, 0xa9,
|
||||
0x81, 0x80, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xcb, 0xdd, 0xc8, 0x90, 0x71, 0x4a, 0x2f, 0x6b, 0x55,
|
||||
0x68, 0x56, 0x69, 0xac, 0xd4, 0xd9, 0xbb, 0x7a, 0x62, 0x3c, 0x30, 0x4c, 0x65, 0x7d, 0xbd, 0xda,
|
||||
0xd2, 0xa8, 0x80, 0x7f, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xca, 0xdd, 0xc8, 0x90, 0x71, 0x49, 0x2e,
|
||||
0x6a, 0x55, 0x67, 0xd3, 0xa9, 0x81, 0x80, 0x34, 0x33, 0x4c, 0x74, 0x94, 0xca, 0xde, 0xc8, 0x91,
|
||||
0x71, 0x4a, 0x2f, 0x6a, 0x56, 0x68, 0xbd, 0xda, 0xd2, 0xa8, 0x7f, 0x7e, 0x33, 0x32, 0x4b, 0x73,
|
||||
0x92, 0xc9, 0xdc, 0xc7, 0x8f, 0x70, 0x49, 0x2e, 0x69, 0x54, 0x67, 0x6a, 0x56, 0x68, 0x93, 0xca,
|
||||
0xdd, 0xc8, 0x90, 0x70, 0x49, 0x2e, 0x6a, 0x55, 0x68, 0x3d, 0x31, 0x4e, 0x67, 0x7f
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
void upd1771c_reset(struct upd1771c_t *scv) {
|
||||
memset(scv->upd1771c_packets,0,16);
|
||||
scv->upd1771c_mode = 0;
|
||||
scv->upd1771c_pos = 0;
|
||||
scv->upd1771c_posc = 0;
|
||||
scv->upd1771c_wave = 0;
|
||||
scv->upd1771c_vol = 0;
|
||||
scv->upd1771c_period = 0;
|
||||
scv->upd1771c_off = 0;
|
||||
scv->upd1771c_npos = 0;
|
||||
//scv->upd1771c_repsamp = 0;
|
||||
}
|
||||
|
||||
void upd1771c_write_packet(struct upd1771c_t *scv, uint8_t ind, uint8_t val) {
|
||||
scv->upd1771c_packets[ind&15] = val;
|
||||
switch (scv->upd1771c_packets[0]) {
|
||||
case 1:
|
||||
if (ind == 3) {
|
||||
scv->upd1771c_mode = 1;
|
||||
scv->upd1771c_wave = (scv->upd1771c_packets[1]&0xe0)>>5;
|
||||
scv->upd1771c_off = 0; //?
|
||||
scv->upd1771c_period = scv->upd1771c_packets[2];
|
||||
scv->upd1771c_vol = scv->upd1771c_packets[3]&0x1f;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (ind == 3) {
|
||||
scv->upd1771c_mode = 2;
|
||||
scv->upd1771c_wave = (scv->upd1771c_packets[1]&0xe0)>>5;
|
||||
scv->upd1771c_off = scv->upd1771c_packets[1]&0x1f;
|
||||
scv->upd1771c_period = scv->upd1771c_packets[2]<0x20?0x20:scv->upd1771c_packets[2];
|
||||
scv->upd1771c_vol = scv->upd1771c_packets[3]&0x1f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
case 0:
|
||||
scv->upd1771c_mode = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void upd1771c_sound_set_clock(struct upd1771c_t *scv, unsigned int clock, unsigned int divi) {
|
||||
scv->upd1771c_repsamp = divi;
|
||||
}
|
||||
|
||||
int16_t upd1771c_sound_stream_update(struct upd1771c_t *scv) {
|
||||
int16_t s = 0;
|
||||
for (int i = 0; i < scv->upd1771c_repsamp; i++) {
|
||||
s = 0;
|
||||
switch (scv->upd1771c_mode) {
|
||||
case 2:
|
||||
s = ((int16_t)WAVEFORMS[scv->upd1771c_wave][scv->upd1771c_posc])*scv->upd1771c_vol;
|
||||
scv->upd1771c_pos++;
|
||||
if (scv->upd1771c_pos >= scv->upd1771c_period) {
|
||||
scv->upd1771c_pos=0;
|
||||
scv->upd1771c_posc++;
|
||||
if (scv->upd1771c_posc == 32)
|
||||
scv->upd1771c_posc = scv->upd1771c_off;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
scv->upd1771c_pos++;
|
||||
if (scv->upd1771c_pos >= ((((uint32_t)scv->upd1771c_period) + 1)*128)) {
|
||||
scv->upd1771c_pos=0;
|
||||
scv->upd1771c_posc++;
|
||||
if (scv->upd1771c_posc == NOISE_SIZE)
|
||||
scv->upd1771c_posc = 0;
|
||||
}
|
||||
uint16_t p = scv->upd1771c_posc;
|
||||
p = p>=254?253:p;
|
||||
s = ((int16_t)(noise_tbl[scv->upd1771c_wave][p])-127)*scv->upd1771c_vol;
|
||||
// inaccurate noise mixing :/
|
||||
// s |= (scv->upd1771c_npos&128)?127*scv->upd1771c_vol:0;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
33
src/engine/platform/sound/upd1771c.h
Normal file
33
src/engine/platform/sound/upd1771c.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#ifndef __UPD1771C_SOUND_H__
|
||||
#define __UPD1771C_SOUND_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct upd1771c_t {
|
||||
uint8_t upd1771c_packets[16];
|
||||
uint8_t upd1771c_mode;
|
||||
uint32_t upd1771c_pos;
|
||||
uint8_t upd1771c_off;
|
||||
uint8_t upd1771c_posc;
|
||||
uint8_t upd1771c_wave;
|
||||
uint8_t upd1771c_vol;
|
||||
uint8_t upd1771c_period;
|
||||
uint8_t upd1771c_npos;
|
||||
int upd1771c_repsamp;
|
||||
};
|
||||
|
||||
void upd1771c_reset(struct upd1771c_t *scv);
|
||||
void upd1771c_write_packet(struct upd1771c_t *scv, uint8_t ind, uint8_t val);
|
||||
int16_t upd1771c_sound_stream_update(struct upd1771c_t *scv);
|
||||
void upd1771c_sound_set_clock(struct upd1771c_t *scv, unsigned int clock, unsigned int divi);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
339
src/engine/platform/sound/ymf278b/License.txt
Normal file
339
src/engine/platform/sound/ymf278b/License.txt
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
1094
src/engine/platform/sound/ymf278b/ymf278.cpp
Normal file
1094
src/engine/platform/sound/ymf278b/ymf278.cpp
Normal file
File diff suppressed because it is too large
Load diff
155
src/engine/platform/sound/ymf278b/ymf278.h
Normal file
155
src/engine/platform/sound/ymf278b/ymf278.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#ifndef YMF278_HH
|
||||
#define YMF278_HH
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#define UNREACHABLE while (1) assert(false)
|
||||
|
||||
using byte = uint8_t;
|
||||
|
||||
template<typename T>
|
||||
const T& YMF_clamp(const T& value, const T& min, const T& max) {
|
||||
return std::min(std::max(value, min), max);
|
||||
}
|
||||
|
||||
class MemoryInterface {
|
||||
public:
|
||||
virtual byte operator[](unsigned address) const = 0;
|
||||
virtual unsigned getSize() const = 0;
|
||||
virtual void write(unsigned address, byte value) = 0;
|
||||
virtual void clear(byte value) = 0;
|
||||
virtual void setMemoryType(bool memoryType) {};
|
||||
};
|
||||
|
||||
class YMF278Base
|
||||
{
|
||||
public:
|
||||
YMF278Base(MemoryInterface& memory, int channelCount, int clockDivider, double clockFrequency);
|
||||
~YMF278Base();
|
||||
int getChannelCount();
|
||||
int getClockDivider();
|
||||
double getClockFrequency();
|
||||
void setClockFrequency(double clockFrequency);
|
||||
double getSampleRate();
|
||||
virtual void reset();
|
||||
|
||||
void generate(short& fleft, short& fright, short& rleft, short& rright, short* channelBufs = nullptr);
|
||||
|
||||
class Slot final {
|
||||
public:
|
||||
Slot();
|
||||
void reset();
|
||||
int compute_rate(int val) const;
|
||||
int compute_decay_rate(int val) const;
|
||||
unsigned decay_rate(int num, int sample_rate);
|
||||
void envelope_next(int sample_rate);
|
||||
int16_t compute_vib() const;
|
||||
uint16_t compute_am() const;
|
||||
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, unsigned version);
|
||||
|
||||
uint32_t startaddr;
|
||||
uint16_t loopaddr;
|
||||
uint16_t endaddr; // Note: stored in 2s complement (0x0000 = 0, 0x0001 = -65536, 0xffff = -1)
|
||||
uint32_t step; // fixed-point frequency step
|
||||
// invariant: step == calcStep(OCT, FN)
|
||||
uint32_t stepptr; // fixed-point pointer into the sample
|
||||
uint16_t pos;
|
||||
|
||||
int16_t env_vol;
|
||||
|
||||
uint32_t lfo_cnt;
|
||||
|
||||
int16_t DL;
|
||||
uint16_t wave; // wavetable number
|
||||
uint16_t FN; // f-number TODO store 'FN | 1024'?
|
||||
int8_t OCT; // octave [-8..+7]
|
||||
bool PRVB; // pseudo-reverb
|
||||
uint8_t TLdest; // destination total level
|
||||
uint8_t TL; // total level (goes towards TLdest)
|
||||
uint8_t pan; // panpot 0..15
|
||||
bool ch; // channel select
|
||||
bool keyon; // slot keyed on
|
||||
bool DAMP;
|
||||
uint8_t lfo; // LFO speed 0..7
|
||||
uint8_t vib; // vibrato 0..7
|
||||
uint8_t AM; // AM level 0..7
|
||||
uint8_t AR; // 0..15
|
||||
uint8_t D1R; // 0..15
|
||||
uint8_t D2R; // 0..15
|
||||
uint8_t RC; // rate correction 0..15
|
||||
uint8_t RR; // 0..15
|
||||
|
||||
uint8_t bits; // width of the samples
|
||||
|
||||
uint8_t state; // envelope generator state
|
||||
bool lfo_active;
|
||||
};
|
||||
|
||||
protected:
|
||||
void keyOnHelper(Slot& slot);
|
||||
|
||||
MemoryInterface& memory;
|
||||
std::vector<Slot> slots;
|
||||
|
||||
private:
|
||||
int16_t getSample(Slot& slot, uint16_t pos) const;
|
||||
static uint16_t nextPos(Slot& slot, uint16_t pos, uint16_t increment);
|
||||
void advance();
|
||||
bool anyActive();
|
||||
|
||||
/** Global envelope generator counter. */
|
||||
unsigned eg_cnt;
|
||||
|
||||
unsigned channelCount, clockDivider;
|
||||
double clockFrequency;
|
||||
};
|
||||
|
||||
class YMF278 final : public YMF278Base {
|
||||
public:
|
||||
YMF278(MemoryInterface& memory);
|
||||
void reset() override;
|
||||
void writeReg(byte reg, byte data);
|
||||
byte readReg(byte reg);
|
||||
byte peekReg(byte reg) const;
|
||||
|
||||
void generateMix(short fmL, short fmR, short& bufFL, short& bufFR, short& bufRL, short& bufRR, short* channelBufs = nullptr) {
|
||||
generate(bufFL, bufFR, bufRL, bufRR, channelBufs);
|
||||
bufFL = std::min(std::max((pcmMixL * bufFL + fmMixL * fmL) >> 4, -0x8000), 0x7fff);
|
||||
bufFR = std::min(std::max((pcmMixR * bufFR + fmMixR * fmR) >> 4, -0x8000), 0x7fff);;
|
||||
}
|
||||
|
||||
private:
|
||||
int fmMixL, fmMixR, pcmMixL, pcmMixR;
|
||||
int memAdr;
|
||||
byte regs[256];
|
||||
};
|
||||
|
||||
class YMW258 final : public YMF278Base {
|
||||
public:
|
||||
YMW258(MemoryInterface& memory);
|
||||
void writeReg(byte channel, byte reg, byte data);
|
||||
};
|
||||
|
||||
class MemoryMoonSound : MemoryInterface {
|
||||
public:
|
||||
MemoryMoonSound(MemoryInterface& rom, MemoryInterface& ram);
|
||||
byte operator[](unsigned address) const override;
|
||||
unsigned getSize() const override;
|
||||
void write(unsigned address, byte value) override;
|
||||
void clear(byte value) override;
|
||||
void setMemoryType(bool memoryType) override;
|
||||
|
||||
private:
|
||||
unsigned getRamAddress(unsigned addr) const;
|
||||
|
||||
MemoryInterface& rom;
|
||||
MemoryInterface& ram;
|
||||
|
||||
bool memoryType;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -304,9 +304,9 @@ public:
|
|||
|
||||
// simple getters for debugging
|
||||
fm_operator<RegisterType> *debug_operator(uint32_t index) const { return m_op[index]; }
|
||||
int32_t debug_output(uint32_t index) const { return m_output[index]; }
|
||||
int32_t debug_special1() const { return m_special1; }
|
||||
int32_t debug_special2() const { return m_special2; }
|
||||
int32_t debug_output(uint32_t index) const { return m_output[index]; }
|
||||
int32_t debug_special1() const { return m_special1; }
|
||||
int32_t debug_special2() const { return m_special2; }
|
||||
|
||||
private:
|
||||
// helper to add values to the outputs based on channel enables
|
||||
|
|
@ -320,21 +320,21 @@ private:
|
|||
constexpr int out3_index = 3 % RegisterType::OUTPUTS;
|
||||
|
||||
if (RegisterType::OUTPUTS == 1 || m_regs.ch_output_0(choffs)) {
|
||||
m_output[out0_index]=value;
|
||||
m_output[out0_index]=value;
|
||||
output.data[out0_index] += value;
|
||||
}
|
||||
}
|
||||
if (RegisterType::OUTPUTS >= 2 && m_regs.ch_output_1(choffs)) {
|
||||
m_output[out1_index]=value;
|
||||
m_output[out1_index]=value;
|
||||
output.data[out1_index] += value;
|
||||
}
|
||||
}
|
||||
if (RegisterType::OUTPUTS >= 3 && m_regs.ch_output_2(choffs)) {
|
||||
m_output[out2_index]=value;
|
||||
m_output[out2_index]=value;
|
||||
output.data[out2_index] += value;
|
||||
}
|
||||
}
|
||||
if (RegisterType::OUTPUTS >= 4 && m_regs.ch_output_3(choffs)) {
|
||||
m_output[out3_index]=value;
|
||||
m_output[out3_index]=value;
|
||||
output.data[out3_index] += value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// internal state
|
||||
|
|
@ -344,9 +344,9 @@ private:
|
|||
fm_operator<RegisterType> *m_op[4]; // up to 4 operators
|
||||
RegisterType &m_regs; // direct reference to registers
|
||||
fm_engine_base<RegisterType> &m_owner; // reference to the owning engine
|
||||
mutable int32_t m_output[4];
|
||||
mutable int32_t m_special1;
|
||||
mutable int32_t m_special2;
|
||||
mutable int32_t m_output[4];
|
||||
mutable int32_t m_special1;
|
||||
mutable int32_t m_special2;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -529,7 +529,7 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
protected:
|
||||
// internal state
|
||||
uint8_t m_address; // address register
|
||||
|
|
@ -577,8 +577,8 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; }
|
||||
|
||||
protected:
|
||||
// internal state
|
||||
|
|
@ -628,7 +628,7 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
|
||||
protected:
|
||||
// internal state
|
||||
|
|
@ -677,7 +677,7 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
|
||||
protected:
|
||||
// internal state
|
||||
|
|
@ -791,6 +791,8 @@ public:
|
|||
// generate samples of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
pcm_engine* debug_pcm_engine() { return &m_pcm; }
|
||||
protected:
|
||||
// internal state
|
||||
uint16_t m_address; // address register
|
||||
|
|
|
|||
|
|
@ -309,6 +309,7 @@ void pcm_channel::clock(uint32_t env_counter)
|
|||
|
||||
void pcm_channel::output(output_data &output) const
|
||||
{
|
||||
m_output[0] = m_output[1] = m_output[2] = m_output[3] = 0;
|
||||
// early out if the envelope is effectively off
|
||||
uint32_t envelope = m_env_attenuation;
|
||||
if (envelope > EG_QUIET)
|
||||
|
|
@ -340,6 +341,8 @@ void pcm_channel::output(output_data &output) const
|
|||
uint32_t outnum = m_regs.ch_output_channel(m_choffs) * 2;
|
||||
output.data[outnum + 0] += (lvol * sample) >> 15;
|
||||
output.data[outnum + 1] += (rvol * sample) >> 15;
|
||||
m_output[outnum + 0] = output.data[outnum + 0];
|
||||
m_output[outnum + 1] = output.data[outnum + 1];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -267,6 +267,8 @@ public:
|
|||
// load a new wavetable entry
|
||||
void load_wavetable();
|
||||
|
||||
int32_t debug_output(uint32_t index) const { return m_output[index]; }
|
||||
|
||||
private:
|
||||
// internal helpers
|
||||
void start_attack();
|
||||
|
|
@ -291,6 +293,7 @@ private:
|
|||
pcm_cache m_cache; // cached data
|
||||
pcm_registers &m_regs; // reference to registers
|
||||
pcm_engine &m_owner; // reference to our owner
|
||||
mutable int32_t m_output[4];
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -331,6 +334,8 @@ public:
|
|||
// return a reference to our registers
|
||||
pcm_registers ®s() { return m_regs; }
|
||||
|
||||
// simple getters for debugging
|
||||
pcm_channel *debug_channel(uint32_t index) const { return m_channel[index].get(); }
|
||||
private:
|
||||
// internal state
|
||||
ymfm_interface &m_intf; // reference to the interface
|
||||
|
|
|
|||
|
|
@ -67,6 +67,22 @@ void DivPlatformSoundUnit::writeControlUpper(int ch) {
|
|||
void DivPlatformSoundUnit::tick(bool sysTick) {
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i].std.next();
|
||||
if (sysTick) {
|
||||
if (chan[i].pw_slide!=0) {
|
||||
chan[i].virtual_duty-=chan[i].pw_slide;
|
||||
chan[i].virtual_duty=CLAMP(chan[i].virtual_duty,0,0xfff);
|
||||
chan[i].duty=chan[i].virtual_duty>>5;
|
||||
|
||||
chWrite(i,0x08,chan[i].duty);
|
||||
}
|
||||
if (chan[i].cutoff_slide!=0) {
|
||||
chan[i].cutoff+=chan[i].cutoff_slide*4;
|
||||
chan[i].cutoff=CLAMP(chan[i].cutoff,0,0x3fff);
|
||||
|
||||
chWrite(i,0x06,chan[i].cutoff&0xff);
|
||||
chWrite(i,0x07,chan[i].cutoff>>8);
|
||||
}
|
||||
}
|
||||
if (chan[i].std.vol.had) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
|
|
@ -86,6 +102,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
chan[i].virtual_duty=(unsigned short)chan[i].duty<<5;
|
||||
chWrite(i,0x08,chan[i].duty);
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
|
|
@ -351,6 +368,7 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
chan[c.chan].duty=c.value&127;
|
||||
chan[c.chan].virtual_duty=(unsigned short)chan[c.chan].duty << 5;
|
||||
chWrite(c.chan,0x08,chan[c.chan].duty);
|
||||
break;
|
||||
case DIV_CMD_C64_RESONANCE:
|
||||
|
|
@ -498,6 +516,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_SU(c.chan,chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_C64_PW_SLIDE:
|
||||
chan[c.chan].pw_slide=c.value*c.value2;
|
||||
break;
|
||||
case DIV_CMD_C64_CUTOFF_SLIDE:
|
||||
chan[c.chan].cutoff_slide=c.value*c.value2;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -564,6 +588,11 @@ void DivPlatformSoundUnit::reset() {
|
|||
for (int i=0; i<8; i++) {
|
||||
chan[i]=DivPlatformSoundUnit::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
|
||||
chan[i].cutoff_slide=0;
|
||||
chan[i].pw_slide=0;
|
||||
|
||||
chan[i].virtual_duty=0x800; // for some reason duty by default is 50%
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,9 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
signed short wave;
|
||||
unsigned short hwSeqPos;
|
||||
short hwSeqDelay;
|
||||
short cutoff_slide;
|
||||
short pw_slide;
|
||||
short virtual_duty;
|
||||
Channel():
|
||||
SharedChannel<signed char>(127),
|
||||
cutoff(16383),
|
||||
|
|
@ -71,7 +74,10 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
syncTimer(0),
|
||||
wave(0),
|
||||
hwSeqPos(0),
|
||||
hwSeqDelay(0) {}
|
||||
hwSeqDelay(0),
|
||||
cutoff_slide(0),
|
||||
pw_slide(0),
|
||||
virtual_duty(0) {}
|
||||
};
|
||||
Channel chan[8];
|
||||
DivDispatchOscBuffer* oscBuf[8];
|
||||
|
|
|
|||
599
src/engine/platform/supervision.cpp
Normal file
599
src/engine/platform/supervision.cpp
Normal file
|
|
@ -0,0 +1,599 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 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 "supervision.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
|
||||
const char* regCheatSheetSupervision[]={
|
||||
"Freq0L", "10",
|
||||
"Freq0H", "11",
|
||||
"Control0", "12",
|
||||
"Len0", "13",
|
||||
|
||||
"Freq1L", "14",
|
||||
"Freq1H", "15",
|
||||
"Control1", "16",
|
||||
"Len1", "17",
|
||||
|
||||
"DMAstartL", "18",
|
||||
"DMAstartH", "19",
|
||||
"DMAlen", "1a",
|
||||
"DMAstep", "1b",
|
||||
"DMAon", "1c",
|
||||
|
||||
"NoisDiv", "28",
|
||||
"NoisLen", "29",
|
||||
"NoisCtrl", "2a",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformSupervision::getRegisterSheet() {
|
||||
return regCheatSheetSupervision;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::acquire(short** buf, size_t len) {
|
||||
int mask_bits=0;
|
||||
for (int i=0; i<4; i++)
|
||||
mask_bits |= isMuted[i]?0:8>>i;
|
||||
supervision_set_mute_mask(&svision,mask_bits);
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
supervision_memorymap_registers_write(&svision,w.addr|0x2000,w.val);
|
||||
regPool[w.addr&0x3f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
unsigned char s[6];
|
||||
supervision_sound_stream_update(&svision,s,2);
|
||||
tempL[0]=(((int)s[0])-128)*256;
|
||||
tempR[0]=(((int)s[1])-128)*256;
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP((((int)s[2+i])-128)*256,-32768,32767);
|
||||
}
|
||||
|
||||
tempL[0]=(tempL[0]>>1)+(tempL[0]>>2);
|
||||
tempR[0]=(tempR[0]>>1)+(tempR[0]>>2);
|
||||
|
||||
if (tempL[0]<-32768) tempL[0]=-32768;
|
||||
if (tempL[0]>32767) tempL[0]=32767;
|
||||
if (tempR[0]<-32768) tempR[0]=-32768;
|
||||
if (tempR[0]>32767) tempR[0]=32767;
|
||||
|
||||
//printf("tempL: %d tempR: %d\n",tempL,tempR);
|
||||
buf[0][h]=tempL[0];
|
||||
buf[1][h]=tempR[0];
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
int f=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
if (i==2 || i==3) {
|
||||
chan[i].baseFreq=f;
|
||||
//if (chan[i].baseFreq>255) chan[i].baseFreq=255;
|
||||
if (chan[i].baseFreq<0) chan[i].baseFreq=0;
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(f);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
}
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].pan=chan[i].std.panL.val&3;
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
if (i<2) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock<<1,CHIP_DIVIDER);
|
||||
if (chan[i].freq<1) chan[i].freq=1;
|
||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||
if (chan[i].freqChanged || chan[i].initWrite) {
|
||||
rWrite(0x10|(i<<2),chan[i].freq&0xff);
|
||||
rWrite(0x11|(i<<2),(chan[i].freq>>8)&0x7);
|
||||
}
|
||||
chan[i].initWrite=false;
|
||||
} else if (i==3) {
|
||||
int ntPos=chan[i].baseFreq;
|
||||
if (NEW_ARP_STRAT) {
|
||||
if (chan[i].fixedArp) {
|
||||
ntPos=chan[i].baseNoteOverride;
|
||||
} else {
|
||||
ntPos+=chan[i].arpOff;
|
||||
}
|
||||
}
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=15-(ntPos&15);
|
||||
unsigned char r=(chan[i].freq<<4)|(chan[i].outVol&0xf);
|
||||
rWrite(0x28,r);
|
||||
noiseReg[0]=r;
|
||||
rWrite(0x29,0xc8);
|
||||
r=((chan[i].duty&1)^dutySwap)|(0x02|0x10)|(chan[i].pan<<2);
|
||||
rWrite(0x2A,r);
|
||||
}
|
||||
if (chan[i].keyOn && i==2) {
|
||||
if (chan[i].pcm) {
|
||||
int ntPos=chan[i].sampleNote;
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=3-(ntPos&3);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
unsigned int off=MIN(sampleOff[sNum]+chan[i].hasOffset,sampleOff[sNum]+sampleLen[sNum]);
|
||||
unsigned int len=MAX((((int)sampleLen[sNum])-((int)chan[i].hasOffset)),0);
|
||||
if (len) {
|
||||
rWrite(0x18,off&0xff);
|
||||
rWrite(0x19,(off>>8&0x3f)|0x80);
|
||||
rWrite(0x1A,MIN(MAX(len>>4,0),255));
|
||||
rWrite(0x1B,chan[i].freq|((chan[i].pan&3)<<2)|((off>>14&7)<<4));
|
||||
rWrite(0x1C,0x80);
|
||||
}
|
||||
sampleOffset=chan[i].hasOffset;
|
||||
chan[i].hasOffset=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOff && i==2) {
|
||||
if (chan[i].pcm) {
|
||||
int ntPos=chan[i].sampleNote;
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=3-(ntPos&3);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
rWrite(0x1C,0x00);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOn) chan[i].kon=true;
|
||||
if (chan[i].keyOff) chan[i].kon=false;
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
||||
if (chan[i].kon) {
|
||||
if (i<2) {
|
||||
rWrite(0x12|(i<<2),(chan[i].outVol&0xf)|((chan[i].duty&3)<<4));
|
||||
rWrite(0x13|(i<<2),0xc8);
|
||||
} else if (i == 3) {
|
||||
int ntPos=chan[i].baseFreq;
|
||||
if (NEW_ARP_STRAT) {
|
||||
if (chan[i].fixedArp) {
|
||||
ntPos=chan[i].baseNoteOverride;
|
||||
} else {
|
||||
ntPos+=chan[i].arpOff;
|
||||
}
|
||||
}
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=15-(ntPos&15);
|
||||
unsigned char r=(chan[i].freq<<4)|(chan[i].outVol&0xf);
|
||||
if (noiseReg[0] != r) rWrite(0x28,r);
|
||||
noiseReg[0]=r;
|
||||
rWrite(0x29,0xc8);
|
||||
r=((chan[i].duty&1)^dutySwap)|(0x02|0x10)|(chan[i].pan<<2);
|
||||
if (noiseReg[2] != r) rWrite(0x2A,r);
|
||||
noiseReg[2]=r;
|
||||
} else if (i==2) {
|
||||
if (chan[i].pcm) {
|
||||
int ntPos=chan[i].sampleNote;
|
||||
ntPos+=chan[i].pitch2;
|
||||
chan[i].freq=3-(ntPos&3);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
unsigned int off=MIN(sampleOff[sNum]+sampleOffset,sampleOff[sNum]+sampleLen[sNum]);
|
||||
unsigned int len=MAX((((int)sampleLen[sNum])-((int)sampleOffset)),0);
|
||||
if (len) {
|
||||
rWrite(0x1A,MIN(MAX(len>>4,0),255));
|
||||
if (chan[i].outVol==0) {
|
||||
rWrite(0x1B,chan[i].freq|((off>>14&7)<<4));
|
||||
} else {
|
||||
rWrite(0x1B,chan[i].freq|((chan[i].pan&3)<<2)|((off>>14&7)<<4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i < 2) {
|
||||
rWrite(0x12|(i<<2),0);
|
||||
rWrite(0x13|(i<<2),0xc8);
|
||||
} else if (i == 3) {
|
||||
rWrite(0x29,0);
|
||||
unsigned char r=0;
|
||||
if (noiseReg[2] != r) rWrite(0x2A,r);
|
||||
noiseReg[2]=r;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION);
|
||||
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
|
||||
chan[c.chan].pcm=true;
|
||||
} else {
|
||||
chan[c.chan].pcm=false;
|
||||
}
|
||||
if (chan[c.chan].pcm) {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
if (c.chan==2) {
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sampleNote=DIV_NOTE_NULL;
|
||||
chan[c.chan].sampleNoteDelta=0;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=c.chan==3?c.value:NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].vol);
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
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].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
if (chan[c.chan].active) {
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].outVol);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
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_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta);
|
||||
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_STD_NOISE_MODE:
|
||||
chan[c.chan].duty=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_POS:
|
||||
chan[c.chan].hasOffset=c.value;
|
||||
chan[c.chan].keyOn=true;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
chan[c.chan].pan=0;
|
||||
if (c.value&0xf0) chan[c.chan].pan|=2;
|
||||
if (c.value2>>4) chan[c.chan].pan|=1;
|
||||
if (chan[c.chan].pan==0) chan[c.chan].pan=3;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SUPERVISION));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_CMD_MACRO_RESTART:
|
||||
chan[c.chan].std.restart(c.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::forceIns() {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
//chwrite(i,0x05,isMuted[i]?0:chan[i].pan);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformSupervision::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformSupervision::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformSupervision::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformSupervision::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::getRegisterPoolSize() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::reset() {
|
||||
writes.clear();
|
||||
memset(regPool,0,64);
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformSupervision::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
supervision_sound_reset(&svision);
|
||||
memset(tempL,0,32*sizeof(int));
|
||||
memset(tempR,0,32*sizeof(int));
|
||||
memset(noiseReg,0,3*sizeof(unsigned char));
|
||||
noiseReg[2]=0xff;
|
||||
sampleOffset=0;
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::getOutputCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool DivPlatformSupervision::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::setFlags(const DivConfig& flags) {
|
||||
if (flags.getInt("swapDuty",true)) {
|
||||
dutySwap=1;
|
||||
} else {
|
||||
dutySwap=0;
|
||||
}
|
||||
otherFlags=0;
|
||||
if (flags.getInt("sqStereo",false)) {
|
||||
otherFlags |= 1;
|
||||
}
|
||||
chipClock=4000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/64;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
supervision_sound_set_clock(&svision,(unsigned int)chipClock);
|
||||
supervision_sound_set_flags(&svision,(unsigned int)otherFlags);
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
const void* DivPlatformSupervision::getSampleMem(int index) {
|
||||
return index==0?sampleMem:NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformSupervision::getSampleMemCapacity(int index) {
|
||||
return index==0?65536:0;
|
||||
}
|
||||
|
||||
size_t DivPlatformSupervision::getSampleMemUsage(int index) {
|
||||
return index==0?sampleMemLen:0;
|
||||
}
|
||||
|
||||
bool DivPlatformSupervision::isSampleLoaded(int index, int sample) {
|
||||
if (index!=0) return false;
|
||||
if (sample<0 || sample>255) return false;
|
||||
return sampleLoaded[sample];
|
||||
}
|
||||
|
||||
const DivMemoryComposition* DivPlatformSupervision::getMemCompo(int index) {
|
||||
if (index!=0) return NULL;
|
||||
return &memCompo;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::renderSamples(int sysID) {
|
||||
memset(sampleMem,0,getSampleMemCapacity(0));
|
||||
memset(sampleLoaded,0,256*sizeof(bool));
|
||||
|
||||
memCompo=DivMemoryComposition();
|
||||
memCompo.name="Sample Memory";
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
sampleOff[i]=0;
|
||||
continue;
|
||||
}
|
||||
unsigned int paddedLen=((s->length8>>1)+31)&(~0x1f);
|
||||
logV("%d padded length: %d",i,paddedLen);
|
||||
if (paddedLen>=4096) {
|
||||
paddedLen=4096;
|
||||
}
|
||||
if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) {
|
||||
memPos=(memPos+0x3fff)&(~0x3fff);
|
||||
}
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
|
||||
sampleLen[i]=(getSampleMemCapacity(0)-memPos)>>1;
|
||||
for (size_t i=0; i<(getSampleMemCapacity(0)-memPos)>>1; i++) {
|
||||
sampleMem[memPos+i]=(((s->data8[i*2+0]+128)>>4)<<4&0xf0)|(((s->data8[i*2+1]+128)>>4)&0xf);
|
||||
}
|
||||
logW("out of memory for sample %d!",i);
|
||||
} else {
|
||||
size_t len=MIN((s->length8>>1),paddedLen);
|
||||
sampleLen[i]=(unsigned int)(len);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
sampleMem[memPos+i]=(((s->data8[i*2+0]+128)>>4)<<4&0xf0)|(((s->data8[i*2+1]+128)>>4)&0xf);
|
||||
}
|
||||
sampleLoaded[i]=true;
|
||||
}
|
||||
sampleOff[i]=memPos;
|
||||
memCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"Sample",i,memPos,memPos+paddedLen));
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
|
||||
memCompo.capacity=65536;
|
||||
memCompo.used=sampleMemLen;
|
||||
}
|
||||
|
||||
bool DivPlatformSupervision::getDCOffRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
int DivPlatformSupervision::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
sampleMem=svision.supervision_dma_mem;
|
||||
dutySwap=0;
|
||||
otherFlags=0;
|
||||
sampleOffset=0;
|
||||
sampleMemLen=0;
|
||||
memset(sampleMem,0,65536);
|
||||
svision.ch_mask=15;
|
||||
svision.flags=1;
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 4;
|
||||
}
|
||||
|
||||
void DivPlatformSupervision::quit() {
|
||||
for (int i=0; i<4; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformSupervision::~DivPlatformSupervision() {
|
||||
}
|
||||
104
src/engine/platform/supervision.h
Normal file
104
src/engine/platform/supervision.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 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 _SUPERVISION_H
|
||||
#define _SUPERVISION_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../fixedQueue.h"
|
||||
#include "sound/supervision.h"
|
||||
|
||||
class DivPlatformSupervision: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
unsigned int duty, len, pan, pcm; // pcm is channel 3 ONLY
|
||||
int sample, hasOffset; // again, for channel 3 ONLY
|
||||
bool setPos, kon, initWrite;
|
||||
Channel():
|
||||
SharedChannel<signed char>(63),
|
||||
duty(0),
|
||||
len(0x1f),
|
||||
pan(3),
|
||||
pcm(false),
|
||||
hasOffset(0),
|
||||
setPos(false),
|
||||
kon(false),
|
||||
initWrite(true) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(9) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,512> writes;
|
||||
|
||||
int curChan;
|
||||
int tempL[32];
|
||||
int tempR[32];
|
||||
int coreQuality;
|
||||
unsigned char regPool[64];
|
||||
unsigned int sampleOff[256];
|
||||
unsigned int sampleLen[256];
|
||||
bool sampleLoaded[256];
|
||||
DivMemoryComposition memCompo;
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
unsigned char dutySwap;
|
||||
unsigned char otherFlags;
|
||||
unsigned int sampleOffset;
|
||||
unsigned char noiseReg[3];
|
||||
struct svision_t svision;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int getOutputCount();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
bool isSampleLoaded(int index, int sample);
|
||||
const DivMemoryComposition* getMemCompo(int index);
|
||||
void renderSamples(int chipID);
|
||||
bool getDCOffRequired();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformSupervision();
|
||||
};
|
||||
|
||||
#endif
|
||||
366
src/engine/platform/upd1771c.cpp
Normal file
366
src/engine/platform/upd1771c.cpp
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 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 "upd1771c.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 64
|
||||
|
||||
const char* regCheatSheetUPD1771c[]={
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformUPD1771c::getRegisterSheet() {
|
||||
return regCheatSheetUPD1771c;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::acquire(short** buf, size_t len) {
|
||||
for (size_t h=0; h<len; h++) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
upd1771c_write_packet(&scv,w.addr&15,w.val);
|
||||
regPool[w.addr&0xf]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
short s=upd1771c_sound_stream_update(&scv)<<3;
|
||||
if (isMuted[0]) s=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=s;
|
||||
buf[0][h]=s;
|
||||
buf[1][h]=s;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::tick(bool sysTick) {
|
||||
for (int i=0; i<1; i++) {
|
||||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31);
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
int f=parent->calcArp(chan[i].note,chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_PERIODIC(f);
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
chan[i].wave=chan[i].std.wave.val&7;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val&1;
|
||||
}
|
||||
if (chan[i].std.ex1.had) {
|
||||
chan[i].pos=chan[i].std.ex1.val&31;
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
if (i==0) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
}
|
||||
if (chan[i].freqChanged || initWrite[i] || chan[i].keyOn) {
|
||||
if (chan[i].duty == 0) {
|
||||
rWrite(0,2);
|
||||
rWrite(1,(chan[i].wave<<5)|chan[i].pos);
|
||||
float p = ((float)chan[i].freq)/((float)(31-chan[i].pos))*31.0;
|
||||
rWrite(2,MIN(MAX((int)p,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else if (chan[i].duty == 1) {
|
||||
rWrite(0,1);
|
||||
rWrite(1,(chan[i].wave<<5));
|
||||
rWrite(2,MIN(MAX(chan[i].freq>>7,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else {
|
||||
rWrite(0,0);
|
||||
}
|
||||
initWrite[i]=0;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(0,0);
|
||||
}
|
||||
if (chan[i].keyOn) kon[i]=1;
|
||||
if (chan[i].keyOff) kon[i]=0;
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
||||
if (kon[i]) {
|
||||
if (i==0) {
|
||||
if (chan[i].duty == 0) {
|
||||
rWrite(0,2);
|
||||
rWrite(1,(chan[i].wave<<5)|chan[i].pos);
|
||||
// TODO: improve
|
||||
float p = ((float)chan[i].freq)/((float)(32-chan[i].pos))*32.0;
|
||||
rWrite(2,MIN(MAX((int)p,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else if (chan[i].duty == 1) {
|
||||
rWrite(0,1);
|
||||
rWrite(1,(chan[i].wave<<5));
|
||||
rWrite(2,MIN(MAX(chan[i].freq>>7,0),255));
|
||||
rWrite(3,chan[i].outVol);
|
||||
} else {
|
||||
rWrite(0,0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (i == 0) {
|
||||
rWrite(0,0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=c.chan==3?c.value:NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].vol);
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
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].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
if (chan[c.chan].active) {
|
||||
//chwrite(c.chan,0x04,0x80|chan[c.chan].outVol);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
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_PERIODIC(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_STD_NOISE_MODE:
|
||||
chan[c.chan].wave=c.value&7;
|
||||
chan[c.chan].duty=c.value>>4&1;
|
||||
break;
|
||||
case DIV_CMD_N163_WAVE_POSITION:
|
||||
chan[c.chan].pos=c.value;
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(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].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_UPD1771C));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 31;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_CMD_MACRO_RESTART:
|
||||
chan[c.chan].std.restart(c.value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::forceIns() {
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
//chwrite(i,0x05,isMuted[i]?0:chan[i].pan);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformUPD1771c::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformUPD1771c::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformUPD1771c::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformUPD1771c::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::getRegisterPoolSize() {
|
||||
return 16;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::reset() {
|
||||
writes.clear();
|
||||
memset(regPool,0,16);
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i]=DivPlatformUPD1771c::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
upd1771c_reset(&scv);
|
||||
memset(tempL,0,32*sizeof(int));
|
||||
memset(tempR,0,32*sizeof(int));
|
||||
memset(kon,0,1*sizeof(unsigned char));
|
||||
memset(initWrite,1,1*sizeof(unsigned char));
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::getOutputCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
bool DivPlatformUPD1771c::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<1; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::setFlags(const DivConfig& flags) {
|
||||
chipClock=6000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/32;
|
||||
for (int i=0; i<1; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
upd1771c_sound_set_clock(&scv,(unsigned int)chipClock,8);
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformUPD1771c::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<1; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformUPD1771c::quit() {
|
||||
for (int i=0; i<1; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformUPD1771c::~DivPlatformUPD1771c() {
|
||||
}
|
||||
84
src/engine/platform/upd1771c.h
Normal file
84
src/engine/platform/upd1771c.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2024 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 _UPD1771C_H
|
||||
#define _UPD1771C_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../fixedQueue.h"
|
||||
#include "sound/upd1771c.h"
|
||||
|
||||
class DivPlatformUPD1771c: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
unsigned int wave;
|
||||
int pos, duty;
|
||||
Channel():
|
||||
SharedChannel<signed char>(15),
|
||||
wave(0),
|
||||
pos(0),
|
||||
duty(0) {}
|
||||
};
|
||||
Channel chan[1];
|
||||
DivDispatchOscBuffer* oscBuf[1];
|
||||
bool isMuted[4];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(9) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,512> writes;
|
||||
|
||||
int curChan;
|
||||
int tempL[32];
|
||||
int tempR[32];
|
||||
int coreQuality;
|
||||
unsigned char regPool[16];
|
||||
unsigned char kon[1];
|
||||
unsigned char initWrite[1];
|
||||
struct upd1771c_t scv;
|
||||
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
int getOutputCount();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformUPD1771c();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -173,6 +173,7 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -255,6 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -311,6 +313,16 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
|
|||
fmOut[i]=0;
|
||||
}
|
||||
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
while (true) {
|
||||
bool canWeWrite=fm_lle.prescaler_latch[1]&1;
|
||||
|
||||
|
|
@ -444,6 +456,10 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::tick(bool sysTick) {
|
||||
// PSG
|
||||
ay->tick(sysTick);
|
||||
|
|
@ -1006,7 +1022,20 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
|
|||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -440,6 +441,7 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -680,6 +682,10 @@ void DivPlatformYM2608::acquire_lle(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::tick(bool sysTick) {
|
||||
// FM
|
||||
for (int i=0; i<6; i++) {
|
||||
|
|
@ -1539,7 +1545,20 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
|
|||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -373,6 +374,7 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -1509,7 +1511,20 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -324,6 +324,7 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -439,6 +440,7 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -1578,7 +1580,20 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
}
|
||||
|
||||
public:
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
writeADPCMAOff=0;
|
||||
writeADPCMAOn=0;
|
||||
|
|
|
|||
|
|
@ -265,8 +265,47 @@ const char* cmdName[]={
|
|||
"BIFURCATOR_PARAMETER",
|
||||
|
||||
"FDS_MOD_AUTO",
|
||||
|
||||
"FM_OPMASK",
|
||||
|
||||
"FM_OPMASK"
|
||||
"MULTIPCM_MIX_FM",
|
||||
"MULTIPCM_MIX_PCM",
|
||||
"MULTIPCM_LFO",
|
||||
"MULTIPCM_VIB",
|
||||
"MULTIPCM_AM",
|
||||
"MULTIPCM_AR",
|
||||
"MULTIPCM_D1R",
|
||||
"MULTIPCM_DL",
|
||||
"MULTIPCM_D2R",
|
||||
"MULTIPCM_RR",
|
||||
"MULTIPCM_RC",
|
||||
"MULTIPCM_DAMP",
|
||||
"MULTIPCM_PSEUDO_REVERB",
|
||||
"MULTIPCM_LFO_RESET",
|
||||
"MULTIPCM_LEVEL_DIRECT",
|
||||
|
||||
"SID3_SPECIAL_WAVE",
|
||||
"SID3_RING_MOD_SRC",
|
||||
"SID3_HARD_SYNC_SRC",
|
||||
"SID3_PHASE_MOD_SRC",
|
||||
"SID3_WAVE_MIX",
|
||||
"SID3_LFSR_FEEDBACK_BITS",
|
||||
"SID3_1_BIT_NOISE",
|
||||
"SID3_FILTER_DISTORTION",
|
||||
"SID3_FILTER_OUTPUT_VOLUME",
|
||||
"SID3_CHANNEL_INVERSION",
|
||||
"SID3_FILTER_CONNECTION",
|
||||
"SID3_FILTER_MATRIX",
|
||||
"SID3_FILTER_ENABLE",
|
||||
|
||||
"C64_PW_SLIDE",
|
||||
"C64_CUTOFF_SLIDE",
|
||||
|
||||
"SID3_PHASE_RESET",
|
||||
"SID3_NOISE_PHASE_RESET",
|
||||
"SID3_ENVELOPE_RESET",
|
||||
"SID3_CUTOFF_SCALING",
|
||||
"SID3_RESONANCE_SCALING"
|
||||
};
|
||||
|
||||
static_assert((sizeof(cmdName)/sizeof(void*))==DIV_CMD_MAX,"update cmdName!");
|
||||
|
|
@ -536,7 +575,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
bool comparison=(song.delayBehavior==1)?(effectVal<=nextSpeed):(effectVal<(nextSpeed*(curSubSong->timeBase+1)));
|
||||
if (song.delayBehavior==2) comparison=true;
|
||||
if (comparison) {
|
||||
chan[i].rowDelay=effectVal+1;
|
||||
chan[i].rowDelay=effectVal;
|
||||
chan[i].delayOrder=whatOrder;
|
||||
chan[i].delayRow=whatRow;
|
||||
if (effectVal==nextSpeed) {
|
||||
|
|
@ -1580,6 +1619,19 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
if (--subticks<=0) {
|
||||
subticks=tickMult;
|
||||
|
||||
// apply delayed rows before potentially advancing to a new row, which would overwrite the
|
||||
// delayed row's state before it has a chance to do anything. a typical example would be
|
||||
// a delay scheduling a note-on to be simultaneous with the next row, and the next row also
|
||||
// containing a delayed note. if we don't apply the delayed row first,
|
||||
for (int i=0; i<chans; i++) {
|
||||
// delay effects
|
||||
if (chan[i].rowDelay>0) {
|
||||
if (--chan[i].rowDelay==0) {
|
||||
processRow(i,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stepPlay!=1) {
|
||||
tempoAccum+=(skipping && virtualTempoN<virtualTempoD)?virtualTempoD:virtualTempoN;
|
||||
while (tempoAccum>=virtualTempoD) {
|
||||
|
|
@ -1610,15 +1662,9 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
// under no circumstances shall the accumulator become this large
|
||||
if (tempoAccum>1023) tempoAccum=1023;
|
||||
}
|
||||
|
||||
// process stuff
|
||||
if (!shallStop) for (int i=0; i<chans; i++) {
|
||||
// delay effects
|
||||
if (chan[i].rowDelay>0) {
|
||||
if (--chan[i].rowDelay==0) {
|
||||
processRow(i,true);
|
||||
}
|
||||
}
|
||||
|
||||
// retrigger
|
||||
if (chan[i].retrigSpeed) {
|
||||
if (--chan[i].retrigTick<0) {
|
||||
|
|
@ -1632,6 +1678,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
if (!song.noSlidesOnFirstTick || !firstTick) {
|
||||
if (chan[i].volSpeed!=0) {
|
||||
chan[i].volume=(chan[i].volume&0xff)|(dispatchCmd(DivCommand(DIV_CMD_GET_VOLUME,i))<<8);
|
||||
int preSpeedVol=chan[i].volume;
|
||||
chan[i].volume+=chan[i].volSpeed;
|
||||
if (chan[i].volSpeedTarget!=-1) {
|
||||
bool atTarget=false;
|
||||
|
|
@ -1645,7 +1692,11 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
}
|
||||
|
||||
if (atTarget) {
|
||||
chan[i].volume=chan[i].volSpeedTarget;
|
||||
if (chan[i].volSpeed>0) {
|
||||
chan[i].volume=MAX(preSpeedVol,chan[i].volSpeedTarget);
|
||||
} else if (chan[i].volSpeed<0) {
|
||||
chan[i].volume=MIN(preSpeedVol,chan[i].volSpeedTarget);
|
||||
}
|
||||
chan[i].volSpeed=0;
|
||||
chan[i].volSpeedTarget=-1;
|
||||
dispatchCmd(DivCommand(DIV_CMD_HINT_VOLUME,i,chan[i].volume>>8));
|
||||
|
|
|
|||
|
|
@ -285,6 +285,9 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
|
|||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
off=(offset+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
off=((offset*3)+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
off=offset*2;
|
||||
break;
|
||||
|
|
@ -348,6 +351,10 @@ int DivSample::getSampleOffset(int offset, int length, DivSampleDepth depth) {
|
|||
off=(offset+1)/2;
|
||||
len=(length+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
off=((offset*3)+1)/2;
|
||||
len=((length*3)+1)/2;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
off=offset*2;
|
||||
len=length*2;
|
||||
|
|
@ -409,6 +416,9 @@ int DivSample::getEndPosition(DivSampleDepth depth) {
|
|||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
off=lengthIMA;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
off=length12;
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
off=length16;
|
||||
break;
|
||||
|
|
@ -606,6 +616,12 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
|
|||
dataIMA=new unsigned char[lengthIMA];
|
||||
memset(dataIMA,0,lengthIMA);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT: // 12-bit PCM (MultiPCM)
|
||||
if (data12!=NULL) delete[] data12;
|
||||
length12=((count*3)+1)/2;
|
||||
data12=new unsigned char[length12];
|
||||
memset(data12,0,length12);
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_16BIT: // 16-bit
|
||||
if (data16!=NULL) delete[] data16;
|
||||
length16=count*2;
|
||||
|
|
@ -1293,6 +1309,14 @@ void DivSample::render(unsigned int formatMask) {
|
|||
case DIV_SAMPLE_DEPTH_IMA_ADPCM: // IMA ADPCM
|
||||
if (adpcm_decode_block(data16,dataIMA,lengthIMA,samples)==0) logE("oh crap!");
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_12BIT: // 12-bit PCM (MultiPCM)
|
||||
for (unsigned int i=0, j=0; i<samples; i+=2, j+=3) {
|
||||
data16[i+0]=(data12[j+0]<<8)|(data12[j+1]&0xf0);
|
||||
if (i+1<samples) {
|
||||
data16[i+1]=(data12[j+2]<<8)|((data12[j+1]<<4)&0xf0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
|
@ -1482,6 +1506,16 @@ void DivSample::render(unsigned int formatMask) {
|
|||
adpcm_free_context(codec);
|
||||
}
|
||||
}
|
||||
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_12BIT)) { // 12-bit PCM (MultiPCM)
|
||||
if (!initInternal(DIV_SAMPLE_DEPTH_12BIT,samples)) return;
|
||||
for (unsigned int i=0, j=0; i<samples; i+=2, j+=3) {
|
||||
data12[j+0]=data16[i+0]>>8;
|
||||
data12[j+1]=((data16[i+0]>>4)&0xf)|(i+1<samples?(data16[i+1]>>4)&0xf:0);
|
||||
if (i+1<samples) {
|
||||
data12[j+2]=data16[i+1]>>8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* DivSample::getCurBuf() {
|
||||
|
|
@ -1512,6 +1546,8 @@ void* DivSample::getCurBuf() {
|
|||
return dataC219;
|
||||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
return dataIMA;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
return data12;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
return data16;
|
||||
default:
|
||||
|
|
@ -1548,6 +1584,8 @@ unsigned int DivSample::getCurBufLen() {
|
|||
return lengthC219;
|
||||
case DIV_SAMPLE_DEPTH_IMA_ADPCM:
|
||||
return lengthIMA;
|
||||
case DIV_SAMPLE_DEPTH_12BIT:
|
||||
return length12;
|
||||
case DIV_SAMPLE_DEPTH_16BIT:
|
||||
return length16;
|
||||
default:
|
||||
|
|
@ -1662,4 +1700,5 @@ DivSample::~DivSample() {
|
|||
if (dataMuLaw) delete[] dataMuLaw;
|
||||
if (dataC219) delete[] dataC219;
|
||||
if (dataIMA) delete[] dataIMA;
|
||||
if (data12) delete[] data12;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ enum DivSampleDepth: unsigned char {
|
|||
DIV_SAMPLE_DEPTH_MULAW=11,
|
||||
DIV_SAMPLE_DEPTH_C219=12,
|
||||
DIV_SAMPLE_DEPTH_IMA_ADPCM=13,
|
||||
DIV_SAMPLE_DEPTH_12BIT=14,
|
||||
DIV_SAMPLE_DEPTH_16BIT=16,
|
||||
DIV_SAMPLE_DEPTH_MAX // boundary for sample depth
|
||||
};
|
||||
|
|
@ -118,6 +119,7 @@ struct DivSample {
|
|||
// - 11: 8-bit µ-law PCM
|
||||
// - 12: C219 "µ-law" PCM
|
||||
// - 13: IMA ADPCM
|
||||
// - 14: 12-bit PCM (MultiPCM)
|
||||
// - 16: 16-bit PCM
|
||||
DivSampleDepth depth;
|
||||
bool loop, brrEmphasis, brrNoFilter, dither;
|
||||
|
|
@ -144,8 +146,9 @@ struct DivSample {
|
|||
unsigned char* dataMuLaw; // 11
|
||||
unsigned char* dataC219; // 12
|
||||
unsigned char* dataIMA; // 13
|
||||
unsigned char* data12; // 14
|
||||
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219, lengthIMA;
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthK, lengthBRR, lengthVOX, lengthMuLaw, lengthC219, lengthIMA, length12;
|
||||
|
||||
unsigned int samples;
|
||||
|
||||
|
|
@ -356,6 +359,7 @@ struct DivSample {
|
|||
dataMuLaw(NULL),
|
||||
dataC219(NULL),
|
||||
dataIMA(NULL),
|
||||
data12(NULL),
|
||||
length8(0),
|
||||
length16(0),
|
||||
length1(0),
|
||||
|
|
@ -370,6 +374,7 @@ struct DivSample {
|
|||
lengthMuLaw(0),
|
||||
lengthC219(0),
|
||||
lengthIMA(0),
|
||||
length12(0),
|
||||
samples(0) {
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
for (int j=0; j<DIV_MAX_SAMPLE_TYPE; j++) {
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ enum DivSystem {
|
|||
DIV_SYSTEM_5E01,
|
||||
DIV_SYSTEM_BIFURCATOR,
|
||||
DIV_SYSTEM_SID2,
|
||||
DIV_SYSTEM_SUPERVISION,
|
||||
DIV_SYSTEM_UPD1771C,
|
||||
DIV_SYSTEM_SID3,
|
||||
|
||||
DIV_SYSTEM_MAX
|
||||
};
|
||||
|
|
|
|||
|
|
@ -602,6 +602,25 @@ void DivEngine::registerSystems() {
|
|||
{0x5b, {DIV_CMD_FM_KSR, _("5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)"), effectOpVal<4>, effectValAnd<1>}},
|
||||
};
|
||||
|
||||
EffectHandlerMap fmOPL4PostEffectHandlerMap(fmOPLPostEffectHandlerMap);
|
||||
fmOPL4PostEffectHandlerMap.insert({
|
||||
{0x1e, {DIV_CMD_MULTIPCM_MIX_FM, _("1Exy: FM global level (x: left, y: right; 0 to 7)"), effectVal}},
|
||||
{0x1f, {DIV_CMD_MULTIPCM_MIX_PCM, _("1Fxy: PCM global level (x: left, y: right; 0 to 7)"), effectVal}},
|
||||
{0x20, {DIV_CMD_MULTIPCM_LFO, _("20xx: PCM LFO Rate (0 to 7)"), effectValAnd<7>}},
|
||||
{0x21, {DIV_CMD_MULTIPCM_VIB, _("21xx: PCM LFO PM Depth (0 to 7)"), effectValAnd<7>}},
|
||||
{0x22, {DIV_CMD_MULTIPCM_AM, _("22xx: PCM LFO AM Depth (0 to 7)"), effectValAnd<7>}},
|
||||
{0x23, {DIV_CMD_MULTIPCM_AR, _("23xx: PCM Attack Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x24, {DIV_CMD_MULTIPCM_D1R, _("24xx: PCM Decay 1 Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x25, {DIV_CMD_MULTIPCM_DL, _("25xx: PCM Decay Level (0 to 15)"), effectValAnd<15>}},
|
||||
{0x26, {DIV_CMD_MULTIPCM_D2R, _("26xx: PCM Decay 2 Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x27, {DIV_CMD_MULTIPCM_RR, _("27xx: PCM Release Rate (0 to 15)"), effectValAnd<15>}},
|
||||
{0x28, {DIV_CMD_MULTIPCM_RC, _("28xx: PCM Rate Correction (0 to 15)"), effectValAnd<15>}},
|
||||
{0x2c, {DIV_CMD_MULTIPCM_DAMP, _("2Cxx: PCM Damp"), effectValAnd<1>}},
|
||||
{0x2d, {DIV_CMD_MULTIPCM_PSEUDO_REVERB, _("2Dxx: PCM Pseudo Reverb"), effectValAnd<1>}},
|
||||
{0x2e, {DIV_CMD_MULTIPCM_LFO_RESET, _("2Exx: PCM LFO Reset"), effectValAnd<1>}},
|
||||
{0x2f, {DIV_CMD_MULTIPCM_LEVEL_DIRECT, _("2Fxx: PCM Level Direct"), effectValAnd<1>}},
|
||||
});
|
||||
|
||||
EffectHandlerMap c64PostEffectHandlerMap={
|
||||
{0x10, {DIV_CMD_WAVE, _("10xx: Set waveform (bit 0: triangle; bit 1: saw; bit 2: pulse; bit 3: noise)")}},
|
||||
{0x11, {DIV_CMD_C64_CUTOFF, _("11xx: Set coarse cutoff (not recommended; use 4xxx instead)")}},
|
||||
|
|
@ -615,6 +634,12 @@ void DivEngine::registerSystems() {
|
|||
{0x1e, {DIV_CMD_C64_EXTENDED, _("1Exy: Change other parameters (LEGACY)")}},
|
||||
{0x20, {DIV_CMD_C64_AD, _("20xy: Set attack/decay (x: attack; y: decay)")}},
|
||||
{0x21, {DIV_CMD_C64_SR, _("21xy: Set sustain/release (x: sustain; y: release)")}},
|
||||
|
||||
{0x22, {DIV_CMD_C64_PW_SLIDE, _("22xx: Pulse width slide up"), effectVal, constVal<1>}},
|
||||
{0x23, {DIV_CMD_C64_PW_SLIDE, _("23xx: Pulse width slide down"), effectVal, constVal<-1>}},
|
||||
|
||||
{0x24, {DIV_CMD_C64_CUTOFF_SLIDE, _("24xx: Filter cutoff slide up"), effectVal, constVal<1>}},
|
||||
{0x25, {DIV_CMD_C64_CUTOFF_SLIDE, _("25xx: Filter cutoff slide down"), effectVal, constVal<-1>}},
|
||||
};
|
||||
const EffectHandler c64FineDutyHandler(DIV_CMD_C64_FINE_DUTY, _("3xxx: Set pulse width (0 to FFF)"), effectValLong<12>);
|
||||
const EffectHandler c64FineCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, _("4xxx: Set cutoff (0 to 7FF)"), effectValLong<11>);
|
||||
|
|
@ -687,12 +712,97 @@ void DivEngine::registerSystems() {
|
|||
{0x14, {DIV_CMD_C64_FILTER_RESET, _("14xy: Reset cutoff (x: on new note; y: now)")}},
|
||||
{0x15, {DIV_CMD_C64_DUTY_RESET, _("15xy: Reset pulse width (x: on new note; y: now)")}},
|
||||
{0x16, {DIV_CMD_C64_EXTENDED, _("16xy: Change other parameters")}},
|
||||
|
||||
{0x17, {DIV_CMD_C64_PW_SLIDE, _("17xx: Pulse width slide up"), effectVal, constVal<1>}},
|
||||
{0x18, {DIV_CMD_C64_PW_SLIDE, _("18xx: Pulse width slide down"), effectVal, constVal<-1>}},
|
||||
|
||||
{0x19, {DIV_CMD_C64_CUTOFF_SLIDE, _("19xx: Filter cutoff slide up"), effectVal, constVal<1>}},
|
||||
{0x1A, {DIV_CMD_C64_CUTOFF_SLIDE, _("1Axx: Filter cutoff slide down"), effectVal, constVal<-1>}},
|
||||
};
|
||||
const EffectHandler SID2FineDutyHandler(DIV_CMD_C64_FINE_DUTY, _("3xxx: Set pulse width (0 to FFF)"), effectValLong<12>);
|
||||
const EffectHandler SID2FineCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, _("4xxx: Set cutoff (0 to FFF)"), effectValLong<11>);
|
||||
for (int i=0; i<16; i++) SID2PostEffectHandlerMap.emplace(0x30+i,SID2FineDutyHandler);
|
||||
for (int i=0; i<16; i++) SID2PostEffectHandlerMap.emplace(0x40+i,SID2FineCutoffHandler);
|
||||
|
||||
EffectHandlerMap SID3PostEffectHandlerMap={
|
||||
{0x60, {DIV_CMD_WAVE, _("60xx: Set waveform (bit 0: triangle; bit 1: saw; bit 2: pulse; bit 3: noise; bit 4: special wave)")}},
|
||||
{0x61, {DIV_CMD_SID3_SPECIAL_WAVE, _("61xx: Set special wave (00-39)")}},
|
||||
{0x62, {DIV_CMD_C64_EXTENDED, _("62xx: Modulation control (bit 0: ring mod; bit 1: osc. sync; bit 2: phase mod)")}},
|
||||
{0x63, {DIV_CMD_C64_DUTY_RESET, _("63xy: Reset pulse width (x: on new note; y: now)")}},
|
||||
|
||||
{0x64, {DIV_CMD_SID3_RING_MOD_SRC, _("64xx: Set ring modulation source channel (0-7)")}},
|
||||
{0x65, {DIV_CMD_SID3_HARD_SYNC_SRC, _("65xx: Set hard sync source channel (0-6)")}},
|
||||
{0x66, {DIV_CMD_SID3_PHASE_MOD_SRC, _("66xx: Set phase modulation source channel (0-6)")}},
|
||||
|
||||
{0x67, {DIV_CMD_FM_AR, _("67xx: Set attack")}},
|
||||
{0x68, {DIV_CMD_FM_DR, _("68xx: Set decay")}},
|
||||
{0x69, {DIV_CMD_FM_SL, _("69xx: Set sustain level")}},
|
||||
{0x6A, {DIV_CMD_FM_D2R, _("6Axx: Set sustain rate")}},
|
||||
{0x6B, {DIV_CMD_FM_RR, _("6Bxx: Set release")}},
|
||||
|
||||
{0x6C, {DIV_CMD_SID3_WAVE_MIX, _("6Cxx: Set wave mix mode (0-4)")}},
|
||||
|
||||
{0x6D, {DIV_CMD_SID3_LFSR_FEEDBACK_BITS, _("6Dxx: Set noise LFSR feedback bits (low byte)"), effectVal, constVal<0>}},
|
||||
{0x6E, {DIV_CMD_SID3_LFSR_FEEDBACK_BITS, _("6Exx: Set noise LFSR feedback bits (medium byte)"), effectVal, constVal<1>}},
|
||||
{0x6F, {DIV_CMD_SID3_LFSR_FEEDBACK_BITS, _("6Fxx: Set noise LFSR feedback bits (higher byte)"), effectVal, constVal<2>}},
|
||||
{0x70, {DIV_CMD_SID3_LFSR_FEEDBACK_BITS, _("70xx: Set noise LFSR feedback bits (highest bits, 0-3F)"), effectVal, constVal<3>}},
|
||||
|
||||
{0x71, {DIV_CMD_C64_RESONANCE, _("71xx: Set filter 1 resonance"), effectVal, constVal<0>}},
|
||||
{0x72, {DIV_CMD_C64_RESONANCE, _("72xx: Set filter 2 resonance"), effectVal, constVal<1>}},
|
||||
{0x73, {DIV_CMD_C64_RESONANCE, _("73xx: Set filter 3 resonance"), effectVal, constVal<2>}},
|
||||
{0x74, {DIV_CMD_C64_RESONANCE, _("74xx: Set filter 4 resonance"), effectVal, constVal<3>}},
|
||||
|
||||
{0x75, {DIV_CMD_SID3_1_BIT_NOISE, _("75xx: Set noise mode (0: usual noise, 1: 1-bit noise (PCM mode on wave channel))")}},
|
||||
|
||||
{0x76, {DIV_CMD_SID3_FILTER_OUTPUT_VOLUME, _("76xx: Set filter 1 output volume"), effectVal, constVal<0>}},
|
||||
{0x77, {DIV_CMD_SID3_FILTER_OUTPUT_VOLUME, _("77xx: Set filter 2 output volume"), effectVal, constVal<1>}},
|
||||
{0x78, {DIV_CMD_SID3_FILTER_OUTPUT_VOLUME, _("78xx: Set filter 3 output volume"), effectVal, constVal<2>}},
|
||||
{0x79, {DIV_CMD_SID3_FILTER_OUTPUT_VOLUME, _("79xx: Set filter 4 output volume"), effectVal, constVal<3>}},
|
||||
|
||||
{0x7A, {DIV_CMD_SID3_FILTER_DISTORTION, _("7Axx: Set filter 1 distortion"), effectVal, constVal<0>}},
|
||||
{0x7B, {DIV_CMD_SID3_FILTER_DISTORTION, _("7Bxx: Set filter 2 distortion"), effectVal, constVal<1>}},
|
||||
{0x7C, {DIV_CMD_SID3_FILTER_DISTORTION, _("7Cxx: Set filter 3 distortion"), effectVal, constVal<2>}},
|
||||
{0x7D, {DIV_CMD_SID3_FILTER_DISTORTION, _("7Dxx: Set filter 4 distortion"), effectVal, constVal<3>}},
|
||||
|
||||
{0x7E, {DIV_CMD_FM_FB, _("7Exx: Set feedback")}},
|
||||
{0x7F, {DIV_CMD_SID3_CHANNEL_INVERSION, _("7Fxx: Set channel signal inversion (bit 0: right channel, bit 1: left channel)")}},
|
||||
|
||||
{0xA0, {DIV_CMD_C64_FILTER_MODE, _("A0xy: Set filter mode (x: filter (0-3); y: bit 0: low pass; bit 1: band pass; bit 2: high pass)")}},
|
||||
{0xA1, {DIV_CMD_SID3_FILTER_CONNECTION, _("A1xy: Set filter connection (x: filter (0-3); y: bit 0: connect to channel input; bit 1: connect to channel output)")}},
|
||||
{0xA2, {DIV_CMD_SID3_FILTER_MATRIX, _("A2xy: Set filter connection matrix (x: filter (0-3); y: bits 0-3: add filter 1-4 output to filter's input)")}},
|
||||
{0xA3, {DIV_CMD_SID3_FILTER_ENABLE, _("A3xy: Enable filter (x: filter (0-3); y: enable)")}},
|
||||
|
||||
{0xA4, {DIV_CMD_C64_PW_SLIDE, _("A4xx: Pulse width slide up"), effectVal, constVal<1>}},
|
||||
{0xA5, {DIV_CMD_C64_PW_SLIDE, _("A5xx: Pulse width slide down"), effectVal, constVal<-1>}},
|
||||
|
||||
{0xA6, {DIV_CMD_C64_CUTOFF_SLIDE, _("A6xx: Filter 1 cutoff slide up"), effectVal, constVal<1>}},
|
||||
{0xA7, {DIV_CMD_C64_CUTOFF_SLIDE, _("A7xx: Filter 1 cutoff slide down"), effectVal, constVal<-1>}},
|
||||
{0xA8, {DIV_CMD_C64_CUTOFF_SLIDE, _("A8xx: Filter 2 cutoff slide up"), effectVal, constVal<2>}},
|
||||
{0xA9, {DIV_CMD_C64_CUTOFF_SLIDE, _("A9xx: Filter 2 cutoff slide down"), effectVal, constVal<-2>}},
|
||||
{0xAA, {DIV_CMD_C64_CUTOFF_SLIDE, _("AAxx: Filter 3 cutoff slide up"), effectVal, constVal<3>}},
|
||||
{0xAB, {DIV_CMD_C64_CUTOFF_SLIDE, _("ABxx: Filter 3 cutoff slide down"), effectVal, constVal<-3>}},
|
||||
{0xAC, {DIV_CMD_C64_CUTOFF_SLIDE, _("ACxx: Filter 4 cutoff slide up"), effectVal, constVal<4>}},
|
||||
{0xAD, {DIV_CMD_C64_CUTOFF_SLIDE, _("ADxx: Filter 4 cutoff slide down"), effectVal, constVal<-4>}},
|
||||
|
||||
{0xAE, {DIV_CMD_SID3_PHASE_RESET, _("AExx: Phase reset on tick xx")}},
|
||||
{0xAF, {DIV_CMD_SID3_NOISE_PHASE_RESET, _("AFxx: Noise phase reset on tick xx")}},
|
||||
{0xB0, {DIV_CMD_SID3_ENVELOPE_RESET, _("B0xx: Envelope reset on tick xx")}},
|
||||
|
||||
{0xB1, {DIV_CMD_SID3_CUTOFF_SCALING, _("B1xy: Cutoff scaling control (x: filter (0-3); y: bit 0: enable scaling; bit 1: invert scaling)")}},
|
||||
{0xB2, {DIV_CMD_SID3_RESONANCE_SCALING, _("B2xy: Resonance scaling control (x: filter (0-3); y: bit 0: enable scaling; bit 1: invert scaling)")}},
|
||||
};
|
||||
|
||||
const EffectHandler SID3FineDutyHandler(DIV_CMD_C64_FINE_DUTY, _("5xxx: Set pulse width (0 to FFF)"), effectValLong<12>);
|
||||
const EffectHandler SID3FineCutoffHandler1(DIV_CMD_C64_FINE_CUTOFF, _("1xxx: Set cutoff of filter 1 (0 to FFF)"), effectValLong<12>, constVal<0>);
|
||||
const EffectHandler SID3FineCutoffHandler2(DIV_CMD_C64_FINE_CUTOFF, _("2xxx: Set cutoff of filter 2 (0 to FFF)"), effectValLong<12>, constVal<1>);
|
||||
const EffectHandler SID3FineCutoffHandler3(DIV_CMD_C64_FINE_CUTOFF, _("3xxx: Set cutoff of filter 3 (0 to FFF)"), effectValLong<12>, constVal<2>);
|
||||
const EffectHandler SID3FineCutoffHandler4(DIV_CMD_C64_FINE_CUTOFF, _("4xxx: Set cutoff of filter 4 (0 to FFF)"), effectValLong<12>, constVal<3>);
|
||||
for (int i=0; i<16; i++) SID3PostEffectHandlerMap.emplace(0x50+i,SID3FineDutyHandler);
|
||||
for (int i=0; i<16; i++) SID3PostEffectHandlerMap.emplace(0x10+i,SID3FineCutoffHandler1);
|
||||
for (int i=0; i<16; i++) SID3PostEffectHandlerMap.emplace(0x20+i,SID3FineCutoffHandler2);
|
||||
for (int i=0; i<16; i++) SID3PostEffectHandlerMap.emplace(0x30+i,SID3FineCutoffHandler3);
|
||||
for (int i=0; i<16; i++) SID3PostEffectHandlerMap.emplace(0x40+i,SID3FineCutoffHandler4);
|
||||
|
||||
// SysDefs
|
||||
|
||||
// this chip uses YMZ ADPCM, but the emulator uses ADPCM-B because I got it wrong back then.
|
||||
|
|
@ -1481,7 +1591,7 @@ void DivEngine::registerSystems() {
|
|||
|
||||
sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef(
|
||||
_("Yamaha YM2413 (OPLL) with drums"), NULL, 0xa7, 0, 11, true, false, 0x150, false, 0, 0, 0,
|
||||
_("the OPLL chips but with drums mode turned on."),
|
||||
_("the OPLL chip but with drums mode turned on."),
|
||||
{_("FM 1"), _("FM 2"), _("FM 3"), _("FM 4"), _("FM 5"), _("FM 6"), _("Kick"), _("Snare"), _("Tom"), _("Top"), _("HiHat")},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "BD", "SD", "TM", "TP", "HH"},
|
||||
{DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE},
|
||||
|
|
@ -1618,24 +1728,28 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
// to Grauw: feel free to change this to 24 during development of OPL4's PCM part.
|
||||
// TODO: add 12-bit and 16-bit big-endian sample formats
|
||||
sysDefs[DIV_SYSTEM_OPL4]=new DivSysDef(
|
||||
_("Yamaha YMF278B (OPL4)"), NULL, 0xae, 0, 42, true, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("Yamaha YMF278B (OPL4)"), NULL, 0xae, 0, 42, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_12BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("like OPL3, but this time it also has a 24-channel version of MultiPCM."),
|
||||
{_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("FM 16"), _("FM 17"), _("FM 18"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "F16", "F17", "F18", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM}
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
|
||||
fmEffectHandlerMap,
|
||||
fmOPL4PostEffectHandlerMap
|
||||
);
|
||||
|
||||
// TODO: same here
|
||||
sysDefs[DIV_SYSTEM_OPL4_DRUMS]=new DivSysDef(
|
||||
_("Yamaha YMF278B (OPL4) with drums"), NULL, 0xaf, 0, 44, true, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("Yamaha YMF278B (OPL4) with drums"), NULL, 0xaf, 0, 44, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_12BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 0, 0,
|
||||
_("the OPL4 but with drums mode turned on."),
|
||||
{_("4OP 1"), _("FM 2"), _("4OP 3"), _("FM 4"), _("4OP 5"), _("FM 6"), _("4OP 7"), _("FM 8"), _("4OP 9"), _("FM 10"), _("4OP 11"), _("FM 12"), _("FM 13"), _("FM 14"), _("FM 15"), _("Kick/FM 16"), _("Snare"), _("Tom"), _("Top"), _("HiHat"), _("PCM 1"), _("PCM 2"), _("PCM 3"), _("PCM 4"), _("PCM 5"), _("PCM 6"), _("PCM 7"), _("PCM 8"), _("PCM 9"), _("PCM 10"), _("PCM 11"), _("PCM 12"), _("PCM 13"), _("PCM 14"), _("PCM 15"), _("PCM 16"), _("PCM 17"), _("PCM 18"), _("PCM 19"), _("PCM 20"), _("PCM 21"), _("PCM 22"), _("PCM 23"), _("PCM 24")},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P8", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "F13", "F14", "F15", "BD", "SD", "TM", "TP", "HH", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "P21", "P22", "P23", "P24"},
|
||||
{DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM}
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM, DIV_INS_MULTIPCM},
|
||||
{DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_OPL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
|
||||
fmOPLDrumsEffectHandlerMap,
|
||||
fmOPL4PostEffectHandlerMap
|
||||
);
|
||||
|
||||
EffectHandlerMap es5506PreEffectHandlerMap={
|
||||
|
|
@ -1733,6 +1847,12 @@ void DivEngine::registerSystems() {
|
|||
{0x20, {DIV_CMD_SU_SWEEP_ENABLE, _("20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)"), constVal<0>, effectVal}},
|
||||
{0x21, {DIV_CMD_SU_SWEEP_ENABLE, _("21xx: Toggle volume sweep (bit 0-4: speed; bit 5: direction is up; bit 6: loop; bit 7: alternate)"), constVal<1>, effectVal}},
|
||||
{0x22, {DIV_CMD_SU_SWEEP_ENABLE, _("22xx: Toggle cutoff sweep (bit 0-6: speed; bit 7: direction is up)"), constVal<2>, effectVal}},
|
||||
|
||||
{0x23, {DIV_CMD_C64_PW_SLIDE, _("23xx: Pulse width slide up"), effectVal, constVal<1>}},
|
||||
{0x24, {DIV_CMD_C64_PW_SLIDE, _("24xx: Pulse width slide down"), effectVal, constVal<-1>}},
|
||||
|
||||
{0x25, {DIV_CMD_C64_CUTOFF_SLIDE, _("25xx: Filter cutoff slide up"), effectVal, constVal<1>}},
|
||||
{0x26, {DIV_CMD_C64_CUTOFF_SLIDE, _("26xx: Filter cutoff slide down"), effectVal, constVal<-1>}},
|
||||
};
|
||||
const EffectHandler suCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, _("4xxx: Set cutoff (0 to FFF)"), effectValLong<12>);
|
||||
for (int i=0; i<16; i++) {
|
||||
|
|
@ -1918,6 +2038,33 @@ void DivEngine::registerSystems() {
|
|||
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SUPERVISION]=new DivSysDef(
|
||||
_("Watara Supervision"), NULL, 0xe3, 0, 4, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT, 0, 0,
|
||||
_("a handheld that was trying to compete with the Game Boy, but it never succeded."),
|
||||
{_("Pulse 1"), _("Pulse 2"), _("PCM"), _("Noise")},
|
||||
{"S1", "S2", "PCM", "NO"},
|
||||
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_NOISE},
|
||||
{DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_SUPERVISION},
|
||||
{DIV_INS_SUPERVISION, DIV_INS_SUPERVISION, DIV_INS_AMIGA, DIV_INS_SUPERVISION},
|
||||
{
|
||||
{0x12, {DIV_CMD_STD_NOISE_MODE, _("12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)")}},
|
||||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_UPD1771C]=new DivSysDef(
|
||||
_("NEC μPD1771C"), NULL, 0xe4, 0, 1, false, true, 0, false, 0, 0, 0,
|
||||
_("this was an SoC with some funky wavetable/noise hardware"),
|
||||
{_("Wave/Noise")},
|
||||
{"W"},
|
||||
{DIV_CH_NOISE},
|
||||
{DIV_INS_UPD1771C},
|
||||
{},
|
||||
{
|
||||
{0x10, {DIV_CMD_STD_NOISE_MODE, _("10xx: Set duty/waveform (bit 0-3: waveform; bit 4: mode)")}},
|
||||
{0x12, {DIV_CMD_N163_WAVE_POSITION, _("12xx: Set waveform position (0-31)")}},
|
||||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SM8521]=new DivSysDef(
|
||||
_("Sharp SM8521"), NULL, 0xc8, 0, 3, false, true, 0, false, 0, 32, 16,
|
||||
_("a SoC with wavetable sound hardware."),
|
||||
|
|
@ -2148,6 +2295,18 @@ void DivEngine::registerSystems() {
|
|||
SID2PostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SID3]=new DivSysDef(
|
||||
_("SID3"), NULL, 0xf5, 0, 7, false, true, 0, false, (1U<<DIV_SAMPLE_DEPTH_8BIT)|(1U<<DIV_SAMPLE_DEPTH_16BIT), 256, 256,
|
||||
_("a fantasy sound chip created by LTVA. it is a big rework of SID chip with probably too many features added on top."),
|
||||
{_("Channel 1"), _("Channel 2"), _("Channel 3"), _("Channel 4"), _("Channel 5"), _("Channel 6"), _("Wave")},
|
||||
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "WA"},
|
||||
{DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_WAVE},
|
||||
{DIV_INS_SID3, DIV_INS_SID3, DIV_INS_SID3, DIV_INS_SID3, DIV_INS_SID3, DIV_INS_SID3, DIV_INS_SID3},
|
||||
{},
|
||||
{},
|
||||
SID3PostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
|
||||
_("Dummy System"), NULL, 0xfd, 0, 8, false, true, 0, false, 0, 0, 0,
|
||||
_("this is a system designed for testing purposes."),
|
||||
|
|
|
|||
|
|
@ -617,6 +617,96 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
// disable envelope
|
||||
for (int i=0; i<6; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0x80+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0x88+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0x90+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x80+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x88+i);
|
||||
w->writeC(0x0f);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x90+i);
|
||||
w->writeC(0x0f);
|
||||
}
|
||||
for (int i=0; i<24; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x80+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x98+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0xb0+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0xc8+i);
|
||||
w->writeC(0x00);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0xe0+i);
|
||||
w->writeC(0x00);
|
||||
}
|
||||
// key off + freq reset
|
||||
for (int i=0; i<9; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0xa0+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x00|baseAddr2);
|
||||
w->writeC(0xb0+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0xa0+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0xb0+i);
|
||||
w->writeC(0);
|
||||
}
|
||||
for (int i=0; i<24; i++) {
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x20+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x38+i);
|
||||
w->writeC(0);
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x02|baseAddr2);
|
||||
w->writeC(0x68+i);
|
||||
w->writeC(8);
|
||||
}
|
||||
// reset 4-op
|
||||
w->writeC(0xd0);
|
||||
w->writeC(0x01|baseAddr2);
|
||||
w->writeC(0x04);
|
||||
w->writeC(0x00);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1096,6 +1186,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeS_BE(baseAddr2S|(write.addr&0x1ff));
|
||||
w->writeC(write.val&0xff);
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
w->writeC(0xd0);
|
||||
w->writeC(((write.addr>>8)&0x7f)|baseAddr2);
|
||||
w->writeC(write.addr&0xff);
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
default:
|
||||
logW("write not handled!");
|
||||
break;
|
||||
|
|
@ -1276,6 +1373,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
DivDispatch* writeC140[2]={NULL,NULL};
|
||||
DivDispatch* writeC219[2]={NULL,NULL};
|
||||
DivDispatch* writeNES[2]={NULL,NULL};
|
||||
DivDispatch* writePCM_OPL4[2]={NULL,NULL};
|
||||
|
||||
int writeNESIndex[2]={0,0};
|
||||
|
||||
|
|
@ -1874,6 +1972,22 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPL4:
|
||||
case DIV_SYSTEM_OPL4_DRUMS:
|
||||
if (!hasOPL4) {
|
||||
hasOPL4=disCont[i].dispatch->chipClock;
|
||||
CHIP_VOL(13,1.0);
|
||||
willExport[i]=true;
|
||||
writePCM_OPL4[0]=disCont[i].dispatch;
|
||||
} else if (!(hasOPL4&0x40000000)) {
|
||||
isSecond[i]=true;
|
||||
CHIP_VOL_SECOND(13,1.0);
|
||||
willExport[i]=true;
|
||||
writePCM_OPL4[1]=disCont[i].dispatch;
|
||||
hasOPL4|=0x40000000;
|
||||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -2209,6 +2323,16 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
w->write(sampleMem,sampleMemLen);
|
||||
delete[] sampleMem;
|
||||
}
|
||||
// PCM (OPL4)
|
||||
if (writePCM_OPL4[i]!=NULL && writePCM_OPL4[i]->getSampleMemUsage(0)>0) {
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
w->writeC(0x84);
|
||||
w->writeI((writePCM_OPL4[i]->getSampleMemUsage(0)+8)|(i*0x80000000));
|
||||
w->writeI(writePCM_OPL4[i]->getSampleMemCapacity(0));
|
||||
w->writeI(0);
|
||||
w->write(writePCM_OPL4[i]->getSampleMem(0),writePCM_OPL4[i]->getSampleMemUsage(0));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue