Merge branch 'master' into ymf289b
This commit is contained in:
commit
b58fe36356
228 changed files with 8248 additions and 3664 deletions
|
|
@ -237,56 +237,60 @@ bool DivConfig::loadFromBase64(const char* buf) {
|
|||
}
|
||||
|
||||
bool DivConfig::getBool(String key, bool fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
if (val=="true") {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
if (val->second=="true") {
|
||||
return true;
|
||||
} else if (val=="false") {
|
||||
} else if (val->second=="false") {
|
||||
return false;
|
||||
}
|
||||
} catch (std::out_of_range& e) {
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
int DivConfig::getInt(String key, int fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
int ret=std::stoi(val);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
int ret=std::stoi(val->second);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
float DivConfig::getFloat(String key, float fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
float ret=std::stof(val);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
float ret=std::stof(val->second);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
double DivConfig::getDouble(String key, double fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
double ret=std::stod(val);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
double ret=std::stod(val->second);
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
String DivConfig::getString(String key, String fallback) const {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
return val;
|
||||
} catch (std::out_of_range& e) {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
return val->second;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
|
@ -294,37 +298,34 @@ String DivConfig::getString(String key, String fallback) const {
|
|||
std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fallback) const {
|
||||
String next;
|
||||
std::vector<int> ret;
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
|
||||
for (char i: val) {
|
||||
if (i==',') {
|
||||
auto val=conf.find(key);
|
||||
if (val!=conf.cend()) {
|
||||
try {
|
||||
for (char i: val->second) {
|
||||
if (i==',') {
|
||||
int num=std::stoi(next);
|
||||
ret.push_back(num);
|
||||
next="";
|
||||
} else {
|
||||
next+=i;
|
||||
}
|
||||
}
|
||||
if (!next.empty()) {
|
||||
int num=std::stoi(next);
|
||||
ret.push_back(num);
|
||||
next="";
|
||||
} else {
|
||||
next+=i;
|
||||
}
|
||||
}
|
||||
if (!next.empty()) {
|
||||
int num=std::stoi(next);
|
||||
ret.push_back(num);
|
||||
}
|
||||
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
return ret;
|
||||
} catch (std::out_of_range& e) {
|
||||
} catch (std::invalid_argument& e) {
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
bool DivConfig::has(String key) const {
|
||||
try {
|
||||
String test=conf.at(key);
|
||||
} catch (std::out_of_range& e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
auto val=conf.find(key);
|
||||
return (val!=conf.cend());
|
||||
}
|
||||
|
||||
void DivConfig::set(String key, bool value) {
|
||||
|
|
|
|||
|
|
@ -485,6 +485,12 @@ class DivDispatch {
|
|||
*/
|
||||
virtual bool keyOffAffectsPorta(int ch);
|
||||
|
||||
/**
|
||||
* test whether volume is global.
|
||||
* @return whether it is.
|
||||
*/
|
||||
virtual bool isVolGlobal();
|
||||
|
||||
/**
|
||||
* get the lowest note in a portamento.
|
||||
* @param ch the channel in question.
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@
|
|||
#include "platform/sm8521.h"
|
||||
#include "platform/pv1000.h"
|
||||
#include "platform/k053260.h"
|
||||
#include "platform/ted.h"
|
||||
#include "platform/pcmdac.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
|
|
@ -215,13 +216,6 @@ void DivDispatchContainer::clear() {
|
|||
if (dispatch->getDCOffRequired()) {
|
||||
dcOffCompensation=true;
|
||||
}
|
||||
// run for one cycle to determine DC offset
|
||||
// TODO: SAA1099 doesn't like that
|
||||
/*dispatch->acquire(bbIn[0],bbIn[1],0,1);
|
||||
temp[0]=bbIn[0][0];
|
||||
temp[1]=bbIn[1][0];
|
||||
prevSample[0]=temp[0];
|
||||
prevSample[1]=temp[1];*/
|
||||
}
|
||||
|
||||
void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, double gotRate, const DivConfig& flags) {
|
||||
|
|
@ -478,7 +472,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
break;
|
||||
case DIV_SYSTEM_NAMCO:
|
||||
dispatch=new DivPlatformNamcoWSG;
|
||||
// Pac-Man (TODO: support Pole Position?)
|
||||
// Pac-Man
|
||||
((DivPlatformNamcoWSG*)dispatch)->setDeviceType(1);
|
||||
break;
|
||||
case DIV_SYSTEM_NAMCO_15XX:
|
||||
|
|
@ -507,6 +501,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_K053260:
|
||||
dispatch=new DivPlatformK053260;
|
||||
break;
|
||||
case DIV_SYSTEM_TED:
|
||||
dispatch=new DivPlatformTED;
|
||||
break;
|
||||
case DIV_SYSTEM_PCM_DAC:
|
||||
dispatch=new DivPlatformPCMDAC;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
|
|||
case 0xea:
|
||||
return "EAxx: Legato";
|
||||
case 0xeb:
|
||||
return "EBxx: Set sample bank";
|
||||
return "EBxx: Set LEGACY sample mode bank";
|
||||
case 0xec:
|
||||
return "ECxx: Note cut";
|
||||
case 0xed:
|
||||
|
|
@ -927,12 +927,13 @@ void DivEngine::runExportThread() {
|
|||
}
|
||||
}
|
||||
|
||||
float* outBuf[2];
|
||||
float* outBuf[DIV_MAX_OUTPUTS];
|
||||
memset(outBuf,0,sizeof(void*)*DIV_MAX_OUTPUTS);
|
||||
outBuf[0]=new float[EXPORT_BUFSIZE];
|
||||
outBuf[1]=new float[EXPORT_BUFSIZE];
|
||||
short* sysBuf[DIV_MAX_CHIPS];
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
|
||||
sysBuf[i]=new short[EXPORT_BUFSIZE*disCont[i].dispatch->getOutputCount()];
|
||||
}
|
||||
|
||||
// take control of audio output
|
||||
|
|
@ -3112,6 +3113,8 @@ int DivEngine::addInstrumentPtr(DivInstrument* which) {
|
|||
song.ins.push_back(which);
|
||||
song.insLen=song.ins.size();
|
||||
checkAssetDir(song.insDir,song.ins.size());
|
||||
checkAssetDir(song.waveDir,song.wave.size());
|
||||
checkAssetDir(song.sampleDir,song.sample.size());
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return song.insLen;
|
||||
|
|
@ -3850,6 +3853,23 @@ void DivEngine::delSample(int index) {
|
|||
song.sampleLen=song.sample.size();
|
||||
removeAsset(song.sampleDir,index);
|
||||
checkAssetDir(song.sampleDir,song.sample.size());
|
||||
|
||||
// compensate
|
||||
for (DivInstrument* i: song.ins) {
|
||||
if (i->amiga.initSample==index) {
|
||||
i->amiga.initSample=-1;
|
||||
} else if (i->amiga.initSample>index) {
|
||||
i->amiga.initSample--;
|
||||
}
|
||||
for (int j=0; j<120; j++) {
|
||||
if (i->amiga.noteMap[j].map==index) {
|
||||
i->amiga.noteMap[j].map=-1;
|
||||
} else if (i->amiga.noteMap[j].map>index) {
|
||||
i->amiga.noteMap[j].map--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderSamples();
|
||||
}
|
||||
saveLock.unlock();
|
||||
|
|
@ -4041,6 +4061,27 @@ void DivEngine::exchangeIns(int one, int two) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivEngine::exchangeWave(int one, int two) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
void DivEngine::exchangeSample(int one, int two) {
|
||||
for (DivInstrument* i: song.ins) {
|
||||
if (i->amiga.initSample==one) {
|
||||
i->amiga.initSample=two;
|
||||
} else if (i->amiga.initSample==two) {
|
||||
i->amiga.initSample=one;
|
||||
}
|
||||
for (int j=0; j<120; j++) {
|
||||
if (i->amiga.noteMap[j].map==one) {
|
||||
i->amiga.noteMap[j].map=two;
|
||||
} else if (i->amiga.noteMap[j].map==two) {
|
||||
i->amiga.noteMap[j].map=one;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DivEngine::moveInsUp(int which) {
|
||||
if (which<1 || which>=(int)song.ins.size()) return false;
|
||||
BUSY_BEGIN;
|
||||
|
|
@ -4063,6 +4104,7 @@ bool DivEngine::moveWaveUp(int which) {
|
|||
song.wave[which]=song.wave[which-1];
|
||||
song.wave[which-1]=prev;
|
||||
moveAsset(song.waveDir,which,which-1);
|
||||
exchangeWave(which,which-1);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
return true;
|
||||
|
|
@ -4079,6 +4121,7 @@ bool DivEngine::moveSampleUp(int which) {
|
|||
song.sample[which]=song.sample[which-1];
|
||||
song.sample[which-1]=prev;
|
||||
moveAsset(song.sampleDir,which,which-1);
|
||||
exchangeSample(which,which-1);
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
|
@ -4106,6 +4149,7 @@ bool DivEngine::moveWaveDown(int which) {
|
|||
saveLock.lock();
|
||||
song.wave[which]=song.wave[which+1];
|
||||
song.wave[which+1]=prev;
|
||||
exchangeWave(which,which+1);
|
||||
moveAsset(song.waveDir,which,which+1);
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -4122,6 +4166,7 @@ bool DivEngine::moveSampleDown(int which) {
|
|||
saveLock.lock();
|
||||
song.sample[which]=song.sample[which+1];
|
||||
song.sample[which+1]=prev;
|
||||
exchangeSample(which,which+1);
|
||||
moveAsset(song.sampleDir,which,which+1);
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@
|
|||
|
||||
#define DIV_UNSTABLE
|
||||
|
||||
#define DIV_VERSION "dev164"
|
||||
#define DIV_ENGINE_VERSION 164
|
||||
#define DIV_VERSION "dev165"
|
||||
#define DIV_ENGINE_VERSION 165
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -531,6 +531,9 @@ class DivEngine {
|
|||
void initSongWithDesc(const char* description, bool inBase64=true, bool oldVol=false);
|
||||
|
||||
void exchangeIns(int one, int two);
|
||||
void exchangeWave(int one, int two);
|
||||
void exchangeSample(int one, int two);
|
||||
|
||||
void swapChannels(int src, int dest);
|
||||
void stompChannel(int ch);
|
||||
|
||||
|
|
@ -602,7 +605,7 @@ class DivEngine {
|
|||
// - -2 to add a whole loop of trailing
|
||||
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171, bool patternHints=false, bool directStream=false, int trailingTicks=-1);
|
||||
// dump to ZSM.
|
||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
|
||||
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true, bool optimize=true);
|
||||
// dump command stream.
|
||||
SafeWriter* saveCommand(bool binary=false);
|
||||
// export to an audio file
|
||||
|
|
|
|||
|
|
@ -142,76 +142,78 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
// compatibility flags
|
||||
ds.limitSlides=true;
|
||||
ds.linearPitch=1;
|
||||
ds.loopModality=0;
|
||||
ds.properNoiseLayout=false;
|
||||
ds.waveDutyIsVol=false;
|
||||
// TODO: WHAT?! geodude.dmf fails when this is true
|
||||
// but isn't that how Defle behaves???
|
||||
ds.resetMacroOnPorta=false;
|
||||
ds.legacyVolumeSlides=true;
|
||||
ds.compatibleArpeggio=true;
|
||||
ds.noteOffResetsSlides=true;
|
||||
ds.targetResetsSlides=true;
|
||||
ds.arpNonPorta=false;
|
||||
ds.algMacroBehavior=false;
|
||||
ds.brokenShortcutSlides=false;
|
||||
ds.ignoreDuplicateSlides=true;
|
||||
ds.brokenDACMode=true;
|
||||
ds.oneTickCut=false;
|
||||
ds.newInsTriggersInPorta=true;
|
||||
ds.arp0Reset=true;
|
||||
ds.brokenSpeedSel=true;
|
||||
ds.noSlidesOnFirstTick=false;
|
||||
ds.rowResetsArpPos=false;
|
||||
ds.ignoreJumpAtEnd=true;
|
||||
ds.buggyPortaAfterSlide=true;
|
||||
ds.gbInsAffectsEnvelope=true;
|
||||
ds.ignoreDACModeOutsideIntendedChannel=false;
|
||||
ds.e1e2AlsoTakePriority=true;
|
||||
ds.fbPortaPause=true;
|
||||
ds.snDutyReset=true;
|
||||
ds.oldOctaveBoundary=false;
|
||||
ds.noOPN2Vol=true;
|
||||
ds.newVolumeScaling=false;
|
||||
ds.volMacroLinger=false;
|
||||
ds.brokenOutVol=true;
|
||||
ds.brokenOutVol2=true;
|
||||
ds.e1e2StopOnSameNote=true;
|
||||
ds.brokenPortaArp=false;
|
||||
ds.snNoLowPeriods=true;
|
||||
ds.disableSampleMacro=true;
|
||||
ds.delayBehavior=0;
|
||||
ds.jumpTreatment=2;
|
||||
if (!getConfInt("noDMFCompat",0)) {
|
||||
ds.limitSlides=true;
|
||||
ds.linearPitch=1;
|
||||
ds.loopModality=0;
|
||||
ds.properNoiseLayout=false;
|
||||
ds.waveDutyIsVol=false;
|
||||
// TODO: WHAT?! geodude.dmf fails when this is true
|
||||
// but isn't that how Defle behaves???
|
||||
ds.resetMacroOnPorta=false;
|
||||
ds.legacyVolumeSlides=true;
|
||||
ds.compatibleArpeggio=true;
|
||||
ds.noteOffResetsSlides=true;
|
||||
ds.targetResetsSlides=true;
|
||||
ds.arpNonPorta=false;
|
||||
ds.algMacroBehavior=false;
|
||||
ds.brokenShortcutSlides=false;
|
||||
ds.ignoreDuplicateSlides=true;
|
||||
ds.brokenDACMode=true;
|
||||
ds.oneTickCut=false;
|
||||
ds.newInsTriggersInPorta=true;
|
||||
ds.arp0Reset=true;
|
||||
ds.brokenSpeedSel=true;
|
||||
ds.noSlidesOnFirstTick=false;
|
||||
ds.rowResetsArpPos=false;
|
||||
ds.ignoreJumpAtEnd=true;
|
||||
ds.buggyPortaAfterSlide=true;
|
||||
ds.gbInsAffectsEnvelope=true;
|
||||
ds.ignoreDACModeOutsideIntendedChannel=false;
|
||||
ds.e1e2AlsoTakePriority=true;
|
||||
ds.fbPortaPause=true;
|
||||
ds.snDutyReset=true;
|
||||
ds.oldOctaveBoundary=false;
|
||||
ds.noOPN2Vol=true;
|
||||
ds.newVolumeScaling=false;
|
||||
ds.volMacroLinger=false;
|
||||
ds.brokenOutVol=true;
|
||||
ds.brokenOutVol2=true;
|
||||
ds.e1e2StopOnSameNote=true;
|
||||
ds.brokenPortaArp=false;
|
||||
ds.snNoLowPeriods=true;
|
||||
ds.disableSampleMacro=true;
|
||||
ds.delayBehavior=0;
|
||||
ds.jumpTreatment=2;
|
||||
|
||||
// 1.1 compat flags
|
||||
if (ds.version>24) {
|
||||
ds.waveDutyIsVol=true;
|
||||
ds.legacyVolumeSlides=false;
|
||||
}
|
||||
// 1.1 compat flags
|
||||
if (ds.version>24) {
|
||||
ds.waveDutyIsVol=true;
|
||||
ds.legacyVolumeSlides=false;
|
||||
}
|
||||
|
||||
// Neo Geo detune is caused by Defle running Neo Geo at the wrong clock.
|
||||
/*
|
||||
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
|
||||
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|
||||
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
|
||||
ds.tuning=443.23;
|
||||
}
|
||||
*/
|
||||
// Neo Geo detune is caused by Defle running Neo Geo at the wrong clock.
|
||||
/*
|
||||
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
|
||||
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|
||||
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
|
||||
ds.tuning=443.23;
|
||||
}
|
||||
*/
|
||||
|
||||
// Genesis detuned on Defle v10 and earlier
|
||||
/*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) {
|
||||
ds.tuning=443.23;
|
||||
}*/
|
||||
// C64 detuned on Defle v11 and earlier
|
||||
/*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) {
|
||||
ds.tuning=433.2;
|
||||
}*/
|
||||
// Genesis detuned on Defle v10 and earlier
|
||||
/*if (ds.version<19 && ds.system[0]==DIV_SYSTEM_GENESIS) {
|
||||
ds.tuning=443.23;
|
||||
}*/
|
||||
// C64 detuned on Defle v11 and earlier
|
||||
/*if (ds.version<21 && (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580)) {
|
||||
ds.tuning=433.2;
|
||||
}*/
|
||||
|
||||
// Game Boy arp+soundLen screwery
|
||||
if (ds.system[0]==DIV_SYSTEM_GB) {
|
||||
ds.systemFlags[0].set("enoughAlready",true);
|
||||
// Game Boy arp+soundLen screwery
|
||||
if (ds.system[0]==DIV_SYSTEM_GB) {
|
||||
ds.systemFlags[0].set("enoughAlready",true);
|
||||
}
|
||||
}
|
||||
|
||||
logI("reading module data...");
|
||||
|
|
@ -869,7 +871,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
if (ds.version>0x15) {
|
||||
sample->depth=(DivSampleDepth)reader.readC();
|
||||
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && sample->depth!=DIV_SAMPLE_DEPTH_16BIT) {
|
||||
logW("%d: sample depth is wrong! (%d)",i,sample->depth);
|
||||
logW("%d: sample depth is wrong! (%d)",i,(int)sample->depth);
|
||||
sample->depth=DIV_SAMPLE_DEPTH_16BIT;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1937,8 +1939,8 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
ds.system[i]=systemFromFileFur(sysID);
|
||||
logD("- %d: %.2x (%s)",i,sysID,getSystemName(ds.system[i]));
|
||||
if (sysID!=0 && systemToFileFur(ds.system[i])==0) {
|
||||
logE("unrecognized system ID %.2x",ds.system[i]);
|
||||
lastError=fmt::sprintf("unrecognized system ID %.2x!",ds.system[i]);
|
||||
logE("unrecognized system ID %.2x",sysID);
|
||||
lastError=fmt::sprintf("unrecognized system ID %.2x!",sysID);
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -2344,7 +2346,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
if (ds.version>=136) song.patchbayAuto=reader.readC();
|
||||
if (ds.version>=136) ds.patchbayAuto=reader.readC();
|
||||
|
||||
if (ds.version>=138) {
|
||||
ds.brokenPortaLegato=reader.readC();
|
||||
|
|
@ -2941,6 +2943,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// Namco 163 pitch compensation compat
|
||||
if (ds.version<165) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_N163) {
|
||||
ds.systemFlags[i].set("lenCompensate",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
|
|||
|
|
@ -961,6 +961,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
|
|||
featureSM=true;
|
||||
featureSL=true;
|
||||
break;
|
||||
case DIV_INS_TED:
|
||||
break;
|
||||
|
||||
case DIV_INS_MAX:
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ enum DivInstrumentType: unsigned short {
|
|||
DIV_INS_SM8521=48,
|
||||
DIV_INS_PV1000=49,
|
||||
DIV_INS_K053260=50,
|
||||
// DIV_INS_YMF292=51,
|
||||
DIV_INS_TED=52,
|
||||
DIV_INS_MAX,
|
||||
DIV_INS_NULL
|
||||
};
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ bool DivDispatch::keyOffAffectsPorta(int ch) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool DivDispatch::isVolGlobal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
int DivDispatch::getPortaFloor(int ch) {
|
||||
return 0x00;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -809,6 +809,8 @@ void DivPlatformAmiga::setFlags(const DivConfig& flags) {
|
|||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
|
||||
rate=chipClock/AMIGA_DIVIDER;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
|
|
|
|||
|
|
@ -566,6 +566,10 @@ bool DivPlatformC64::getWantPreNote() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivPlatformC64::isVolGlobal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
float DivPlatformC64::getPostAmp() {
|
||||
return (sidCore==1)?3.0f:1.0f;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
void notifyInsChange(int ins);
|
||||
bool getDCOffRequired();
|
||||
bool getWantPreNote();
|
||||
bool isVolGlobal();
|
||||
float getPostAmp();
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
void notifyInsDeletion(void* ins);
|
||||
|
|
|
|||
|
|
@ -360,26 +360,6 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].pcm.isNoteMap) {
|
||||
// note map macros
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<120) {
|
||||
if (chan[i].pcm.next!=chan[i].std.wave.val) {
|
||||
chan[i].pcm.next=chan[i].std.wave.val;
|
||||
chan[i].pcmChanged.index=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!chan[i].pcm.isNoteMap) {
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<parent->song.sampleLen) {
|
||||
if (chan[i].pcm.next!=chan[i].std.wave.val) {
|
||||
chan[i].pcm.next=chan[i].std.wave.val;
|
||||
chan[i].pcmChanged.index=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// update registers
|
||||
if (chan[i].volChanged.changed) {
|
||||
// calculate volume (16 bit)
|
||||
|
|
@ -432,7 +412,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
off=(double)center/8363.0;
|
||||
}
|
||||
if (ins->amiga.useNoteMap) {
|
||||
chan[i].pcm.note=next;
|
||||
//chan[i].pcm.note=next;
|
||||
}
|
||||
// get loop mode
|
||||
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX;
|
||||
|
|
@ -748,13 +728,13 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
||||
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
|
||||
int sample=ins->amiga.getSample(c.value);
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
sampleValid=true;
|
||||
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
|
||||
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
|
||||
chan[c.chan].pcm.note=c.value;
|
||||
chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].pcm.note=c.value;
|
||||
chan[c.chan].filter=ins->es5506.filter;
|
||||
chan[c.chan].envelope=ins->es5506.envelope;
|
||||
}
|
||||
|
|
@ -870,20 +850,6 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
// sample commands
|
||||
case DIV_CMD_WAVE:
|
||||
if (!chan[c.chan].useWave) {
|
||||
if (chan[c.chan].active) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
|
||||
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
|
||||
((!ins->amiga.useNoteMap) && (c.value>=0 && c.value<parent->song.sampleLen))) {
|
||||
chan[c.chan].pcm.next=c.value;
|
||||
chan[c.chan].pcmChanged.index=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// reserved for useWave
|
||||
break;
|
||||
// Filter commands
|
||||
case DIV_CMD_ES5506_FILTER_MODE:
|
||||
if (!chan[c.chan].active) {
|
||||
|
|
@ -1253,6 +1219,7 @@ int DivPlatformES5506::init(DivEngine* p, int channels, int sugRate, const DivCo
|
|||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
volScale=0;
|
||||
curPage=0;
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
isMuted[i]=false;
|
||||
|
|
|
|||
|
|
@ -105,15 +105,17 @@ class DivPlatformFMBase: public DivDispatch {
|
|||
}
|
||||
}
|
||||
}
|
||||
// only used by OPN2 for DAC writes
|
||||
inline void urgentWrite(unsigned short a, unsigned char v) {
|
||||
if (!skipRegisterWrites && !flushFirst) {
|
||||
if (writes.empty()) {
|
||||
writes.push_back(QueuedWrite(a,v));
|
||||
} else if (writes.size()>16 || writes.front().addrOrVal) {
|
||||
writes.push_back(QueuedWrite(a,v));
|
||||
} else {
|
||||
writes.push_front(QueuedWrite(a,v));
|
||||
if (!writes.empty()) {
|
||||
// check for hard reset
|
||||
if (writes.front().addr==0xf0) {
|
||||
// replace hard reset with DAC write
|
||||
writes.pop_front();
|
||||
}
|
||||
}
|
||||
writes.push_front(QueuedWrite(a,v));
|
||||
if (dumpWrites) {
|
||||
addWrite(a,v);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ void DivPlatformGB::acquire(short** buf, size_t len) {
|
|||
}
|
||||
|
||||
void DivPlatformGB::updateWave() {
|
||||
logV("WAVE UPDATE");
|
||||
rWrite(0x1a,0);
|
||||
for (int i=0; i<16; i++) {
|
||||
int nibble1=ws.output[((i<<1)+antiClickWavePos)&31];
|
||||
|
|
@ -255,7 +254,7 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
chan[i].sweepChanged=true;
|
||||
break;
|
||||
case DivInstrumentGB::DIV_GB_HWCMD_WAIT:
|
||||
chan[i].hwSeqDelay=data+1;
|
||||
chan[i].hwSeqDelay=(data+1)*parent->tickMult;
|
||||
leave=true;
|
||||
break;
|
||||
case DivInstrumentGB::DIV_GB_HWCMD_WAIT_REL:
|
||||
|
|
|
|||
|
|
@ -96,32 +96,22 @@ void DivPlatformGenesis::processDAC(int iRate) {
|
|||
//sample>>=1;
|
||||
if (sample<-128) sample=-128;
|
||||
if (sample>127) sample=127;
|
||||
urgentWrite(0x2a,(unsigned char)sample+0x80);
|
||||
dacWrite=(unsigned char)(sample+0x80);
|
||||
}
|
||||
} else {
|
||||
if (!chan[5].dacReady) {
|
||||
chan[5].dacDelay+=32000;
|
||||
if (chan[5].dacDelay>=iRate) {
|
||||
chan[5].dacDelay-=iRate;
|
||||
chan[5].dacReady=true;
|
||||
}
|
||||
}
|
||||
if (chan[5].dacMode && chan[5].dacSample!=-1) {
|
||||
chan[5].dacPeriod+=chan[5].dacRate;
|
||||
if (chan[5].dacPeriod>=iRate) {
|
||||
DivSample* s=parent->getSample(chan[5].dacSample);
|
||||
if (s->samples>0 && chan[5].dacPos<s->samples) {
|
||||
if (!isMuted[5]) {
|
||||
if (chan[5].dacReady && writes.size()<16) {
|
||||
int sample;
|
||||
if (parent->song.noOPN2Vol) {
|
||||
sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
|
||||
} else {
|
||||
sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
|
||||
}
|
||||
urgentWrite(0x2a,(unsigned char)sample+0x80);
|
||||
chan[5].dacReady=false;
|
||||
int sample;
|
||||
if (parent->song.noOPN2Vol) {
|
||||
sample=s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
|
||||
} else {
|
||||
sample=(s->data8[chan[5].dacDirection?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
|
||||
}
|
||||
dacWrite=(unsigned char)(sample+0x80);
|
||||
}
|
||||
chan[5].dacPos++;
|
||||
if (!chan[5].dacDirection && (s->isLoopable() && chan[5].dacPos>=(unsigned int)s->loopEnd)) {
|
||||
|
|
@ -151,24 +141,34 @@ void DivPlatformGenesis::acquire_nuked(short** buf, size_t len) {
|
|||
os[0]=0; os[1]=0;
|
||||
for (int i=0; i<6; i++) {
|
||||
if (!writes.empty()) {
|
||||
if (--delay<0) {
|
||||
delay=0;
|
||||
QueuedWrite& w=writes.front();
|
||||
if (w.addrOrVal) {
|
||||
//logV("%.3x = %.2x",w.addr,w.val);
|
||||
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
|
||||
lastBusy=0;
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop_front();
|
||||
} else {
|
||||
lastBusy++;
|
||||
if (fm.write_busy==0) {
|
||||
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
|
||||
w.addrOrVal=true;
|
||||
QueuedWrite& w=writes.front();
|
||||
if (w.addrOrVal) {
|
||||
//logV("%.3x = %.2x",w.addr,w.val);
|
||||
OPN2_Write(&fm,0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop_front();
|
||||
|
||||
if (dacWrite>=0) {
|
||||
if (!canWriteDAC) {
|
||||
canWriteDAC=true;
|
||||
} else {
|
||||
urgentWrite(0x2a,dacWrite);
|
||||
dacWrite=-1;
|
||||
canWriteDAC=writes.empty();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fm.write_busy==0) {
|
||||
OPN2_Write(&fm,0x0+((w.addr>>8)<<1),w.addr);
|
||||
w.addrOrVal=true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canWriteDAC=true;
|
||||
if (dacWrite>=0) {
|
||||
urgentWrite(0x2a,dacWrite);
|
||||
dacWrite=-1;
|
||||
}
|
||||
flushFirst=false;
|
||||
}
|
||||
|
||||
|
|
@ -227,8 +227,22 @@ void DivPlatformGenesis::acquire_ymfm(short** buf, size_t len) {
|
|||
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop_front();
|
||||
lastBusy=1;
|
||||
|
||||
if (dacWrite>=0) {
|
||||
if (!canWriteDAC) {
|
||||
canWriteDAC=true;
|
||||
} else {
|
||||
urgentWrite(0x2a,dacWrite);
|
||||
dacWrite=-1;
|
||||
canWriteDAC=writes.empty();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
canWriteDAC=true;
|
||||
if (dacWrite>=0) {
|
||||
urgentWrite(0x2a,dacWrite);
|
||||
dacWrite=-1;
|
||||
}
|
||||
flushFirst=false;
|
||||
}
|
||||
|
||||
|
|
@ -288,6 +302,11 @@ void DivPlatformGenesis::fillStream(std::vector<DivDelayedWrite>& stream, int sR
|
|||
for (size_t i=0; i<len; i++) {
|
||||
processDAC(sRate);
|
||||
|
||||
if (dacWrite>=0) {
|
||||
urgentWrite(0x2a,dacWrite);
|
||||
dacWrite=-1;
|
||||
}
|
||||
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
stream.push_back(DivDelayedWrite(i,w.addr,w.val));
|
||||
|
|
@ -953,6 +972,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
|
|
@ -1314,11 +1334,12 @@ void DivPlatformGenesis::reset() {
|
|||
pendingWrites[i]=-1;
|
||||
}
|
||||
|
||||
lastBusy=60;
|
||||
lfoValue=8;
|
||||
softPCMTimer=0;
|
||||
extMode=false;
|
||||
flushFirst=false;
|
||||
dacWrite=-1;
|
||||
canWriteDAC=true;
|
||||
|
||||
if (softPCM) {
|
||||
chan[5].dacMode=true;
|
||||
|
|
@ -1330,8 +1351,6 @@ void DivPlatformGenesis::reset() {
|
|||
|
||||
// LFO
|
||||
immWrite(0x22,lfoValue);
|
||||
|
||||
delay=0;
|
||||
}
|
||||
|
||||
int DivPlatformGenesis::getOutputCount() {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,6 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
unsigned int dacPos;
|
||||
int dacSample;
|
||||
int dacDelay;
|
||||
bool dacReady;
|
||||
bool dacDirection;
|
||||
bool setPos;
|
||||
unsigned char sampleBank;
|
||||
|
|
@ -69,7 +68,6 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
dacPos(0),
|
||||
dacSample(-1),
|
||||
dacDelay(0),
|
||||
dacReady(true),
|
||||
dacDirection(false),
|
||||
setPos(false),
|
||||
sampleBank(0),
|
||||
|
|
@ -86,8 +84,9 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
|
||||
int softPCMTimer;
|
||||
|
||||
bool extMode, softPCM, noExtMacros, useYMFM;
|
||||
bool extMode, softPCM, noExtMacros, useYMFM, canWriteDAC;
|
||||
unsigned char chipType;
|
||||
short dacWrite;
|
||||
|
||||
unsigned char dacVolTable[128];
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "genesisext.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
|
|
@ -143,6 +144,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
opChan[ch].insChanged=true;
|
||||
}
|
||||
opChan[ch].ins=c.value;
|
||||
chan[extChanOffs].ins=opChan[ch].ins;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (c.value==0 && c.value2==0) {
|
||||
|
|
@ -218,8 +220,15 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
if (!extMode) {
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
}
|
||||
chan[extChanOffs].insChanged=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
|
|
@ -430,17 +439,21 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
|
|||
return;
|
||||
}
|
||||
isOpMuted[ch-2]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-2];
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
DivPlatformGenesis::muteChannel(extChanOffs,IS_EXTCH_MUTED);
|
||||
|
||||
if (extMode) {
|
||||
int ordch=orderedOps[ch-2];
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
}
|
||||
}
|
||||
|
||||
static int opChanOffsL[4]={
|
||||
|
|
@ -476,13 +489,6 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
if (chan[csmChan].active) { // CSM
|
||||
writeMask^=0xf0;
|
||||
}
|
||||
/*printf(
|
||||
"Mask: %c %c %c %c\n",
|
||||
(writeMask&0x10)?'1':'-',
|
||||
(writeMask&0x20)?'2':'-',
|
||||
(writeMask&0x40)?'3':'-',
|
||||
(writeMask&0x80)?'4':'-'
|
||||
);*/
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
|
|
@ -518,6 +524,39 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// channel macros
|
||||
if (opChan[i].std.alg.had) {
|
||||
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
|
||||
if (isOpMuted[j] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i==0 || fbAllOps) {
|
||||
if (opChan[i].std.fb.had) {
|
||||
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
}
|
||||
}
|
||||
if (opChan[i].std.fms.had) {
|
||||
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ams.had) {
|
||||
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ex3.had) {
|
||||
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
|
||||
rWrite(0x22,lfoValue);
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
|
|
@ -576,6 +615,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
unsigned char hardResetMask=0;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
|
@ -603,8 +643,13 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
writeNoteOn=true;
|
||||
if (opChan[i].mask) {
|
||||
writeMask|=1<<(4+i);
|
||||
if (opChan[i].hardReset) {
|
||||
hardResetMask|=1<<(4+i);
|
||||
}
|
||||
}
|
||||
if (!opChan[i].hardReset) {
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -631,14 +676,9 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
if (chan[csmChan].active) { // CSM
|
||||
writeMask^=0xf0;
|
||||
}
|
||||
/*printf(
|
||||
"Mask: %c %c %c %c\n",
|
||||
(writeMask&0x10)?'1':'-',
|
||||
(writeMask&0x20)?'2':'-',
|
||||
(writeMask&0x40)?'3':'-',
|
||||
(writeMask&0x80)?'4':'-'
|
||||
);*/
|
||||
writeMask^=hardResetMask;
|
||||
immWrite(0x28,writeMask);
|
||||
writeMask^=hardResetMask;
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
|
|
@ -651,6 +691,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
immWrite(0x28,writeMask);
|
||||
|
|
|
|||
|
|
@ -145,15 +145,15 @@ void DivPlatformK053260::tick(bool sysTick) {
|
|||
off=8363.0/s->centerRate;
|
||||
}
|
||||
}
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
DivSample* s=parent->getSample(sample);
|
||||
chan[i].freq=0x1000-(int)(off*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].freq>4095) chan[i].freq=4095;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].keyOn) {
|
||||
unsigned int start=0;
|
||||
unsigned int length=0;
|
||||
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
start=sampleOffK053260[chan[i].sample];
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
start=sampleOffK053260[sample];
|
||||
length=s->length8;
|
||||
if (chan[i].reverse) {
|
||||
start+=length;
|
||||
|
|
@ -163,8 +163,7 @@ void DivPlatformK053260::tick(bool sysTick) {
|
|||
if (chan[i].audPos>0) {
|
||||
if (chan[i].reverse) {
|
||||
start=start-MIN(chan[i].audPos,s->length8);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
start=start+MIN(chan[i].audPos,s->length8);
|
||||
}
|
||||
length=MAX(1,length-chan[i].audPos);
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ void DivPlatformMSM5232::acquire(short** buf, size_t len) {
|
|||
|
||||
for (int i=0; i<8; i++) {
|
||||
int o=(
|
||||
((regPool[12+(i>>4)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+
|
||||
((regPool[12+(i>>4)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+
|
||||
((regPool[12+(i>>4)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+
|
||||
((regPool[12+(i>>4)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0)
|
||||
((regPool[12+(i>>2)]&1)?((msm->vo16[i]*partVolume[3+(i&4)])>>8):0)+
|
||||
((regPool[12+(i>>2)]&2)?((msm->vo8[i]*partVolume[2+(i&4)])>>8):0)+
|
||||
((regPool[12+(i>>2)]&4)?((msm->vo4[i]*partVolume[1+(i&4)])>>8):0)+
|
||||
((regPool[12+(i>>2)]&8)?((msm->vo2[i]*partVolume[i&4])>>8):0)
|
||||
)<<2;
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(o,-32768,32767);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,10 +31,6 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformMSM6258::acquire(short** buf, size_t len) {
|
||||
short* outs[2]={
|
||||
&msmOut,
|
||||
NULL
|
||||
};
|
||||
for (size_t h=0; h<len; h++) {
|
||||
if (--msmClockCount<0) {
|
||||
if (--msmDividerCount<=0) {
|
||||
|
|
@ -71,7 +67,7 @@ void DivPlatformMSM6258::acquire(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
msm->sound_stream_update(outs,1);
|
||||
msm->sound_stream_update(&msmOut,1);
|
||||
msmDividerCount=msmDivider;
|
||||
}
|
||||
msmClockCount=msmClock;
|
||||
|
|
@ -390,6 +386,9 @@ int DivPlatformMSM6258::init(DivEngine* p, int channels, int sugRate, const DivC
|
|||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
msm=new okim6258_device(4000000);
|
||||
msm->set_start_div(okim6258_device::FOSC_DIV_BY_1024);
|
||||
msm->set_type(okim6258_device::TYPE_4BITS);
|
||||
msm->set_outbits(okim6258_device::OUTPUT_12BITS);
|
||||
msm->device_start();
|
||||
setFlags(flags);
|
||||
reset();
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ class DivPlatformMSM6258: public DivDispatch {
|
|||
};
|
||||
FixedQueue<QueuedWrite,256> writes;
|
||||
okim6258_device* msm;
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel;
|
||||
signed char msmDividerCount, msmClockCount;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
rWriteMask(0x78-(c<<3)+(a&7),v,m) \
|
||||
}
|
||||
|
||||
#define CHIP_FREQBASE (15*32768)
|
||||
#define CHIP_FREQBASE (15*524288)
|
||||
|
||||
const char* regCheatSheetN163[]={
|
||||
"FreqL7", "40",
|
||||
|
|
@ -256,7 +256,12 @@ void DivPlatformN163::tick(bool sysTick) {
|
|||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
// TODO: what is this mess?
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/16);
|
||||
if (lenCompensate) {
|
||||
chan[i].freq=(((chan[i].freq*chan[i].curWaveLen)*(chanMax+1))/256);
|
||||
} else {
|
||||
chan[i].freq*=(chanMax+1);
|
||||
chan[i].freq>>=3;
|
||||
}
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
@ -359,13 +364,13 @@ int DivPlatformN163::dispatch(DivCommand c) {
|
|||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:16);
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:16);
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
|
|
@ -570,6 +575,8 @@ void DivPlatformN163::setFlags(const DivConfig& flags) {
|
|||
oscBuf[i]->rate=rate/(initChanMax+1);
|
||||
}
|
||||
|
||||
lenCompensate=flags.getBool("lenCompensate",false);
|
||||
|
||||
// needed to make sure changing channel count won't trigger glitches
|
||||
reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class DivPlatformN163: public DivDispatch {
|
|||
unsigned char initChanMax;
|
||||
unsigned char chanMax;
|
||||
short loadWave, loadPos;
|
||||
bool multiplex;
|
||||
bool multiplex, lenCompensate;
|
||||
|
||||
n163_core n163;
|
||||
unsigned char regPool[128];
|
||||
|
|
|
|||
|
|
@ -571,7 +571,7 @@ void DivPlatformNamcoWSG::setFlags(const DivConfig& flags) {
|
|||
chipClock=3072000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/32;
|
||||
namco->device_clock_changed(rate);
|
||||
namco->device_clock_changed(96000);
|
||||
for (int i=0; i<chans; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,9 +130,9 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
|
|||
for (size_t i=0; i<len; i++) {
|
||||
doPCM;
|
||||
|
||||
nes1_NP->Tick(1);
|
||||
nes2_NP->TickFrameSequence(1);
|
||||
nes2_NP->Tick(1);
|
||||
nes1_NP->Tick(8);
|
||||
nes2_NP->TickFrameSequence(8);
|
||||
nes2_NP->Tick(8);
|
||||
nes1_NP->Render(out1);
|
||||
nes2_NP->Render(out2);
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
|
|||
if (sample>32767) sample=32767;
|
||||
if (sample<-32768) sample=-32768;
|
||||
buf[0][i]=sample;
|
||||
if (++writeOscBuf>=32) {
|
||||
if (++writeOscBuf>=4) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=nes1_NP->out[0]<<11;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=nes1_NP->out[1]<<11;
|
||||
|
|
@ -332,7 +332,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
if (chan[4].keyOn) {
|
||||
if (dpcmMode && !skipRegisterWrites && dacSample>=0 && dacSample<parent->song.sampleLen) {
|
||||
unsigned int dpcmAddr=sampleOffDPCM[dacSample];
|
||||
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
|
||||
unsigned int dpcmLen=parent->getSample(dacSample)->lengthDPCM>>4;
|
||||
if (dpcmLen>255) dpcmLen=255;
|
||||
goingToLoop=parent->getSample(dacSample)->isLoopable();
|
||||
// write DPCM
|
||||
|
|
@ -749,8 +749,11 @@ void DivPlatformNES::setFlags(const DivConfig& flags) {
|
|||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock;
|
||||
if (useNP) {
|
||||
rate/=8;
|
||||
}
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate/32;
|
||||
oscBuf[i]->rate=rate/(useNP?4:32);
|
||||
}
|
||||
|
||||
dpcmModeDefault=flags.getBool("dpcmMode",true);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
|
||||
#define CHIP_FREQBASE 1180068
|
||||
|
||||
#define DRUM_VOL(_x) (drumActivated[_x]?drumVol[_x]:15)
|
||||
|
||||
const unsigned char cycleMapOPLL[18]={
|
||||
8, 7, 6, 7, 8, 7, 8, 6, 0, 1, 2, 7, 8, 9, 3, 4, 5, 9
|
||||
};
|
||||
|
|
@ -52,7 +54,7 @@ void DivPlatformOPLL::acquire_nuked(short** buf, size_t len) {
|
|||
QueuedWrite& w=writes.front();
|
||||
if (w.addrOrVal) {
|
||||
OPLL_Write(&fm,1,w.val);
|
||||
//printf("write: %x = %.2x\n",w.addr,w.val);
|
||||
//logV("write: %x = %.2x",w.addr,w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
writes.pop();
|
||||
delay=21;
|
||||
|
|
@ -104,10 +106,10 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
|
||||
if (i>=6 && properDrums) {
|
||||
drumVol[i-6]=15-chan[i].outVol;
|
||||
rWrite(0x36,drumVol[0]);
|
||||
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
|
||||
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
|
||||
} else if (i<6 || !drums) {
|
||||
rWrite(0x36,DRUM_VOL(0));
|
||||
rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4));
|
||||
rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4));
|
||||
} else if (i<6 || !crapDrums) {
|
||||
if (i<9) {
|
||||
rWrite(0x30+i,((15-VOL_SCALE_LOG_BROKEN(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
|
||||
}
|
||||
|
|
@ -230,13 +232,16 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
if (i>=6 && properDrums) {
|
||||
drumState&=~(0x10>>(i-6));
|
||||
immWrite(0x0e,0x20|drumState);
|
||||
} else if (i>=6 && drums) {
|
||||
//logV("properDrums %d",i);
|
||||
} else if (i>=6 && crapDrums) {
|
||||
drumState&=~(0x10>>(chan[i].note%12));
|
||||
immWrite(0x0e,0x20|drumState);
|
||||
//logV("drums %d",i);
|
||||
} else {
|
||||
if (i<9) {
|
||||
immWrite(0x20+i,(chan[i].freqH)|(chan[i].state.alg?0x20:0));
|
||||
}
|
||||
//logV("normal %d",i);
|
||||
}
|
||||
//chan[i].keyOn=false;
|
||||
chan[i].keyOff=false;
|
||||
|
|
@ -253,7 +258,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
for (int i=0; i<11; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq;
|
||||
if (chan[i].fixedFreq>0 && properDrums) chan[i].freq=chan[i].fixedFreq;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
int freqt=toFreq(chan[i].freq);
|
||||
|
|
@ -262,7 +267,21 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
if (i>=6 && properDrums && (i<9 || !noTopHatFreq)) {
|
||||
immWrite(0x10+drumSlot[i],freqt&0xff);
|
||||
immWrite(0x20+drumSlot[i],freqt>>8);
|
||||
} else if (i<6 || !drums) {
|
||||
switch (i) {
|
||||
case 7:
|
||||
lastFreqSH=0;
|
||||
break;
|
||||
case 8:
|
||||
lastFreqTT=0;
|
||||
break;
|
||||
case 9:
|
||||
lastFreqTT=1;
|
||||
break;
|
||||
case 19:
|
||||
lastFreqSH=1;
|
||||
break;
|
||||
}
|
||||
} else if (i<6 || !crapDrums) {
|
||||
if (i<9) {
|
||||
immWrite(0x10+i,freqt&0xff);
|
||||
}
|
||||
|
|
@ -275,7 +294,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
immWrite(0x0e,0x20|drumState);
|
||||
}
|
||||
chan[i].keyOn=false;
|
||||
} else if (chan[i].keyOn && i>=6 && drums) {
|
||||
} else if (chan[i].keyOn && i>=6 && crapDrums) {
|
||||
//printf("%d\n",chan[i].note%12);
|
||||
drumState|=(0x10>>(chan[i].note%12));
|
||||
immWrite(0x0e,0x20|drumState);
|
||||
|
|
@ -362,22 +381,24 @@ void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) {
|
|||
}
|
||||
if (chan[ch].state.opllPreset==16) { // compatible drums mode
|
||||
if (ch>=6) {
|
||||
drums=true;
|
||||
immWrite(0x16,0x20);
|
||||
immWrite(0x26,0x05);
|
||||
immWrite(0x16,0x20);
|
||||
immWrite(0x26,0x05);
|
||||
immWrite(0x17,0x50);
|
||||
immWrite(0x27,0x05);
|
||||
immWrite(0x17,0x50);
|
||||
immWrite(0x27,0x05);
|
||||
immWrite(0x18,0xC0);
|
||||
immWrite(0x28,0x01);
|
||||
if (!properDrumsSys) {
|
||||
crapDrums=true;
|
||||
immWrite(0x16,0x20);
|
||||
immWrite(0x26,0x05);
|
||||
immWrite(0x16,0x20);
|
||||
immWrite(0x26,0x05);
|
||||
immWrite(0x17,0x50);
|
||||
immWrite(0x27,0x05);
|
||||
immWrite(0x17,0x50);
|
||||
immWrite(0x27,0x05);
|
||||
immWrite(0x18,0xC0);
|
||||
immWrite(0x28,0x01);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (ch>=6) {
|
||||
if (drums) {
|
||||
drums=false;
|
||||
if (crapDrums) {
|
||||
crapDrums=false;
|
||||
immWrite(0x0e,0);
|
||||
drumState=0;
|
||||
}
|
||||
|
|
@ -389,6 +410,45 @@ void DivPlatformOPLL::commitState(int ch, DivInstrument* ins) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformOPLL::switchMode(bool mode) {
|
||||
if (mode==properDrums) return;
|
||||
if (mode) {
|
||||
//logV("mode switch to DRUMS");
|
||||
for (int i=0; i<5; i++) {
|
||||
drumActivated[i]=chan[6+i].keyOn;
|
||||
}
|
||||
|
||||
immWrite(0x26,0);
|
||||
immWrite(0x27,0);
|
||||
immWrite(0x28,0);
|
||||
immWrite(0x16,0);
|
||||
immWrite(0x17,0);
|
||||
immWrite(0x18,0);
|
||||
immWrite(0x0e,0x20);
|
||||
rWrite(0x36,DRUM_VOL(0));
|
||||
rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4));
|
||||
rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4));
|
||||
oldWrites[0x36]=-1;
|
||||
oldWrites[0x37]=-1;
|
||||
oldWrites[0x38]=-1;
|
||||
} else {
|
||||
//logV("mode switch to NORMAL");
|
||||
immWrite(0x0e,0x20);
|
||||
immWrite(0x0e,0x00);
|
||||
for (int i=6; i<9; i++) {
|
||||
if (chan[i].active) {
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].keyOff=false;
|
||||
chan[i].keyOn=true;
|
||||
oldWrites[0x30+i]=-1;
|
||||
}
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
properDrums=mode;
|
||||
drumState=0;
|
||||
}
|
||||
|
||||
int DivPlatformOPLL::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
|
|
@ -405,18 +465,31 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
|
||||
if (c.chan>=6 && properDrums) { // drums mode
|
||||
chan[c.chan].insChanged=false;
|
||||
drumActivated[c.chan-6]=true;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
if (chan[c.chan].state.opllPreset==16 && chan[c.chan].state.fixedDrums) {
|
||||
switch (c.chan) {
|
||||
case 6:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9);
|
||||
break;
|
||||
case 7: case 10:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9);
|
||||
break;
|
||||
case 8: case 9:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
break;
|
||||
if (fixedAll) {
|
||||
chan[6].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9);
|
||||
chan[7].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9);
|
||||
chan[8].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
chan[9].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
chan[10].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9);
|
||||
|
||||
chan[7].freqChanged=true;
|
||||
chan[8].freqChanged=true;
|
||||
chan[9].freqChanged=true;
|
||||
} else {
|
||||
switch (c.chan) {
|
||||
case 6:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9);
|
||||
break;
|
||||
case 7: case 10:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9);
|
||||
break;
|
||||
case 8: case 9:
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
|
|
@ -426,6 +499,10 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
}
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].active=true;
|
||||
|
||||
rWrite(0x36,DRUM_VOL(0));
|
||||
rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4));
|
||||
rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -436,7 +513,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].note=c.value;
|
||||
|
||||
if (c.chan>=6 && drums) {
|
||||
if (c.chan>=6 && crapDrums) {
|
||||
switch (chan[c.chan].note%12) {
|
||||
case 0: // kick
|
||||
drumVol[0]=(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15);
|
||||
|
|
@ -454,9 +531,9 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
drumVol[4]=(15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15);
|
||||
break;
|
||||
}
|
||||
rWrite(0x36,drumVol[0]);
|
||||
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
|
||||
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
|
||||
rWrite(0x36,DRUM_VOL(0));
|
||||
rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4));
|
||||
rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4));
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
|
|
@ -491,11 +568,11 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
}
|
||||
if (c.chan>=6 && properDrums) {
|
||||
drumVol[c.chan-6]=15-chan[c.chan].outVol;
|
||||
rWrite(0x36,drumVol[0]);
|
||||
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
|
||||
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
|
||||
rWrite(0x36,DRUM_VOL(0));
|
||||
rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4));
|
||||
rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4));
|
||||
break;
|
||||
} else if (c.chan<6 || !drums) {
|
||||
} else if (c.chan<6 || !crapDrums) {
|
||||
if (c.chan<9) {
|
||||
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG_BROKEN(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
|
||||
}
|
||||
|
|
@ -553,7 +630,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (c.chan>=9 && !properDrums) return 0;
|
||||
if (c.chan<6 || (!drums && !properDrums)) {
|
||||
if (c.chan<6 || (!crapDrums && !properDrums)) {
|
||||
if (chan[c.chan].insChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPLL);
|
||||
commitState(c.chan,ins);
|
||||
|
|
@ -768,14 +845,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
case DIV_CMD_FM_EXTCH:
|
||||
if (!properDrumsSys) break;
|
||||
if ((int)properDrums==c.value) break;
|
||||
if (c.value) {
|
||||
properDrums=true;
|
||||
immWrite(0x0e,0x20);
|
||||
} else {
|
||||
properDrums=false;
|
||||
immWrite(0x0e,0x00);
|
||||
drumState=0;
|
||||
}
|
||||
switchMode(c.value);
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
|
|
@ -830,7 +900,7 @@ void DivPlatformOPLL::forceIns() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (drums) { // WHAT?! FIX THIS!
|
||||
if (crapDrums) { // WHAT?! FIX THIS!
|
||||
immWrite(0x16,0x20);
|
||||
immWrite(0x26,0x05);
|
||||
immWrite(0x16,0x20);
|
||||
|
|
@ -842,11 +912,25 @@ void DivPlatformOPLL::forceIns() {
|
|||
immWrite(0x18,0xC0);
|
||||
immWrite(0x28,0x01);
|
||||
}
|
||||
// restore drum volumes
|
||||
// restore drum volumes and state
|
||||
if (properDrums) {
|
||||
rWrite(0x36,drumVol[0]);
|
||||
rWrite(0x37,drumVol[1]|(drumVol[4]<<4));
|
||||
rWrite(0x38,drumVol[3]|(drumVol[2]<<4));
|
||||
rWrite(0x36,DRUM_VOL(0));
|
||||
rWrite(0x37,DRUM_VOL(1)|(DRUM_VOL(4)<<4));
|
||||
rWrite(0x38,DRUM_VOL(3)|(DRUM_VOL(2)<<4));
|
||||
|
||||
if (lastFreqSH==0) {
|
||||
chan[7].freqChanged=true;
|
||||
} else if (lastFreqSH==1) {
|
||||
chan[10].freqChanged=true;
|
||||
}
|
||||
|
||||
if (lastFreqTT==0) {
|
||||
chan[8].freqChanged=true;
|
||||
} else if (lastFreqTT==1) {
|
||||
chan[9].freqChanged=true;
|
||||
}
|
||||
|
||||
chan[6].freqChanged=true;
|
||||
}
|
||||
drumState=0;
|
||||
}
|
||||
|
|
@ -927,16 +1011,18 @@ void DivPlatformOPLL::reset() {
|
|||
drumState=0;
|
||||
lastCustomMemory=-1;
|
||||
|
||||
drumVol[0]=0;
|
||||
drumVol[1]=0;
|
||||
drumVol[2]=0;
|
||||
drumVol[3]=0;
|
||||
drumVol[4]=0;
|
||||
for (int i=0; i<5; i++) {
|
||||
drumVol[i]=0;
|
||||
drumActivated[i]=true;
|
||||
}
|
||||
|
||||
delay=0;
|
||||
drums=false;
|
||||
crapDrums=false;
|
||||
properDrums=properDrumsSys;
|
||||
|
||||
lastFreqSH=-1;
|
||||
lastFreqTT=-1;
|
||||
|
||||
if (properDrums) {
|
||||
immWrite(0x0e,0x20);
|
||||
}
|
||||
|
|
@ -1002,6 +1088,7 @@ void DivPlatformOPLL::setFlags(const DivConfig& flags) {
|
|||
oscBuf[i]->rate=rate/2;
|
||||
}
|
||||
noTopHatFreq=flags.getBool("noTopHatFreq",false);
|
||||
fixedAll=flags.getBool("fixedAll",false);
|
||||
}
|
||||
|
||||
int DivPlatformOPLL::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
|||
|
|
@ -59,12 +59,18 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
unsigned char lastBusy;
|
||||
unsigned char drumState;
|
||||
unsigned char drumVol[5];
|
||||
bool drumActivated[5];
|
||||
|
||||
// -1: undefined
|
||||
// 0: snare/tom
|
||||
// 1: hi-hat/top
|
||||
signed char lastFreqSH, lastFreqTT;
|
||||
|
||||
unsigned char regPool[256];
|
||||
|
||||
bool useYMFM;
|
||||
bool drums;
|
||||
bool properDrums, properDrumsSys, noTopHatFreq;
|
||||
bool crapDrums;
|
||||
bool properDrums, properDrumsSys, noTopHatFreq, fixedAll;
|
||||
bool vrc7;
|
||||
|
||||
unsigned char patchSet;
|
||||
|
|
@ -75,6 +81,7 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
void commitState(int ch, DivInstrument* ins);
|
||||
void switchMode(bool mode);
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include "pcspkr.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __linux__
|
||||
|
|
@ -190,9 +192,6 @@ const char** DivPlatformPCSpeaker::getRegisterSheet() {
|
|||
return regCheatSheetPCSpeaker;
|
||||
}
|
||||
|
||||
const float cut=0.05;
|
||||
const float reso=0.06;
|
||||
|
||||
void DivPlatformPCSpeaker::acquire_unfilt(short** buf, size_t len) {
|
||||
int out=0;
|
||||
for (size_t i=0; i<len; i++) {
|
||||
|
|
@ -229,8 +228,8 @@ void DivPlatformPCSpeaker::acquire_cone(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
float next=(pos>((freq+16)>>1) && !isMuted[0])?1:0;
|
||||
low+=0.04*band;
|
||||
band+=0.04*(next-low-band);
|
||||
low+=cut*band;
|
||||
band+=cut*(next-low-band);
|
||||
float out=(low+band)*0.75;
|
||||
if (out>1.0) out=1.0;
|
||||
if (out<-1.0) out=-1.0;
|
||||
|
|
@ -612,6 +611,17 @@ void DivPlatformPCSpeaker::setFlags(const DivConfig& flags) {
|
|||
rate=chipClock/PCSPKR_DIVIDER;
|
||||
speakerType=flags.getInt("speakerType",0)&3;
|
||||
oscBuf->rate=rate;
|
||||
|
||||
switch (speakerType) {
|
||||
case 1:
|
||||
cut=2.0*sin(M_PI*1900.0/rate);
|
||||
reso=0.0;
|
||||
break;
|
||||
default:
|
||||
cut=2.0*sin(M_PI*2375.0/rate);
|
||||
reso=0.06;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPCSpeaker::notifyInsDeletion(void* ins) {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,9 @@ class DivPlatformPCSpeaker: public DivDispatch {
|
|||
float low, band;
|
||||
float low2, high2, band2;
|
||||
float low3, band3;
|
||||
float cut;
|
||||
float reso;
|
||||
|
||||
unsigned short freq, lastFreq;
|
||||
unsigned char regPool[2];
|
||||
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ void DivPlatformPET::reset() {
|
|||
memset(regPool,0,16);
|
||||
chan[0]=Channel();
|
||||
chan[0].std.setEngine(parent);
|
||||
rWrite(10,chan[0].wave);
|
||||
}
|
||||
|
||||
int DivPlatformPET::getOutputCount() {
|
||||
|
|
|
|||
|
|
@ -296,6 +296,10 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
|
||||
chan[i].writeVol=false;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(0,0x9f|i<<5);
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -309,6 +313,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
chan[c.chan].actualNote=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOff=false;
|
||||
//if (!parent->song.brokenOutVol2) {
|
||||
chan[c.chan].writeVol=true;
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
|
|
@ -321,7 +326,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
rWrite(0,0x9f|c.chan<<5);
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
|
|
|
|||
|
|
@ -733,6 +733,7 @@ int DivPlatformSNES::getRegisterPoolSize() {
|
|||
|
||||
void DivPlatformSNES::initEcho() {
|
||||
unsigned char esa=0xf8-(echoDelay<<3);
|
||||
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
|
||||
if (echoOn) {
|
||||
rWrite(0x6d,esa);
|
||||
rWrite(0x7d,echoDelay);
|
||||
|
|
@ -742,13 +743,14 @@ void DivPlatformSNES::initEcho() {
|
|||
for (int i=0; i<8; i++) {
|
||||
rWrite(0x0f+(i<<4),echoFIR[i]);
|
||||
}
|
||||
rWrite(0x6c,control);
|
||||
} else {
|
||||
rWrite(0x6d,0);
|
||||
rWrite(0x7d,0);
|
||||
rWrite(0x2c,0);
|
||||
rWrite(0x3c,0);
|
||||
rWrite(0x6c,control);
|
||||
rWrite(0x7d,0);
|
||||
rWrite(0x6d,0xff);
|
||||
}
|
||||
writeControl=true;
|
||||
}
|
||||
|
||||
void DivPlatformSNES::reset() {
|
||||
|
|
|
|||
|
|
@ -135,9 +135,9 @@ void okim6258_device::device_reset()
|
|||
// sound_stream_update - handle a stream update
|
||||
//-------------------------------------------------
|
||||
|
||||
void okim6258_device::sound_stream_update(short** outputs, int len)
|
||||
void okim6258_device::sound_stream_update(short* output, int len)
|
||||
{
|
||||
short* buffer = outputs[0];
|
||||
short* buffer = output;
|
||||
|
||||
if (m_status & STATUS_PLAYING)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
void device_clock_changed();
|
||||
|
||||
// sound stream updates
|
||||
void sound_stream_update(short** outputs, int len);
|
||||
void sound_stream_update(short* output, int len);
|
||||
|
||||
private:
|
||||
int16_t clock_adpcm(uint8_t nibble);
|
||||
|
|
|
|||
231
src/engine/platform/sound/ted-sound.c
Normal file
231
src/engine/platform/sound/ted-sound.c
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* ted-sound.c
|
||||
*
|
||||
* Written by
|
||||
* Andreas Boose <viceteam@t-online.de>
|
||||
* Tibor Biczo <crown @ axelero . hu>
|
||||
* Marco van den Heuvel <blackystardust68@yahoo.com>
|
||||
*
|
||||
* This file is part of VICE, the Versatile Commodore Emulator.
|
||||
* See README for copyright notice.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ted-sound.h"
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* FIXME: Find proper volume multiplier. */
|
||||
const int16_t volume_tab[16] = {
|
||||
0x0000, 0x0800, 0x1000, 0x1800, 0x2000, 0x2800, 0x3000, 0x3800,
|
||||
0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff
|
||||
};
|
||||
|
||||
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int scc)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int16_t volume;
|
||||
|
||||
if (snd->digital) {
|
||||
for (i = 0; i < nr; i++) {
|
||||
pbuf[i] = (snd->volume * (snd->voice0_output_enabled + snd->voice1_output_enabled));
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < nr; i++) {
|
||||
snd->sample_position_remainder += snd->sample_length_remainder;
|
||||
if (snd->sample_position_remainder >= snd->speed) {
|
||||
snd->sample_position_remainder -= snd->speed;
|
||||
snd->sample_position_integer++;
|
||||
}
|
||||
snd->sample_position_integer += snd->sample_length_integer;
|
||||
if (snd->sample_position_integer >= 8) {
|
||||
/* Advance state engine */
|
||||
uint32_t ticks = snd->sample_position_integer >> 3;
|
||||
if (snd->voice0_accu <= ticks) {
|
||||
uint32_t delay = ticks - snd->voice0_accu;
|
||||
snd->voice0_sign ^= 1;
|
||||
snd->voice0_accu = 1023 - snd->voice0_reload;
|
||||
if (snd->voice0_accu == 0) {
|
||||
snd->voice0_accu = 1024;
|
||||
}
|
||||
if (delay >= snd->voice0_accu) {
|
||||
snd->voice0_sign = ((delay / snd->voice0_accu)
|
||||
& 1) ? snd->voice0_sign ^ 1
|
||||
: snd->voice0_sign;
|
||||
snd->voice0_accu = snd->voice0_accu - (delay % snd->voice0_accu);
|
||||
} else {
|
||||
snd->voice0_accu -= delay;
|
||||
}
|
||||
} else {
|
||||
snd->voice0_accu -= ticks;
|
||||
}
|
||||
|
||||
if (snd->voice1_accu <= ticks) {
|
||||
uint32_t delay = ticks - snd->voice1_accu;
|
||||
snd->voice1_sign ^= 1;
|
||||
snd->noise_shift_register
|
||||
= (snd->noise_shift_register << 1) +
|
||||
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
|
||||
((snd->noise_shift_register >> 5) & 1) ^
|
||||
((snd->noise_shift_register >> 4) & 1) ^
|
||||
((snd->noise_shift_register >> 1) & 1));
|
||||
snd->voice1_accu = 1023 - snd->voice1_reload;
|
||||
if (snd->voice1_accu == 0) {
|
||||
snd->voice1_accu = 1024;
|
||||
}
|
||||
if (delay >= snd->voice1_accu) {
|
||||
snd->voice1_sign = ((delay / snd->voice1_accu)
|
||||
& 1) ? snd->voice1_sign ^ 1
|
||||
: snd->voice1_sign;
|
||||
for (j = 0; j < (int)(delay / snd->voice1_accu);
|
||||
j++) {
|
||||
snd->noise_shift_register
|
||||
= (snd->noise_shift_register << 1) +
|
||||
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
|
||||
((snd->noise_shift_register >> 5) & 1) ^
|
||||
((snd->noise_shift_register >> 4) & 1) ^
|
||||
((snd->noise_shift_register >> 1) & 1));
|
||||
}
|
||||
snd->voice1_accu = snd->voice1_accu - (delay % snd->voice1_accu);
|
||||
} else {
|
||||
snd->voice1_accu -= delay;
|
||||
}
|
||||
} else {
|
||||
snd->voice1_accu -= ticks;
|
||||
}
|
||||
}
|
||||
snd->sample_position_integer = snd->sample_position_integer & 7;
|
||||
|
||||
volume = 0;
|
||||
|
||||
if (snd->voice0_output_enabled && snd->voice0_sign) {
|
||||
volume += snd->volume;
|
||||
}
|
||||
if (snd->voice1_output_enabled && !snd->noise && snd->voice1_sign) {
|
||||
volume += snd->volume;
|
||||
}
|
||||
if (snd->voice1_output_enabled && snd->noise && (!(snd->noise_shift_register & 1))) {
|
||||
volume += snd->volume;
|
||||
}
|
||||
|
||||
pbuf[i] = volume;
|
||||
}
|
||||
}
|
||||
return nr;
|
||||
}
|
||||
|
||||
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec)
|
||||
{
|
||||
uint8_t val;
|
||||
memset(snd,0,sizeof(struct plus4_sound_s));
|
||||
|
||||
snd->speed = speed;
|
||||
snd->sample_length_integer = cycles_per_sec / speed;
|
||||
snd->sample_length_remainder = cycles_per_sec % speed;
|
||||
snd->sample_position_integer = 0;
|
||||
snd->sample_position_remainder = 0;
|
||||
snd->noise_shift_register = 0;
|
||||
|
||||
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
|
||||
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
|
||||
val = snd->plus4_sound_data[3];
|
||||
snd->volume = volume_tab[val & 0x0f];
|
||||
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
|
||||
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
|
||||
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
|
||||
snd->digital = val & 0x80;
|
||||
if (snd->digital) {
|
||||
snd->voice0_sign = 1;
|
||||
snd->voice0_accu = 0;
|
||||
snd->voice1_sign = 1;
|
||||
snd->voice1_accu = 0;
|
||||
snd->noise_shift_register = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val)
|
||||
{
|
||||
switch (addr) {
|
||||
case 0x0e:
|
||||
snd->plus4_sound_data[0] = val;
|
||||
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
|
||||
break;
|
||||
case 0x0f:
|
||||
snd->plus4_sound_data[1] = val;
|
||||
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
|
||||
break;
|
||||
case 0x10:
|
||||
snd->plus4_sound_data[2] = val & 3;
|
||||
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
|
||||
break;
|
||||
case 0x11:
|
||||
snd->volume = volume_tab[val & 0x0f];
|
||||
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
|
||||
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
|
||||
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
|
||||
snd->digital = val & 0x80;
|
||||
if (snd->digital) {
|
||||
snd->voice0_sign = 1;
|
||||
snd->voice0_accu = 0;
|
||||
snd->voice1_sign = 1;
|
||||
snd->voice1_accu = 0;
|
||||
snd->noise_shift_register = 0;
|
||||
}
|
||||
snd->plus4_sound_data[3] = val;
|
||||
break;
|
||||
case 0x12:
|
||||
snd->plus4_sound_data[4] = val & 3;
|
||||
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr)
|
||||
{
|
||||
switch (addr) {
|
||||
case 0x0e:
|
||||
return snd->plus4_sound_data[0];
|
||||
case 0x0f:
|
||||
return snd->plus4_sound_data[1];
|
||||
case 0x10:
|
||||
return snd->plus4_sound_data[2] | 0xc0;
|
||||
case 0x11:
|
||||
return snd->plus4_sound_data[3];
|
||||
case 0x12:
|
||||
return snd->plus4_sound_data[4];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ted_sound_reset(struct plus4_sound_s* snd)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
snd->noise_shift_register = 0;
|
||||
for (i = 0x0e; i <= 0x12; i++) {
|
||||
ted_sound_machine_store(snd,i,0);
|
||||
}
|
||||
}
|
||||
81
src/engine/platform/sound/ted-sound.h
Normal file
81
src/engine/platform/sound/ted-sound.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* ted-sound.h
|
||||
*
|
||||
* Written by
|
||||
* Andreas Boose <viceteam@t-online.de>
|
||||
* Marco van den Heuvel <blackystardust68@yahoo.com>
|
||||
*
|
||||
* This file is part of VICE, the Versatile Commodore Emulator.
|
||||
* See README for copyright notice.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef VICE_TEDSOUND_H
|
||||
#define VICE_TEDSOUND_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct plus4_sound_s {
|
||||
/* Voice 0 collect number of cycles elapsed */
|
||||
uint32_t voice0_accu;
|
||||
/* Voice 0 toggle sign and reload accu if accu reached 0 */
|
||||
uint32_t voice0_reload;
|
||||
/* Voice 0 sign of the square wave */
|
||||
int16_t voice0_sign;
|
||||
uint8_t voice0_output_enabled;
|
||||
|
||||
/* Voice 1 collect number of cycles elapsed */
|
||||
uint32_t voice1_accu;
|
||||
/* Voice 1 toggle sign and reload accu if accu reached 0 */
|
||||
uint32_t voice1_reload;
|
||||
/* Voice 1 sign of the square wave */
|
||||
int16_t voice1_sign;
|
||||
uint8_t voice1_output_enabled;
|
||||
|
||||
/* Volume multiplier */
|
||||
int16_t volume;
|
||||
/* 8 cycles units per sample */
|
||||
uint32_t speed;
|
||||
uint32_t sample_position_integer;
|
||||
uint32_t sample_position_remainder;
|
||||
uint32_t sample_length_integer;
|
||||
uint32_t sample_length_remainder;
|
||||
/* Digital output? */
|
||||
uint8_t digital;
|
||||
/* Noise generator active? */
|
||||
uint8_t noise;
|
||||
uint8_t noise_shift_register;
|
||||
|
||||
/* Registers */
|
||||
uint8_t plus4_sound_data[5];
|
||||
};
|
||||
|
||||
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec);
|
||||
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int sound_chip_channels);
|
||||
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val);
|
||||
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr);
|
||||
void ted_sound_reset(struct plus4_sound_s* snd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -626,7 +626,7 @@ void DivPlatformSoundUnit::quit() {
|
|||
delete oscBuf[i];
|
||||
}
|
||||
delete su;
|
||||
delete sampleMem;
|
||||
delete[] sampleMem;
|
||||
}
|
||||
|
||||
DivPlatformSoundUnit::~DivPlatformSoundUnit() {
|
||||
|
|
|
|||
358
src/engine/platform/ted.cpp
Normal file
358
src/engine/platform/ted.cpp
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 "ted.h"
|
||||
#include "../engine.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 8
|
||||
|
||||
const char* regCheatSheetTED[]={
|
||||
"Freq0L", "0e",
|
||||
"Freq1L", "0f",
|
||||
"Freq1H", "10",
|
||||
"Control", "11",
|
||||
"Freq0H", "12",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformTED::getRegisterSheet() {
|
||||
return regCheatSheetTED;
|
||||
}
|
||||
|
||||
void DivPlatformTED::acquire(short** buf, size_t len) {
|
||||
for (size_t h=0; h<len; h++) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
ted_sound_machine_store(&ted,w.addr,w.val);
|
||||
regPool[(w.addr-0x0e)&7]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
ted_sound_machine_calculate_samples(&ted,&buf[0][h],1,1);
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(ted.voice0_output_enabled && ted.voice0_sign)?(ted.volume<<1):0;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(ted.voice1_output_enabled && ((ted.noise && (!(ted.noise_shift_register&1))) || (!ted.noise && ted.voice1_sign)))?(ted.volume<<1):0;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformTED::tick(bool sysTick) {
|
||||
bool resetPhase=false;
|
||||
|
||||
for (int _i=0; _i<2; _i++) {
|
||||
int i=chanOrder[_i];
|
||||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,MIN(8,chan[i].std.vol.val),8);
|
||||
updateCtrl=true;
|
||||
vol=chan[i].outVol;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].noise=chan[i].std.duty.val&2;
|
||||
chan[i].square=chan[i].std.duty.val&1;
|
||||
chan[i].freqChanged=true;
|
||||
updateCtrl=true;
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
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) {
|
||||
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)-1;
|
||||
if (i==1 && chan[i].noise && !chan[i].square) chan[i].freq>>=4;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>1023) chan[i].freq=1023;
|
||||
|
||||
if (i==1) {
|
||||
rWrite(0x0f,(1022-chan[i].freq)&0xff);
|
||||
rWrite(0x10,((1022-chan[i].freq)>>8)&0xff);
|
||||
} else {
|
||||
rWrite(0x0e,(1022-chan[i].freq)&0xff);
|
||||
rWrite(0x12,((1022-chan[i].freq)>>8)&0xff);
|
||||
}
|
||||
|
||||
if (chan[i].keyOn) {
|
||||
updateCtrl=true;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
updateCtrl=true;
|
||||
}
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) {
|
||||
resetPhase=true;
|
||||
updateCtrl=true;
|
||||
}
|
||||
}
|
||||
|
||||
if (resetPhase) {
|
||||
rWrite(0x11,0x80);
|
||||
}
|
||||
|
||||
if (updateCtrl) {
|
||||
updateCtrl=false;
|
||||
rWrite(0x11,(vol&15)|((chan[0].active && chan[0].square && !isMuted[0])?0x10:0)|((chan[1].active && chan[1].square && !isMuted[1])?0x20:0)|((chan[1].active && chan[1].noise && !isMuted[1])?0x40:0));
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformTED::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_TED);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=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;
|
||||
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;
|
||||
if (keyPriority) {
|
||||
if (chanOrder[0]==c.chan) {
|
||||
chanOrder[0]=chanOrder[1];
|
||||
chanOrder[1]=c.chan;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
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;
|
||||
}
|
||||
vol=chan[c.chan].outVol;
|
||||
updateCtrl=true;
|
||||
}
|
||||
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].noise=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
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_TED));
|
||||
}
|
||||
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 8;
|
||||
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_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformTED::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
updateCtrl=true;
|
||||
}
|
||||
|
||||
void DivPlatformTED::forceIns() {
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
updateCtrl=true;
|
||||
}
|
||||
|
||||
bool DivPlatformTED::isVolGlobal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void* DivPlatformTED::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformTED::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformTED::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformTED::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformTED::getRegisterPoolSize() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
void DivPlatformTED::reset() {
|
||||
writes.clear();
|
||||
memset(regPool,0,8);
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i]=DivPlatformTED::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
ted_sound_machine_init(&ted,1,8);
|
||||
updateCtrl=true;
|
||||
vol=15;
|
||||
|
||||
chanOrder[0]=0;
|
||||
chanOrder[1]=1;
|
||||
}
|
||||
|
||||
int DivPlatformTED::getOutputCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool DivPlatformTED::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformTED::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<2; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformTED::setFlags(const DivConfig& flags) {
|
||||
if (flags.getInt("clockSel",0)) {
|
||||
chipClock=COLOR_PAL*2.0/5.0;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/8;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
keyPriority=flags.getBool("keyPriority",true);
|
||||
}
|
||||
|
||||
void DivPlatformTED::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformTED::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformTED::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<2; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 2;
|
||||
}
|
||||
|
||||
void DivPlatformTED::quit() {
|
||||
for (int i=0; i<2; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformTED::~DivPlatformTED() {
|
||||
}
|
||||
79
src/engine/platform/ted.h
Normal file
79
src/engine/platform/ted.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2023 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 _TED_H
|
||||
#define _TED_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../fixedQueue.h"
|
||||
#include "sound/ted-sound.h"
|
||||
|
||||
class DivPlatformTED: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
bool noise, square;
|
||||
Channel():
|
||||
SharedChannel<signed char>(8),
|
||||
noise(false),
|
||||
square(true) {}
|
||||
};
|
||||
Channel chan[2];
|
||||
DivDispatchOscBuffer* oscBuf[2];
|
||||
bool isMuted[2];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,64> writes;
|
||||
|
||||
struct plus4_sound_s ted;
|
||||
unsigned char vol;
|
||||
bool updateCtrl, keyPriority;
|
||||
|
||||
unsigned char chanOrder[2];
|
||||
unsigned char regPool[8];
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
bool isVolGlobal();
|
||||
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();
|
||||
~DivPlatformTED();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -356,12 +356,12 @@ void DivPlatformTIA::poke(std::vector<DivRegWrite>& wlist) {
|
|||
|
||||
void DivPlatformTIA::setFlags(const DivConfig& flags) {
|
||||
if (flags.getInt("clockSel",0)) {
|
||||
rate=COLOR_PAL*4.0/5.0;
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
} else {
|
||||
rate=COLOR_NTSC;
|
||||
chipClock=COLOR_NTSC;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
chipClock=rate;
|
||||
rate=chipClock;
|
||||
mixingType=flags.getInt("mixingType",0)&3;
|
||||
for (int i=0; i<2; i++) {
|
||||
oscBuf[i]->rate=rate/114;
|
||||
|
|
|
|||
|
|
@ -278,6 +278,10 @@ void DivPlatformVIC20::forceIns() {
|
|||
}
|
||||
}
|
||||
|
||||
bool DivPlatformVIC20::isVolGlobal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void* DivPlatformVIC20::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class DivPlatformVIC20: public DivDispatch {
|
|||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
bool isVolGlobal();
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
|
|
|
|||
|
|
@ -670,6 +670,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].insChanged=true;
|
||||
}
|
||||
opChan[ch].ins=c.value;
|
||||
chan[extChanOffs].ins=opChan[ch].ins;
|
||||
break;
|
||||
case DIV_CMD_PITCH: {
|
||||
opChan[ch].pitch=c.value;
|
||||
|
|
@ -182,8 +183,15 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
if (!extMode) {
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
}
|
||||
chan[extChanOffs].insChanged=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
|
@ -355,6 +363,9 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
opChan[ch].hardReset=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -385,6 +396,9 @@ static int opChanOffsH[4]={
|
|||
};
|
||||
|
||||
void DivPlatformYM2203Ext::tick(bool sysTick) {
|
||||
int hardResetElapsed=0;
|
||||
bool mustHardReset=false;
|
||||
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
|
|
@ -395,6 +409,12 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
|
|||
writeMask&=~(1<<(4+i));
|
||||
opChan[i].keyOff=false;
|
||||
}
|
||||
if (opChan[i].hardReset && opChan[i].keyOn) {
|
||||
mustHardReset=true;
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
hardResetElapsed++;
|
||||
}
|
||||
}
|
||||
if (writeSomething) {
|
||||
immWrite(0x28,writeMask);
|
||||
|
|
@ -432,6 +452,27 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// channel macros
|
||||
if (opChan[i].std.alg.had) {
|
||||
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
|
||||
if (isOpMuted[j] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i==0 || fbAllOps) {
|
||||
if (opChan[i].std.fb.had) {
|
||||
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
}
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
|
|
@ -491,6 +532,7 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
|
|||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
unsigned char hardResetMask=0;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
|
@ -517,12 +559,36 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
|
|||
writeNoteOn=true;
|
||||
if (opChan[i].mask) {
|
||||
writeMask|=1<<(4+i);
|
||||
if (opChan[i].hardReset) {
|
||||
hardResetMask|=1<<(4+i);
|
||||
}
|
||||
}
|
||||
if (!opChan[i].hardReset) {
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
if (writeNoteOn) {
|
||||
writeMask^=hardResetMask;
|
||||
immWrite(0x28,writeMask);
|
||||
writeMask^=hardResetMask;
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
|
||||
immWrite(0xf0,i&0xff);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].keyOn && opChan[i].hardReset) {
|
||||
// restore SL/RR
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -537,13 +603,17 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
isOpMuted[ch-2]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-2];
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
DivPlatformYM2203::muteChannel(extChanOffs,IS_EXTCH_MUTED);
|
||||
|
||||
if (extMode) {
|
||||
int ordch=orderedOps[ch-2];
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1153,6 +1153,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].insChanged=true;
|
||||
}
|
||||
opChan[ch].ins=c.value;
|
||||
chan[extChanOffs].ins=opChan[ch].ins;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (c.value==0 && c.value2==0) {
|
||||
|
|
@ -200,8 +201,15 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
if (!extMode) {
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
}
|
||||
chan[extChanOffs].insChanged=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
|
|
@ -378,6 +386,9 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
opChan[ch].hardReset=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -408,6 +419,9 @@ static int opChanOffsH[4]={
|
|||
};
|
||||
|
||||
void DivPlatformYM2608Ext::tick(bool sysTick) {
|
||||
int hardResetElapsed=0;
|
||||
bool mustHardReset=false;
|
||||
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
|
|
@ -418,6 +432,12 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
|
|||
writeMask&=~(1<<(4+i));
|
||||
opChan[i].keyOff=false;
|
||||
}
|
||||
if (opChan[i].hardReset && opChan[i].keyOn) {
|
||||
mustHardReset=true;
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
hardResetElapsed++;
|
||||
}
|
||||
}
|
||||
if (writeSomething) {
|
||||
immWrite(0x28,writeMask);
|
||||
|
|
@ -455,6 +475,40 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// channel macros
|
||||
if (opChan[i].std.alg.had) {
|
||||
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
|
||||
if (isOpMuted[j] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i==0 || fbAllOps) {
|
||||
if (opChan[i].std.fb.had) {
|
||||
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
}
|
||||
}
|
||||
if (opChan[i].std.fms.had) {
|
||||
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ams.had) {
|
||||
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ex3.had) {
|
||||
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
|
||||
rWrite(0x22,lfoValue);
|
||||
}
|
||||
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
|
|
@ -513,6 +567,7 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
|
|||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
unsigned char hardResetMask=0;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
|
@ -539,12 +594,36 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
|
|||
writeNoteOn=true;
|
||||
if (opChan[i].mask) {
|
||||
writeMask|=1<<(4+i);
|
||||
if (opChan[i].hardReset) {
|
||||
hardResetMask|=1<<(4+i);
|
||||
}
|
||||
}
|
||||
if (!opChan[i].hardReset) {
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
if (writeNoteOn) {
|
||||
writeMask^=hardResetMask;
|
||||
immWrite(0x28,writeMask);
|
||||
writeMask^=hardResetMask;
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
|
||||
immWrite(0xf0,i&0xff);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].keyOn && opChan[i].hardReset) {
|
||||
// restore SL/RR
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -559,16 +638,20 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
isOpMuted[ch-2]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-2];
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
DivPlatformYM2608::muteChannel(extChanOffs,IS_EXTCH_MUTED);
|
||||
|
||||
if (extMode) {
|
||||
int ordch=orderedOps[ch-2];
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2608Ext::forceIns() {
|
||||
|
|
|
|||
|
|
@ -1125,6 +1125,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1192,6 +1192,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
opChan[ch].insChanged=true;
|
||||
}
|
||||
opChan[ch].ins=c.value;
|
||||
chan[extChanOffs].ins=opChan[ch].ins;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (c.value==0 && c.value2==0) {
|
||||
|
|
@ -196,8 +197,15 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
if (!extMode) {
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
}
|
||||
chan[extChanOffs].insChanged=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
|
|
@ -374,6 +382,9 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
opChan[ch].hardReset=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -404,6 +415,9 @@ static int opChanOffsH[4]={
|
|||
};
|
||||
|
||||
void DivPlatformYM2610BExt::tick(bool sysTick) {
|
||||
int hardResetElapsed=0;
|
||||
bool mustHardReset=false;
|
||||
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
|
|
@ -414,6 +428,12 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
writeMask&=~(1<<(4+i));
|
||||
opChan[i].keyOff=false;
|
||||
}
|
||||
if (opChan[i].hardReset && opChan[i].keyOn) {
|
||||
mustHardReset=true;
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
hardResetElapsed++;
|
||||
}
|
||||
}
|
||||
if (writeSomething) {
|
||||
immWrite(0x28,writeMask);
|
||||
|
|
@ -451,6 +471,39 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// channel macros
|
||||
if (opChan[i].std.alg.had) {
|
||||
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
|
||||
if (isOpMuted[j] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i==0 || fbAllOps) {
|
||||
if (opChan[i].std.fb.had) {
|
||||
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
}
|
||||
}
|
||||
if (opChan[i].std.fms.had) {
|
||||
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ams.had) {
|
||||
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ex3.had) {
|
||||
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
|
||||
rWrite(0x22,lfoValue);
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
|
||||
|
|
@ -509,6 +562,7 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
unsigned char hardResetMask=0;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
|
@ -535,12 +589,36 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
writeNoteOn=true;
|
||||
if (opChan[i].mask) {
|
||||
writeMask|=1<<(4+i);
|
||||
if (opChan[i].hardReset) {
|
||||
hardResetMask|=1<<(4+i);
|
||||
}
|
||||
}
|
||||
if (!opChan[i].hardReset) {
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
if (writeNoteOn) {
|
||||
writeMask^=hardResetMask;
|
||||
immWrite(0x28,writeMask);
|
||||
writeMask^=hardResetMask;
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
|
||||
immWrite(0xf0,i&0xff);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].keyOn && opChan[i].hardReset) {
|
||||
// restore SL/RR
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -555,16 +633,20 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
isOpMuted[ch-extChanOffs]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-extChanOffs];
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
}
|
||||
DivPlatformYM2610B::muteChannel(extChanOffs,IS_EXTCH_MUTED);
|
||||
|
||||
if (extMode) {
|
||||
int ordch=orderedOps[ch-extChanOffs];
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::forceIns() {
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].insChanged=true;
|
||||
}
|
||||
opChan[ch].ins=c.value;
|
||||
chan[extChanOffs].ins=opChan[ch].ins;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (c.value==0 && c.value2==0) {
|
||||
|
|
@ -196,8 +197,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extMode==(bool)c.value) break;
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
if (!extMode) {
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
}
|
||||
chan[extChanOffs].insChanged=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
|
|
@ -374,6 +382,9 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
opChan[ch].hardReset=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -404,6 +415,9 @@ static int opChanOffsH[4]={
|
|||
};
|
||||
|
||||
void DivPlatformYM2610Ext::tick(bool sysTick) {
|
||||
int hardResetElapsed=0;
|
||||
bool mustHardReset=false;
|
||||
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
|
|
@ -414,6 +428,12 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
|
|||
writeMask&=~(1<<(4+i));
|
||||
opChan[i].keyOff=false;
|
||||
}
|
||||
if (opChan[i].hardReset && opChan[i].keyOn) {
|
||||
mustHardReset=true;
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,0x0f);
|
||||
hardResetElapsed++;
|
||||
}
|
||||
}
|
||||
if (writeSomething) {
|
||||
immWrite(0x28,writeMask);
|
||||
|
|
@ -451,6 +471,39 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// channel macros
|
||||
if (opChan[i].std.alg.had) {
|
||||
chan[extChanOffs].state.alg=opChan[i].std.alg.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
if (!parent->song.algMacroBehavior) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[j];
|
||||
if (isOpMuted[j] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i==0 || fbAllOps) {
|
||||
if (opChan[i].std.fb.had) {
|
||||
chan[extChanOffs].state.fb=opChan[i].std.fb.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
}
|
||||
}
|
||||
if (opChan[i].std.fms.had) {
|
||||
chan[extChanOffs].state.fms=opChan[i].std.fms.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ams.had) {
|
||||
chan[extChanOffs].state.ams=opChan[i].std.ams.val;
|
||||
rWrite(chanOffs[extChanOffs]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[i].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
if (opChan[i].std.ex3.had) {
|
||||
lfoValue=(opChan[i].std.ex3.val>7)?0:(8|(opChan[i].std.ex3.val&7));
|
||||
rWrite(0x22,lfoValue);
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
|
||||
|
|
@ -509,6 +562,7 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
|
|||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
unsigned char hardResetMask=0;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
|
@ -535,12 +589,36 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
|
|||
writeNoteOn=true;
|
||||
if (opChan[i].mask) {
|
||||
writeMask|=1<<(4+i);
|
||||
if (opChan[i].hardReset) {
|
||||
hardResetMask|=1<<(4+i);
|
||||
}
|
||||
}
|
||||
if (!opChan[i].hardReset) {
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
if (writeNoteOn) {
|
||||
writeMask^=hardResetMask;
|
||||
immWrite(0x28,writeMask);
|
||||
writeMask^=hardResetMask;
|
||||
|
||||
// hard reset handling
|
||||
if (mustHardReset) {
|
||||
for (unsigned int i=hardResetElapsed; i<hardResetCycles; i++) {
|
||||
immWrite(0xf0,i&0xff);
|
||||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].keyOn && opChan[i].hardReset) {
|
||||
// restore SL/RR
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[i];
|
||||
immWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -555,16 +633,20 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
isOpMuted[ch-extChanOffs]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-extChanOffs];
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
}
|
||||
DivPlatformYM2610::muteChannel(extChanOffs,IS_EXTCH_MUTED);
|
||||
|
||||
if (extMode) {
|
||||
int ordch=orderedOps[ch-extChanOffs];
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs] || !op.enable) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
}
|
||||
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610Ext::forceIns() {
|
||||
|
|
|
|||
|
|
@ -1307,7 +1307,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
} else {
|
||||
DivMacroInt* macroInt=disCont[dispatchOfChan[note.channel]].dispatch->getChanMacroInt(dispatchChanOfChan[note.channel]);
|
||||
if (macroInt!=NULL) {
|
||||
if (macroInt->hasRelease) {
|
||||
if (macroInt->hasRelease && !disCont[dispatchOfChan[note.channel]].dispatch->isVolGlobal()) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,note.channel));
|
||||
} else {
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel));
|
||||
|
|
@ -1967,8 +1967,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
if (cycles<=0) {
|
||||
// we have to tick
|
||||
if (nextTick()) {
|
||||
totalTicks=0;
|
||||
totalSeconds=0;
|
||||
/*totalTicks=0;
|
||||
totalSeconds=0;*/
|
||||
lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC);
|
||||
logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG);
|
||||
totalLoops++;
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
|
|||
|
||||
// render data
|
||||
if (depth!=DIV_SAMPLE_DEPTH_8BIT && depth!=DIV_SAMPLE_DEPTH_16BIT) {
|
||||
logW("sample depth is wrong! (%d)",depth);
|
||||
logW("sample depth is wrong! (%d)",(int)depth);
|
||||
depth=DIV_SAMPLE_DEPTH_16BIT;
|
||||
}
|
||||
samples=(double)samples/samplePitchesSD[pitch];
|
||||
|
|
@ -480,6 +480,7 @@ bool DivSample::saveRaw(const char* path) {
|
|||
|
||||
// 16-bit memory is padded to 512, to make things easier for ADPCM-A/B.
|
||||
bool DivSample::initInternal(DivSampleDepth d, int count) {
|
||||
logV("initInternal(%d,%d)",(int)d,count);
|
||||
switch (d) {
|
||||
case DIV_SAMPLE_DEPTH_1BIT: // 1-bit
|
||||
if (data1!=NULL) delete[] data1;
|
||||
|
|
@ -489,7 +490,7 @@ bool DivSample::initInternal(DivSampleDepth d, int count) {
|
|||
break;
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM: // DPCM
|
||||
if (dataDPCM!=NULL) delete[] dataDPCM;
|
||||
lengthDPCM=1+((((count+7)/8)+15)&(~15));
|
||||
lengthDPCM=1+((((count-1)/8)+15)&(~15));
|
||||
dataDPCM=new unsigned char[lengthDPCM];
|
||||
memset(dataDPCM,0xaa,lengthDPCM);
|
||||
break;
|
||||
|
|
@ -748,7 +749,11 @@ void DivSample::convert(DivSampleDepth newDepth) {
|
|||
setSampleCount((samples+7)&(~7));
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_1BIT_DPCM:
|
||||
setSampleCount((1+((((samples+7)/8)+15)&(~15)))<<3);
|
||||
if (samples) {
|
||||
setSampleCount((1+((((samples-1)/8)+15)&(~15)))<<3);
|
||||
} else {
|
||||
setSampleCount(8);
|
||||
}
|
||||
break;
|
||||
case DIV_SAMPLE_DEPTH_YMZ_ADPCM:
|
||||
setSampleCount(((lengthZ+3)&(~0x03))*2);
|
||||
|
|
@ -1168,7 +1173,7 @@ void DivSample::render(unsigned int formatMask) {
|
|||
if (!initInternal(DIV_SAMPLE_DEPTH_1BIT_DPCM,samples)) return;
|
||||
int accum=63;
|
||||
int next=63;
|
||||
for (unsigned int i=0; i<samples; i++) {
|
||||
for (unsigned int i=0; (i<samples && (i>>3)<lengthDPCM); i++) {
|
||||
next=((unsigned short)(data16[i]^0x8000))>>9;
|
||||
if (next>accum) {
|
||||
dataDPCM[i>>3]|=1<<(i&7);
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ enum DivSystem {
|
|||
DIV_SYSTEM_YM2608_CSM,
|
||||
DIV_SYSTEM_SM8521,
|
||||
DIV_SYSTEM_PV1000,
|
||||
DIV_SYSTEM_K053260
|
||||
DIV_SYSTEM_K053260,
|
||||
DIV_SYSTEM_TED
|
||||
};
|
||||
|
||||
enum DivEffectType: unsigned short {
|
||||
|
|
|
|||
|
|
@ -451,7 +451,7 @@ void DivEngine::registerSystems() {
|
|||
|
||||
EffectHandlerMap fmOPN2EffectHandlerMap(fmEffectHandlerMap);
|
||||
fmOPN2EffectHandlerMap.insert({
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}},
|
||||
{0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}},
|
||||
});
|
||||
|
||||
|
|
@ -681,7 +681,7 @@ void DivEngine::registerSystems() {
|
|||
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Toggle noise mode"}},
|
||||
{0x12, {DIV_CMD_PCE_LFO_MODE, "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)"}},
|
||||
{0x13, {DIV_CMD_PCE_LFO_SPEED, "13xx: Set LFO speed"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}}
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -939,7 +939,7 @@ void DivEngine::registerSystems() {
|
|||
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_NULL},
|
||||
{
|
||||
{0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set duty cycle (pulse: 0 to 7)"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (pulse channel)"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -1182,7 +1182,7 @@ void DivEngine::registerSystems() {
|
|||
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Setup noise mode (0: disabled; 1-8: enabled/tap)"}},
|
||||
{0x12, {DIV_CMD_WS_SWEEP_TIME, "12xx: Setup sweep period (0: disabled; 1-20: enabled/period)"}},
|
||||
{0x13, {DIV_CMD_WS_SWEEP_AMOUNT, "13xx: Set sweep amount"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -1497,7 +1497,7 @@ void DivEngine::registerSystems() {
|
|||
{0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}},
|
||||
{0x11, {DIV_CMD_X1_010_ENVELOPE_SHAPE, "11xx: Set envelope shape"}},
|
||||
{0x12, {DIV_CMD_X1_010_SAMPLE_BANK_SLOT, "12xx: Set sample bank slot (0 to 7)"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode"}},
|
||||
{0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}},
|
||||
},
|
||||
{
|
||||
{0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set PCM frequency (1 to FF)"}},
|
||||
|
|
@ -1542,7 +1542,6 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
EffectHandlerMap es5506PreEffectHandlerMap={
|
||||
{0x10, {DIV_CMD_WAVE, "10xx: Change waveform (00 to FF)",effectVal}},
|
||||
{0x11, {DIV_CMD_ES5506_FILTER_MODE, "11xx: Set filter mode (00 to 03)",effectValAnd<3>}},
|
||||
{0x14, {DIV_CMD_ES5506_FILTER_K1, "14xx: Set filter coefficient K1 low byte (00 to FF)",effectValShift<0>,constVal<0x00ff>}},
|
||||
{0x15, {DIV_CMD_ES5506_FILTER_K1, "15xx: Set filter coefficient K1 high byte (00 to FF)",effectValShift<8>,constVal<0xff00>}},
|
||||
|
|
@ -1863,6 +1862,16 @@ void DivEngine::registerSystems() {
|
|||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_TED]=new DivSysDef(
|
||||
"MOS Technology TED", NULL, 0xcd, 0, 2, false, true, 0, false, 0,
|
||||
"two square waves (one may be turned into noise). used in the Commodore Plus/4, 16 and 116.",
|
||||
{"Channel 1", "Channel 2"},
|
||||
{"CH1", "CH2"},
|
||||
{DIV_CH_PULSE, DIV_CH_PULSE},
|
||||
{DIV_INS_TED, DIV_INS_TED},
|
||||
{}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
|
||||
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0,
|
||||
"this is a system designed for testing purposes.",
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ void DivZSM::init(unsigned int rate) {
|
|||
// Channel masks
|
||||
ymMask=0;
|
||||
psgMask=0;
|
||||
// Optimize writes
|
||||
optimize=true;
|
||||
}
|
||||
|
||||
int DivZSM::getoffset() {
|
||||
|
|
@ -116,17 +118,18 @@ void DivZSM::writeYM(unsigned char a, unsigned char v) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivZSM::writeSync(unsigned char a, unsigned char v) {
|
||||
return syncCache.push_back(DivRegWrite(a,v));
|
||||
}
|
||||
|
||||
void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
||||
// TODO: suppress writes to PSG voice that is not audible (volume=0)
|
||||
// ^ Let's leave these alone, ZSMKit has a feature that can benefit
|
||||
// from silent channels.
|
||||
if (a>=69) {
|
||||
logD("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v);
|
||||
return;
|
||||
} else if (a==68) {
|
||||
// Sync event
|
||||
numWrites++;
|
||||
return syncCache.push_back(v);
|
||||
return writeSync(0x00,v);
|
||||
} else if (a>=64) {
|
||||
return writePCM(a-64,v);
|
||||
}
|
||||
|
|
@ -209,6 +212,10 @@ void DivZSM::setLoopPoint() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivZSM::setOptimize(bool o) {
|
||||
optimize=o;
|
||||
}
|
||||
|
||||
SafeWriter* DivZSM::finish() {
|
||||
tick(0); // flush any pending writes / ticks
|
||||
flushTicks(); // flush ticks in case there were no writes pending
|
||||
|
|
@ -274,6 +281,11 @@ void DivZSM::flushWrites() {
|
|||
flushTicks(); // only flush ticks if there are writes pending.
|
||||
for (unsigned char i=0; i<64; i++) {
|
||||
if (psgState[psg_NEW][i]==psgState[psg_PREV][i]) continue;
|
||||
// if optimize=true, suppress writes to PSG voices that are not audible (volume=0 or R+L=0)
|
||||
// ZSMKit has a feature that can benefit from having silent channels
|
||||
// updated, so this is something that can be toggled off or on for export
|
||||
if (optimize && (i&3)!=2 && (psgState[psg_NEW][(i&0x3c)+2]&0x3f)==0) continue; // vol
|
||||
if (optimize && (i&3)!=2 && (psgState[psg_NEW][(i&0x3c)+2]&0xc0)==0) continue; // R+L
|
||||
psgState[psg_PREV][i]=psgState[psg_NEW][i];
|
||||
w->writeC(i);
|
||||
w->writeC(psgState[psg_NEW][i]);
|
||||
|
|
@ -390,15 +402,18 @@ void DivZSM::flushWrites() {
|
|||
}
|
||||
}
|
||||
n=0;
|
||||
while (n<(long)syncCache.size()) { // we have one or more sync events to write
|
||||
int writes=syncCache.size()-n;
|
||||
w->writeC(ZSM_EXT);
|
||||
if (writes>ZSM_SYNC_MAX_WRITES) writes=ZSM_SYNC_MAX_WRITES;
|
||||
w->writeC(ZSM_EXT_SYNC|(writes<<1));
|
||||
for (; writes>0; writes--) {
|
||||
w->writeC(0x00); // 0x00 = Arbitrary sync message
|
||||
w->writeC(syncCache[n++]);
|
||||
for (DivRegWrite& write: syncCache) {
|
||||
if (n%ZSM_SYNC_MAX_WRITES==0) {
|
||||
w->writeC(ZSM_EXT);
|
||||
if (syncCache.size()-n>ZSM_SYNC_MAX_WRITES) {
|
||||
w->writeC((unsigned char)(ZSM_EXT_SYNC|(ZSM_SYNC_MAX_WRITES<<1)));
|
||||
} else {
|
||||
w->writeC((unsigned char)(ZSM_EXT_SYNC|((syncCache.size()-n)<<1)));
|
||||
}
|
||||
}
|
||||
n++;
|
||||
w->writeC(write.addr);
|
||||
w->writeC(write.val);
|
||||
}
|
||||
syncCache.clear();
|
||||
numWrites=0;
|
||||
|
|
|
|||
|
|
@ -63,13 +63,14 @@ class DivZSM {
|
|||
std::vector<unsigned char> pcmData;
|
||||
std::vector<unsigned char> pcmCache;
|
||||
std::vector<S_pcmInst> pcmInsts;
|
||||
std::vector<unsigned char> syncCache;
|
||||
std::vector<DivRegWrite> syncCache;
|
||||
int loopOffset;
|
||||
int numWrites;
|
||||
int ticks;
|
||||
int tickRate;
|
||||
int ymMask;
|
||||
int psgMask;
|
||||
bool optimize;
|
||||
public:
|
||||
DivZSM();
|
||||
~DivZSM();
|
||||
|
|
@ -78,6 +79,8 @@ class DivZSM {
|
|||
void writeYM(unsigned char a, unsigned char v);
|
||||
void writePSG(unsigned char a, unsigned char v);
|
||||
void writePCM(unsigned char a, unsigned char v);
|
||||
void writeSync(unsigned char a, unsigned char v);
|
||||
void setOptimize(bool o);
|
||||
void tick(int numticks = 1);
|
||||
void setLoopPoint();
|
||||
SafeWriter* finish();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||
constexpr int MASTER_CLOCK_MASK=(sizeof(void*)==8)?0xff:0;
|
||||
|
||||
SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
||||
SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop, bool optimize) {
|
||||
int VERA=-1;
|
||||
int YM=-1;
|
||||
int IGNORED=0;
|
||||
|
|
@ -52,7 +52,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
break;
|
||||
default:
|
||||
IGNORED++;
|
||||
logD("Ignoring chip %d systemID %d",i,song.system[i]);
|
||||
logD("Ignoring chip %d systemID %d",i,(int)song.system[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -94,6 +94,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
playSub(false);
|
||||
//size_t tickCount=0;
|
||||
bool done=false;
|
||||
bool loopNow=false;
|
||||
int loopPos=-1;
|
||||
int fracWait=0; // accumulates fractional ticks
|
||||
if (VERA>=0) disCont[VERA].dispatch->toggleRegisterDump(true);
|
||||
|
|
@ -106,12 +107,34 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
// TODO: incorporate the Furnace meta-command for init data and filter
|
||||
// out writes to otherwise-unused channels.
|
||||
}
|
||||
// Indicate the song's tuning as a sync meta-event
|
||||
// specified in terms of how many 1/256th semitones
|
||||
// the song is offset from standard A-440 tuning.
|
||||
// This is mainly to benefit visualizations in players
|
||||
// for non-standard tunings so that they can avoid
|
||||
// displaying the entire song held in pitch bend.
|
||||
// Tunings offsets that exceed a half semitone
|
||||
// will simply be represented in a different key
|
||||
// by nature of overflowing the signed char value
|
||||
signed char tuningoffset=(signed char)(round(3072*(log(song.tuning/440.0)/log(2))))&0xff;
|
||||
zsm.writeSync(0x01,tuningoffset);
|
||||
// Set optimize flag, which mainly buffers PSG writes
|
||||
// whenever the channel is silent
|
||||
zsm.setOptimize(optimize);
|
||||
|
||||
while (!done) {
|
||||
if (loopPos==-1) {
|
||||
if (loopOrder==curOrder && loopRow==curRow && ticks==1 && loop) {
|
||||
loopPos=zsm.getoffset();
|
||||
zsm.setLoopPoint();
|
||||
if (loopOrder==curOrder && loopRow==curRow && loop)
|
||||
loopNow=true;
|
||||
if (loopNow) {
|
||||
// If Virtual Tempo is in use, our exact loop point
|
||||
// might be skipped due to quantization error.
|
||||
// If this happens, the tick immediately following is our loop point.
|
||||
if (ticks==1 || !(loopOrder==curOrder && loopRow==curRow)) {
|
||||
loopPos=zsm.getoffset();
|
||||
zsm.setLoopPoint();
|
||||
loopNow=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nextTick() || !playing) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue