Merge branch 'master' of https://github.com/tildearrow/furnace into nmk112
This commit is contained in:
commit
31977ab7c6
111 changed files with 2630 additions and 574 deletions
|
|
@ -67,6 +67,7 @@ struct blip_t
|
|||
int avail;
|
||||
int size;
|
||||
int integrator;
|
||||
unsigned char hipass;
|
||||
};
|
||||
|
||||
typedef int buf_t;
|
||||
|
|
@ -119,6 +120,7 @@ blip_t* blip_new( int size )
|
|||
{
|
||||
m->factor = time_unit / blip_max_ratio;
|
||||
m->size = size;
|
||||
m->hipass = 1;
|
||||
blip_clear( m );
|
||||
check_assumptions();
|
||||
}
|
||||
|
|
@ -135,6 +137,10 @@ void blip_delete( blip_t* m )
|
|||
}
|
||||
}
|
||||
|
||||
void blip_set_dc( blip_t* m, unsigned char enable ) {
|
||||
m->hipass=enable;
|
||||
}
|
||||
|
||||
void blip_set_rates( blip_t* m, double clock_rate, double sample_rate )
|
||||
{
|
||||
double factor = time_unit * sample_rate / clock_rate;
|
||||
|
|
@ -231,7 +237,9 @@ int blip_read_samples( blip_t* m, short out [], int count, int stereo )
|
|||
out += step;
|
||||
|
||||
/* High-pass filter */
|
||||
sum -= s << (delta_bits - bass_shift);
|
||||
if (m->hipass) {
|
||||
sum -= s << (delta_bits - bass_shift);
|
||||
}
|
||||
}
|
||||
while ( in != end );
|
||||
m->integrator = sum;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ Sample buffer that resamples from input clock rate to output sample rate */
|
|||
#ifndef BLIP_BUF_H
|
||||
#define BLIP_BUF_H
|
||||
|
||||
// MODIFIED by tildearrow:
|
||||
// - add option to disable high-pass filter
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -18,6 +21,9 @@ so that there are blip_max_ratio clocks per sample. Returns pointer to new
|
|||
buffer, or NULL if insufficient memory. */
|
||||
blip_t* blip_new( int sample_count );
|
||||
|
||||
/** (tildearrow) sets whether to enable high-pass filter. */
|
||||
void blip_set_dc( blip_t*, unsigned char enable );
|
||||
|
||||
/** Sets approximate input clock rate and output sample rate. For every
|
||||
clock_rate input clocks, approximately sample_rate samples are generated. */
|
||||
void blip_set_rates( blip_t*, double clock_rate, double sample_rate );
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ Author : Shay Green <gblargg@gmail.com>
|
|||
Website : http://www.slack.net/~ant/
|
||||
License : GNU Lesser General Public License (LGPL)
|
||||
|
||||
MODIFICATION DISCLAIMER
|
||||
-----------------------
|
||||
I have modified this library in order to add a function that disables the DC offset correction high-pass filter.
|
||||
- tildearrow
|
||||
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
template<typename T> struct SharedChannel {
|
||||
int freq, baseFreq, baseNoteOverride, pitch, pitch2, arpOff;
|
||||
int ins, note;
|
||||
bool active, insChanged, freqChanged, fixedArp, keyOn, keyOff, portaPause, inPorta;
|
||||
bool active, insChanged, freqChanged, fixedArp, keyOn, keyOff, portaPause, inPorta, volChanged;
|
||||
T vol, outVol;
|
||||
DivMacroInt std;
|
||||
void handleArp(int offset=0) {
|
||||
|
|
@ -79,6 +79,7 @@ template<typename T> struct SharedChannel {
|
|||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
volChanged(false),
|
||||
vol(initVol),
|
||||
outVol(initVol),
|
||||
std() {}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,9 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_EXTERNAL, // (value)
|
||||
|
||||
DIV_CMD_C64_AD, // (value)
|
||||
DIV_CMD_C64_SR, // (value)
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||
|
||||
DIV_CMD_MAX
|
||||
|
|
@ -385,7 +388,23 @@ struct DivChannelModeHints {
|
|||
// - 1: volume
|
||||
// - 2: pitch
|
||||
// - 3: panning
|
||||
// - 4: ???
|
||||
// - 4: chip primary
|
||||
// - 5: chip secondary
|
||||
// - 6: mixing
|
||||
// - 7: DSP
|
||||
// - 8: note
|
||||
// - 9: misc 1
|
||||
// - 10: misc 2
|
||||
// - 11: misc 3
|
||||
// - 12: attack
|
||||
// - 13: decay
|
||||
// - 14: sustain
|
||||
// - 15: release
|
||||
// - 16: dec linear
|
||||
// - 17: dec exp
|
||||
// - 18: inc linear
|
||||
// - 19: inc bent
|
||||
// - 20: direct
|
||||
unsigned char type[4];
|
||||
// up to 4
|
||||
unsigned char count;
|
||||
|
|
|
|||
|
|
@ -96,8 +96,13 @@ void DivDispatchContainer::setRates(double gotRate) {
|
|||
rateMemory=gotRate;
|
||||
}
|
||||
|
||||
void DivDispatchContainer::setQuality(bool lowQual) {
|
||||
void DivDispatchContainer::setQuality(bool lowQual, bool dcHiPass) {
|
||||
lowQuality=lowQual;
|
||||
hiPass=dcHiPass;
|
||||
for (int i=0; i<DIV_MAX_OUTPUTS; i++) {
|
||||
if (bb[i]==NULL) continue;
|
||||
blip_set_dc(bb[i],dcHiPass);
|
||||
}
|
||||
}
|
||||
|
||||
void DivDispatchContainer::grow(size_t size) {
|
||||
|
|
@ -123,6 +128,7 @@ void DivDispatchContainer::grow(size_t size) {
|
|||
logE("not enough memory!"); \
|
||||
return; \
|
||||
} \
|
||||
blip_set_dc(bb[i],hiPass); \
|
||||
blip_set_rates(bb[i],dispatch->rate,rateMemory); \
|
||||
\
|
||||
if (bbIn[i]==NULL) bbIn[i]=new short[bbInLen]; \
|
||||
|
|
@ -165,9 +171,11 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
|
|||
|
||||
if (dcOffCompensation && runtotal>0) {
|
||||
dcOffCompensation=false;
|
||||
for (int i=0; i<outs; i++) {
|
||||
if (bbIn[i]==NULL) continue;
|
||||
prevSample[i]=bbIn[i][0];
|
||||
if (hiPass) {
|
||||
for (int i=0; i<outs; i++) {
|
||||
if (bbIn[i]==NULL) continue;
|
||||
prevSample[i]=bbIn[i][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lowQuality) {
|
||||
|
|
@ -214,7 +222,7 @@ void DivDispatchContainer::clear() {
|
|||
prevSample[i]=0;
|
||||
}
|
||||
|
||||
if (dispatch->getDCOffRequired()) {
|
||||
if (dispatch->getDCOffRequired() && hiPass) {
|
||||
dcOffCompensation=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -621,6 +629,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
bbOut[i]=new short[bbInLen];
|
||||
memset(bbIn[i],0,bbInLen*sizeof(short));
|
||||
memset(bbOut[i],0,bbInLen*sizeof(short));
|
||||
blip_set_dc(bb[i],hiPass);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1081,7 +1081,7 @@ bool DivEngine::addSystem(DivSystem which) {
|
|||
song.patchbay.push_back((i<<20)|j);
|
||||
}
|
||||
} else {
|
||||
song.patchbay.reserve(outs);
|
||||
if (outs>0) song.patchbay.reserve(outs);
|
||||
for (unsigned int j=0; j<outs; j++) {
|
||||
song.patchbay.push_back((i<<20)|(j<<16)|j);
|
||||
}
|
||||
|
|
@ -1189,11 +1189,11 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
|||
|
||||
// prepare swap list
|
||||
int index=0;
|
||||
swapList.reserve(song.systemLen);
|
||||
if (song.systemLen>0) swapList.reserve(song.systemLen);
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
chanList.clear();
|
||||
const int channelCount=getChannelCount(song.system[i]);
|
||||
chanList.reserve(channelCount);
|
||||
if (channelCount>0) chanList.reserve(channelCount);
|
||||
for (int j=0; j<channelCount; j++) {
|
||||
chanList.push_back(index);
|
||||
index++;
|
||||
|
|
@ -1424,6 +1424,11 @@ DivChannelPair DivEngine::getChanPaired(int ch) {
|
|||
return disCont[dispatchOfChan[ch]].dispatch->getPaired(dispatchChanOfChan[ch]);
|
||||
}
|
||||
|
||||
DivChannelModeHints DivEngine::getChanModeHints(int ch) {
|
||||
if (ch<0 || ch>=chans) return DivChannelModeHints();
|
||||
return disCont[dispatchOfChan[ch]].dispatch->getModeHints(dispatchChanOfChan[ch]);
|
||||
}
|
||||
|
||||
unsigned char* DivEngine::getRegisterPool(int sys, int& size, int& depth) {
|
||||
if (sys<0 || sys>=song.systemLen) return NULL;
|
||||
if (disCont[sys].dispatch==NULL) return NULL;
|
||||
|
|
@ -3348,7 +3353,7 @@ bool DivEngine::switchMaster(bool full) {
|
|||
if (initAudioBackend()) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].setRates(got.rate);
|
||||
disCont[i].setQuality(lowQuality);
|
||||
disCont[i].setQuality(lowQuality,dcHiPass);
|
||||
}
|
||||
if (!output->setRun(true)) {
|
||||
logE("error while activating audio!");
|
||||
|
|
@ -3447,10 +3452,14 @@ void DivEngine::initDispatch(bool isRender) {
|
|||
BUSY_BEGIN;
|
||||
logV("initializing dispatch...");
|
||||
if (isRender) logI("render cores set");
|
||||
|
||||
lowQuality=getConfInt("audioQuality",0);
|
||||
dcHiPass=getConfInt("audioHiPass",1);
|
||||
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].init(song.system[i],this,getChannelCount(song.system[i]),got.rate,song.systemFlags[i],isRender);
|
||||
disCont[i].setRates(got.rate);
|
||||
disCont[i].setQuality(lowQuality);
|
||||
disCont[i].setQuality(lowQuality,dcHiPass);
|
||||
}
|
||||
if (song.patchbayAuto) {
|
||||
saveLock.lock();
|
||||
|
|
@ -3529,7 +3538,6 @@ bool DivEngine::initAudioBackend() {
|
|||
}
|
||||
#endif
|
||||
|
||||
lowQuality=getConfInt("audioQuality",0);
|
||||
forceMono=getConfInt("forceMono",0);
|
||||
clampSamples=getConfInt("clampSamples",0);
|
||||
lowLatency=getConfInt("lowLatency",0);
|
||||
|
|
@ -3777,6 +3785,7 @@ bool DivEngine::init() {
|
|||
logE("not enough memory!");
|
||||
return false;
|
||||
}
|
||||
blip_set_dc(samp_bb,0);
|
||||
|
||||
samp_bbOut=new short[32768];
|
||||
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ class DivWorkPool;
|
|||
|
||||
#define DIV_UNSTABLE
|
||||
|
||||
#define DIV_VERSION "dev184"
|
||||
#define DIV_ENGINE_VERSION 184
|
||||
#define DIV_VERSION "dev187"
|
||||
#define DIV_ENGINE_VERSION 187
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -205,7 +205,7 @@ struct DivDispatchContainer {
|
|||
short* bbInMapped[DIV_MAX_OUTPUTS];
|
||||
short* bbIn[DIV_MAX_OUTPUTS];
|
||||
short* bbOut[DIV_MAX_OUTPUTS];
|
||||
bool lowQuality, dcOffCompensation;
|
||||
bool lowQuality, dcOffCompensation, hiPass;
|
||||
double rateMemory;
|
||||
|
||||
// used in multi-thread
|
||||
|
|
@ -213,7 +213,7 @@ struct DivDispatchContainer {
|
|||
unsigned int size;
|
||||
|
||||
void setRates(double gotRate);
|
||||
void setQuality(bool lowQual);
|
||||
void setQuality(bool lowQual, bool dcHiPass);
|
||||
void grow(size_t size);
|
||||
void acquire(size_t offset, size_t count);
|
||||
void flush(size_t count);
|
||||
|
|
@ -230,6 +230,7 @@ struct DivDispatchContainer {
|
|||
lastAvail(0),
|
||||
lowQuality(false),
|
||||
dcOffCompensation(false),
|
||||
hiPass(true),
|
||||
rateMemory(0.0),
|
||||
cycles(0),
|
||||
size(0) {
|
||||
|
|
@ -389,6 +390,7 @@ class DivEngine {
|
|||
int chans;
|
||||
bool active;
|
||||
bool lowQuality;
|
||||
bool dcHiPass;
|
||||
bool playing;
|
||||
bool freelance;
|
||||
bool shallStop, shallStopSched;
|
||||
|
|
@ -1017,6 +1019,9 @@ class DivEngine {
|
|||
// get channel pairs
|
||||
DivChannelPair getChanPaired(int chan);
|
||||
|
||||
// get channel mode hints
|
||||
DivChannelModeHints getChanModeHints(int chan);
|
||||
|
||||
// get register pool
|
||||
unsigned char* getRegisterPool(int sys, int& size, int& depth);
|
||||
|
||||
|
|
@ -1221,6 +1226,7 @@ class DivEngine {
|
|||
chans(0),
|
||||
active(false),
|
||||
lowQuality(false),
|
||||
dcHiPass(true),
|
||||
playing(false),
|
||||
freelance(false),
|
||||
shallStop(false),
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.insLen=16;
|
||||
}
|
||||
logI("reading instruments (%d)...",ds.insLen);
|
||||
ds.ins.reserve(ds.insLen);
|
||||
if (ds.insLen>0) ds.ins.reserve(ds.insLen);
|
||||
for (int i=0; i<ds.insLen; i++) {
|
||||
DivInstrument* ins=new DivInstrument;
|
||||
unsigned char mode=0;
|
||||
|
|
@ -601,6 +601,8 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
if (ds.system[0]==DIV_SYSTEM_C64_6581 || ds.system[0]==DIV_SYSTEM_C64_8580) {
|
||||
bool volIsCutoff=false;
|
||||
|
||||
ins->c64.triOn=reader.readC();
|
||||
ins->c64.sawOn=reader.readC();
|
||||
ins->c64.pulseOn=reader.readC();
|
||||
|
|
@ -617,9 +619,9 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ins->c64.oscSync=reader.readC();
|
||||
ins->c64.toFilter=reader.readC();
|
||||
if (ds.version<0x11) {
|
||||
ins->c64.volIsCutoff=reader.readI();
|
||||
volIsCutoff=reader.readI();
|
||||
} else {
|
||||
ins->c64.volIsCutoff=reader.readC();
|
||||
volIsCutoff=reader.readC();
|
||||
}
|
||||
ins->c64.initFilter=reader.readC();
|
||||
|
||||
|
|
@ -631,10 +633,16 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ins->c64.ch3off=reader.readC();
|
||||
|
||||
// weird storage
|
||||
if (ins->c64.volIsCutoff) {
|
||||
for (int j=0; j<ins->std.volMacro.len; j++) {
|
||||
ins->std.volMacro.val[j]-=18;
|
||||
if (volIsCutoff) {
|
||||
// move to alg (new cutoff)
|
||||
ins->std.algMacro.len=ins->std.volMacro.len;
|
||||
ins->std.algMacro.loop=ins->std.volMacro.loop;
|
||||
ins->std.algMacro.rel=ins->std.volMacro.rel;
|
||||
for (int j=0; j<ins->std.algMacro.len; j++) {
|
||||
ins->std.algMacro.val[j]=-(ins->std.volMacro.val[j]-18);
|
||||
}
|
||||
ins->std.volMacro.len=0;
|
||||
memset(ins->std.volMacro.val,0,256*sizeof(int));
|
||||
}
|
||||
for (int j=0; j<ins->std.dutyMacro.len; j++) {
|
||||
ins->std.dutyMacro.val[j]-=12;
|
||||
|
|
@ -671,7 +679,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
if (ds.version>0x0b) {
|
||||
ds.waveLen=(unsigned char)reader.readC();
|
||||
logI("reading wavetables (%d)...",ds.waveLen);
|
||||
ds.wave.reserve(ds.waveLen);
|
||||
if (ds.waveLen>0) ds.wave.reserve(ds.waveLen);
|
||||
for (int i=0; i<ds.waveLen; i++) {
|
||||
DivWavetable* wave=new DivWavetable;
|
||||
wave->len=(unsigned char)reader.readI();
|
||||
|
|
@ -841,7 +849,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
// it appears this byte stored the YMU759 sample rate
|
||||
ymuSampleRate=reader.readC();
|
||||
}
|
||||
ds.sample.reserve(ds.sampleLen);
|
||||
if (ds.sampleLen>0) ds.sample.reserve(ds.sampleLen);
|
||||
for (int i=0; i<ds.sampleLen; i++) {
|
||||
DivSample* sample=new DivSample;
|
||||
int length=reader.readI();
|
||||
|
|
@ -1054,9 +1062,11 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.systemFlags[0].set("dpcmMode",false);
|
||||
}
|
||||
|
||||
// C64 no key priority
|
||||
// C64 no key priority, reset time and multiply relative
|
||||
if (ds.system[0]==DIV_SYSTEM_C64_8580 || ds.system[0]==DIV_SYSTEM_C64_6581) {
|
||||
ds.systemFlags[0].set("keyPriority",false);
|
||||
ds.systemFlags[0].set("initResetTime",1);
|
||||
ds.systemFlags[0].set("multiplyRel",true);
|
||||
}
|
||||
|
||||
// OPM broken pitch
|
||||
|
|
@ -1953,6 +1963,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
|
||||
logD("systems:");
|
||||
ds.systemLen=0;
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
unsigned char sysID=reader.readC();
|
||||
ds.system[i]=systemFromFileFur(sysID);
|
||||
|
|
@ -1973,6 +1984,13 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
tchans=DIV_MAX_CHANS;
|
||||
logW("too many channels!");
|
||||
}
|
||||
logV("system len: %d",ds.systemLen);
|
||||
if (ds.systemLen<1) {
|
||||
logE("zero chips!");
|
||||
lastError="zero chips!";
|
||||
delete[] file;
|
||||
return false;
|
||||
}
|
||||
|
||||
// system volume
|
||||
for (int i=0; i<DIV_MAX_CHIPS; i++) {
|
||||
|
|
@ -2361,7 +2379,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
// patchbay
|
||||
unsigned int conns=reader.readI();
|
||||
ds.patchbay.reserve(conns);
|
||||
if (conns>0) ds.patchbay.reserve(conns);
|
||||
for (unsigned int i=0; i<conns; i++) {
|
||||
ds.patchbay.push_back((unsigned int)reader.readI());
|
||||
}
|
||||
|
|
@ -3008,6 +3026,25 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// C64 1Exy compat
|
||||
if (ds.version<186) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_C64_8580 || ds.system[i]==DIV_SYSTEM_C64_6581) {
|
||||
ds.systemFlags[i].set("no1EUpdate",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// C64 original reset time and multiply relative
|
||||
if (ds.version<187) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_C64_8580 || ds.system[i]==DIV_SYSTEM_C64_6581) {
|
||||
ds.systemFlags[i].set("initResetTime",1);
|
||||
ds.systemFlags[i].set("multiplyRel",true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (active) quitDispatch();
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
|
|
@ -3149,8 +3186,8 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
ds.sampleLen=ds.sample.size();
|
||||
|
||||
// orders
|
||||
ds.subsong[0]->ordersLen=ordCount=reader.readC();
|
||||
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>127) {
|
||||
ds.subsong[0]->ordersLen=ordCount=(unsigned char)reader.readC();
|
||||
if (ds.subsong[0]->ordersLen<1 || ds.subsong[0]->ordersLen>128) {
|
||||
logD("invalid order count!");
|
||||
throw EndOfFileException(&reader,reader.tell());
|
||||
}
|
||||
|
|
@ -6052,21 +6089,41 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
}
|
||||
}
|
||||
} else { // STD
|
||||
bool volIsCutoff=false;
|
||||
|
||||
if (sys!=DIV_SYSTEM_GB) {
|
||||
int realVolMacroLen=i->std.volMacro.len;
|
||||
if (realVolMacroLen>127) realVolMacroLen=127;
|
||||
w->writeC(realVolMacroLen);
|
||||
if ((sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) && i->c64.volIsCutoff) {
|
||||
for (int j=0; j<realVolMacroLen; j++) {
|
||||
w->writeI(i->std.volMacro.val[j]+18);
|
||||
if (sys==DIV_SYSTEM_C64_6581 || sys==DIV_SYSTEM_C64_8580) {
|
||||
if (i->std.algMacro.len>0) volIsCutoff=true;
|
||||
if (volIsCutoff) {
|
||||
if (i->std.volMacro.len>0) {
|
||||
addWarning(".dmf only supports volume or cutoff macro in C64, but not both. volume macro will be lost.");
|
||||
}
|
||||
realVolMacroLen=i->std.algMacro.len;
|
||||
if (realVolMacroLen>127) realVolMacroLen=127;
|
||||
w->writeC(realVolMacroLen);
|
||||
for (int j=0; j<realVolMacroLen; j++) {
|
||||
w->writeI((-i->std.algMacro.val[j])+18);
|
||||
}
|
||||
} else {
|
||||
w->writeC(realVolMacroLen);
|
||||
for (int j=0; j<realVolMacroLen; j++) {
|
||||
w->writeI(i->std.volMacro.val[j]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
w->writeC(realVolMacroLen);
|
||||
for (int j=0; j<realVolMacroLen; j++) {
|
||||
w->writeI(i->std.volMacro.val[j]);
|
||||
}
|
||||
}
|
||||
if (realVolMacroLen>0) {
|
||||
w->writeC(i->std.volMacro.loop);
|
||||
if (volIsCutoff) {
|
||||
w->writeC(i->std.algMacro.loop);
|
||||
} else {
|
||||
w->writeC(i->std.volMacro.loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6157,7 +6214,7 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
|
|||
w->writeC(i->c64.oscSync);
|
||||
|
||||
w->writeC(i->c64.toFilter);
|
||||
w->writeC(i->c64.volIsCutoff);
|
||||
w->writeC(volIsCutoff);
|
||||
w->writeC(i->c64.initFilter);
|
||||
|
||||
w->writeC(i->c64.res);
|
||||
|
|
|
|||
|
|
@ -349,6 +349,8 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
}
|
||||
|
||||
if (ins->type==DIV_INS_C64) {
|
||||
bool volIsCutoff=false;
|
||||
|
||||
ins->c64.triOn=reader.readC();
|
||||
ins->c64.sawOn=reader.readC();
|
||||
ins->c64.pulseOn=reader.readC();
|
||||
|
|
@ -365,9 +367,9 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
ins->c64.oscSync=reader.readC();
|
||||
ins->c64.toFilter=reader.readC();
|
||||
if (version<0x07) { // TODO: UNSURE
|
||||
ins->c64.volIsCutoff=reader.readI();
|
||||
volIsCutoff=reader.readI();
|
||||
} else {
|
||||
ins->c64.volIsCutoff=reader.readC();
|
||||
volIsCutoff=reader.readC();
|
||||
}
|
||||
ins->c64.initFilter=reader.readC();
|
||||
|
||||
|
|
@ -379,10 +381,16 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
ins->c64.ch3off=reader.readC();
|
||||
|
||||
// weird storage
|
||||
if (ins->c64.volIsCutoff) {
|
||||
for (int j=0; j<ins->std.volMacro.len; j++) {
|
||||
ins->std.volMacro.val[j]-=18;
|
||||
if (volIsCutoff) {
|
||||
// move to alg (new cutoff)
|
||||
ins->std.algMacro.len=ins->std.volMacro.len;
|
||||
ins->std.algMacro.loop=ins->std.volMacro.loop;
|
||||
ins->std.algMacro.rel=ins->std.volMacro.rel;
|
||||
for (int j=0; j<ins->std.algMacro.len; j++) {
|
||||
ins->std.algMacro.val[j]=-(ins->std.volMacro.val[j]-18);
|
||||
}
|
||||
ins->std.volMacro.len=0;
|
||||
memset(ins->std.volMacro.val,0,256*sizeof(int));
|
||||
}
|
||||
for (int j=0; j<ins->std.dutyMacro.len; j++) {
|
||||
ins->std.dutyMacro.val[j]-=12;
|
||||
|
|
|
|||
|
|
@ -101,7 +101,6 @@ bool DivInstrumentC64::operator==(const DivInstrumentC64& other) {
|
|||
_C(ringMod) &&
|
||||
_C(oscSync) &&
|
||||
_C(toFilter) &&
|
||||
_C(volIsCutoff) &&
|
||||
_C(initFilter) &&
|
||||
_C(dutyIsAbs) &&
|
||||
_C(filterIsAbs) &&
|
||||
|
|
@ -196,7 +195,10 @@ bool DivInstrumentWaveSynth::operator==(const DivInstrumentWaveSynth& other) {
|
|||
}
|
||||
|
||||
bool DivInstrumentSoundUnit::operator==(const DivInstrumentSoundUnit& other) {
|
||||
return _C(switchRoles);
|
||||
return (
|
||||
_C(switchRoles) &&
|
||||
_C(hwSeqLen)
|
||||
);
|
||||
}
|
||||
|
||||
bool DivInstrumentES5506::operator==(const DivInstrumentES5506& other) {
|
||||
|
|
@ -388,7 +390,6 @@ void DivInstrument::writeFeature64(SafeWriter* w) {
|
|||
w->writeC(
|
||||
(c64.dutyIsAbs?0x80:0)|
|
||||
(c64.initFilter?0x40:0)|
|
||||
(c64.volIsCutoff?0x20:0)|
|
||||
(c64.toFilter?0x10:0)|
|
||||
(c64.noiseOn?8:0)|
|
||||
(c64.pulseOn?4:0)|
|
||||
|
|
@ -691,6 +692,14 @@ void DivInstrument::writeFeatureSU(SafeWriter* w) {
|
|||
|
||||
w->writeC(su.switchRoles);
|
||||
|
||||
w->writeC(su.hwSeqLen);
|
||||
for (int i=0; i<su.hwSeqLen; i++) {
|
||||
w->writeC(su.hwSeq[i].cmd);
|
||||
w->writeC(su.hwSeq[i].bound);
|
||||
w->writeC(su.hwSeq[i].val);
|
||||
w->writeS(su.hwSeq[i].speed);
|
||||
}
|
||||
|
||||
FEATURE_END;
|
||||
}
|
||||
|
||||
|
|
@ -1312,7 +1321,7 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
w->writeC(c64.oscSync);
|
||||
w->writeC(c64.toFilter);
|
||||
w->writeC(c64.initFilter);
|
||||
w->writeC(c64.volIsCutoff);
|
||||
w->writeC(0); // this was volIsCutoff
|
||||
w->writeC(c64.res);
|
||||
w->writeC(c64.lp);
|
||||
w->writeC(c64.bp);
|
||||
|
|
@ -2083,13 +2092,13 @@ void DivInstrument::readFeatureMA(SafeReader& reader, short version) {
|
|||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
void DivInstrument::readFeature64(SafeReader& reader, short version) {
|
||||
void DivInstrument::readFeature64(SafeReader& reader, bool& volIsCutoff, short version) {
|
||||
READ_FEAT_BEGIN;
|
||||
|
||||
unsigned char next=reader.readC();
|
||||
c64.dutyIsAbs=next&128;
|
||||
c64.initFilter=next&64;
|
||||
c64.volIsCutoff=next&32;
|
||||
volIsCutoff=next&32;
|
||||
c64.toFilter=next&16;
|
||||
c64.noiseOn=next&8;
|
||||
c64.pulseOn=next&4;
|
||||
|
|
@ -2536,6 +2545,16 @@ void DivInstrument::readFeatureSU(SafeReader& reader, short version) {
|
|||
|
||||
su.switchRoles=reader.readC();
|
||||
|
||||
if (version>=185) {
|
||||
su.hwSeqLen=reader.readC();
|
||||
for (int i=0; i<su.hwSeqLen; i++) {
|
||||
su.hwSeq[i].cmd=reader.readC();
|
||||
su.hwSeq[i].bound=reader.readC();
|
||||
su.hwSeq[i].val=reader.readC();
|
||||
su.hwSeq[i].speed=reader.readS();
|
||||
}
|
||||
}
|
||||
|
||||
READ_FEAT_END;
|
||||
}
|
||||
|
||||
|
|
@ -2581,6 +2600,7 @@ void DivInstrument::readFeatureNE(SafeReader& reader, short version) {
|
|||
|
||||
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
|
||||
unsigned char featCode[2];
|
||||
bool volIsCutoff=false;
|
||||
|
||||
int dataLen=reader.size()-4;
|
||||
if (!fui) {
|
||||
|
|
@ -2609,7 +2629,7 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
|
|||
} else if (memcmp(featCode,"MA",2)==0) { // macros
|
||||
readFeatureMA(reader,version);
|
||||
} else if (memcmp(featCode,"64",2)==0) { // C64
|
||||
readFeature64(reader,version);
|
||||
readFeature64(reader,volIsCutoff,version);
|
||||
} else if (memcmp(featCode,"GB",2)==0) { // Game Boy
|
||||
readFeatureGB(reader,version);
|
||||
} else if (memcmp(featCode,"SM",2)==0) { // sample
|
||||
|
|
@ -2658,6 +2678,24 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
|
|||
}
|
||||
}
|
||||
|
||||
// <187 C64 cutoff macro compatibility
|
||||
if (type==DIV_INS_C64 && volIsCutoff && version<187) {
|
||||
memcpy(&std.algMacro,&std.volMacro,sizeof(DivInstrumentMacro));
|
||||
std.algMacro.macroType=DIV_MACRO_ALG;
|
||||
std.volMacro=DivInstrumentMacro(DIV_MACRO_VOL,true);
|
||||
|
||||
if (!c64.filterIsAbs) {
|
||||
for (int i=0; i<std.algMacro.len; i++) {
|
||||
std.algMacro.val[i]=-std.algMacro.val[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <187 special/test/gate merge
|
||||
if (type==DIV_INS_C64 && version<187) {
|
||||
convertC64SpecialMacro();
|
||||
}
|
||||
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -2665,6 +2703,7 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
|
|||
for (int macroValPos=0; macroValPos<y; macroValPos++) x[macroValPos]=reader.readI();
|
||||
|
||||
DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
|
||||
bool volIsCutoff=false;
|
||||
reader.readI(); // length. ignored.
|
||||
|
||||
reader.readS(); // format version. ignored.
|
||||
|
|
@ -2747,7 +2786,7 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
|
|||
c64.oscSync=reader.readC();
|
||||
c64.toFilter=reader.readC();
|
||||
c64.initFilter=reader.readC();
|
||||
c64.volIsCutoff=reader.readC();
|
||||
volIsCutoff=reader.readC();
|
||||
c64.res=reader.readC();
|
||||
c64.lp=reader.readC();
|
||||
c64.bp=reader.readC();
|
||||
|
|
@ -2805,7 +2844,7 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
|
|||
}
|
||||
}
|
||||
if (type==DIV_INS_C64 && version<87) {
|
||||
if (c64.volIsCutoff && !c64.filterIsAbs) for (int j=0; j<std.volMacro.len; j++) {
|
||||
if (volIsCutoff && !c64.filterIsAbs) for (int j=0; j<std.volMacro.len; j++) {
|
||||
std.volMacro.val[j]-=18;
|
||||
}
|
||||
if (!c64.dutyIsAbs) for (int j=0; j<std.dutyMacro.len; j++) {
|
||||
|
|
@ -3391,6 +3430,24 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
|
|||
}
|
||||
}
|
||||
|
||||
// <187 C64 cutoff macro compatibility
|
||||
if (type==DIV_INS_C64 && volIsCutoff && version<187) {
|
||||
memcpy(&std.algMacro,&std.volMacro,sizeof(DivInstrumentMacro));
|
||||
std.algMacro.macroType=DIV_MACRO_ALG;
|
||||
std.volMacro=DivInstrumentMacro(DIV_MACRO_VOL,true);
|
||||
|
||||
if (!c64.filterIsAbs) {
|
||||
for (int i=0; i<std.algMacro.len; i++) {
|
||||
std.algMacro.val[i]=-std.algMacro.val[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// <187 special/test/gate merge
|
||||
if (type==DIV_INS_C64 && version<187) {
|
||||
convertC64SpecialMacro();
|
||||
}
|
||||
|
||||
return DIV_DATA_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -3420,6 +3477,42 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version, DivS
|
|||
return readInsDataOld(reader,version);
|
||||
}
|
||||
|
||||
void DivInstrument::convertC64SpecialMacro() {
|
||||
// merge special and test/gate macros into new special macro
|
||||
int maxLen=MAX(std.ex3Macro.len,std.ex4Macro.len);
|
||||
|
||||
// skip if ex4 is not a sequence macro
|
||||
if (std.ex4Macro.open&6) return;
|
||||
|
||||
// move ex4 macro up and fill in gate
|
||||
for (int i=0; i<std.ex4Macro.len; i++) {
|
||||
std.ex4Macro.val[i]=(std.ex4Macro.val[i]&1)?9:1;
|
||||
}
|
||||
|
||||
// merge ex3 into ex4 if viable to
|
||||
if (std.ex3Macro.len>0 && !(std.ex3Macro.open&6)) {
|
||||
if (std.ex4Macro.len>0 && std.ex4Macro.len<maxLen) {
|
||||
for (int i=std.ex4Macro.len; i<maxLen; i++) {
|
||||
std.ex4Macro.val[i]=std.ex3Macro.val[std.ex4Macro.len-1];
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<maxLen; i++) {
|
||||
std.ex4Macro.val[i]=1;
|
||||
}
|
||||
}
|
||||
for (int i=0; i<maxLen; i++) {
|
||||
if (i>=std.ex3Macro.len) {
|
||||
std.ex4Macro.val[i]|=(std.ex3Macro.val[std.ex3Macro.len-1]&3)<<1;
|
||||
} else {
|
||||
std.ex4Macro.val[i]|=(std.ex3Macro.val[i]&3)<<1;
|
||||
}
|
||||
}
|
||||
}
|
||||
std.ex4Macro.len=maxLen;
|
||||
|
||||
std.ex3Macro=DivInstrumentMacro(DIV_MACRO_EX3);
|
||||
}
|
||||
|
||||
bool DivInstrument::save(const char* path, bool oldFormat, DivSong* song, bool writeInsName) {
|
||||
SafeWriter* w=new SafeWriter();
|
||||
w->init();
|
||||
|
|
@ -3611,7 +3704,7 @@ bool DivInstrument::saveDMP(const char* path) {
|
|||
w->writeC(c64.ringMod);
|
||||
w->writeC(c64.oscSync);
|
||||
w->writeC(c64.toFilter);
|
||||
w->writeC(c64.volIsCutoff);
|
||||
w->writeC(0); // this was volIsCutoff...
|
||||
w->writeC(c64.initFilter);
|
||||
w->writeC(c64.res);
|
||||
w->writeC((c64.cut*100)/2047);
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ struct DivInstrumentGB {
|
|||
|
||||
DIV_GB_HWCMD_MAX
|
||||
};
|
||||
struct HWSeqCommand {
|
||||
struct HWSeqCommandGB {
|
||||
unsigned char cmd;
|
||||
unsigned short data;
|
||||
} hwSeq[256];
|
||||
|
|
@ -403,7 +403,7 @@ struct DivInstrumentGB {
|
|||
hwSeqLen(0),
|
||||
softEnv(false),
|
||||
alwaysInit(false) {
|
||||
memset(hwSeq,0,256*sizeof(int));
|
||||
memset(hwSeq,0,256*sizeof(HWSeqCommandGB));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -412,7 +412,7 @@ struct DivInstrumentC64 {
|
|||
unsigned char a, d, s, r;
|
||||
unsigned short duty;
|
||||
unsigned char ringMod, oscSync;
|
||||
bool toFilter, volIsCutoff, initFilter, dutyIsAbs, filterIsAbs, noTest;
|
||||
bool toFilter, initFilter, dutyIsAbs, filterIsAbs, noTest;
|
||||
unsigned char res;
|
||||
unsigned short cut;
|
||||
bool hp, lp, bp, ch3off;
|
||||
|
|
@ -435,7 +435,6 @@ struct DivInstrumentC64 {
|
|||
ringMod(0),
|
||||
oscSync(0),
|
||||
toFilter(false),
|
||||
volIsCutoff(false),
|
||||
initFilter(false),
|
||||
dutyIsAbs(false),
|
||||
filterIsAbs(false),
|
||||
|
|
@ -663,6 +662,25 @@ struct DivInstrumentWaveSynth {
|
|||
|
||||
struct DivInstrumentSoundUnit {
|
||||
bool switchRoles;
|
||||
unsigned char hwSeqLen;
|
||||
enum HWSeqCommands: unsigned char {
|
||||
DIV_SU_HWCMD_VOL=0,
|
||||
DIV_SU_HWCMD_PITCH,
|
||||
DIV_SU_HWCMD_CUT,
|
||||
DIV_SU_HWCMD_WAIT,
|
||||
DIV_SU_HWCMD_WAIT_REL,
|
||||
DIV_SU_HWCMD_LOOP,
|
||||
DIV_SU_HWCMD_LOOP_REL,
|
||||
|
||||
DIV_SU_HWCMD_MAX
|
||||
};
|
||||
struct HWSeqCommandSU {
|
||||
unsigned char cmd;
|
||||
unsigned char bound;
|
||||
unsigned char val;
|
||||
unsigned short speed;
|
||||
unsigned short padding;
|
||||
} hwSeq[256];
|
||||
|
||||
bool operator==(const DivInstrumentSoundUnit& other);
|
||||
bool operator!=(const DivInstrumentSoundUnit& other) {
|
||||
|
|
@ -670,7 +688,10 @@ struct DivInstrumentSoundUnit {
|
|||
}
|
||||
|
||||
DivInstrumentSoundUnit():
|
||||
switchRoles(false) {}
|
||||
switchRoles(false),
|
||||
hwSeqLen(0) {
|
||||
memset(hwSeq,0,256*sizeof(HWSeqCommandSU));
|
||||
}
|
||||
};
|
||||
|
||||
struct DivInstrumentES5506 {
|
||||
|
|
@ -794,7 +815,7 @@ struct DivInstrument {
|
|||
void readFeatureNA(SafeReader& reader, short version);
|
||||
void readFeatureFM(SafeReader& reader, short version);
|
||||
void readFeatureMA(SafeReader& reader, short version);
|
||||
void readFeature64(SafeReader& reader, short version);
|
||||
void readFeature64(SafeReader& reader, bool& volIsCutoff, short version);
|
||||
void readFeatureGB(SafeReader& reader, short version);
|
||||
void readFeatureSM(SafeReader& reader, short version);
|
||||
void readFeatureOx(SafeReader& reader, int op, short version);
|
||||
|
|
@ -813,6 +834,8 @@ struct DivInstrument {
|
|||
|
||||
DivDataErrors readInsDataOld(SafeReader& reader, short version);
|
||||
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);
|
||||
|
||||
void convertC64SpecialMacro();
|
||||
|
||||
/**
|
||||
* save the instrument to a SafeWriter.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "c64.h"
|
||||
#include "../engine.h"
|
||||
#include "sound/c64_fp/siddefs-fp.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include <math.h>
|
||||
#include "../../ta-log.h"
|
||||
|
||||
|
|
@ -158,21 +159,10 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
|
||||
if (ins->c64.volIsCutoff) {
|
||||
if (ins->c64.filterIsAbs) {
|
||||
filtCut=MIN(2047,chan[i].std.vol.val);
|
||||
} else {
|
||||
filtCut-=((signed char)chan[i].std.vol.val)*7;
|
||||
if (filtCut>2047) filtCut=2047;
|
||||
if (filtCut<0) filtCut=0;
|
||||
}
|
||||
willUpdateFilter=true;
|
||||
} else {
|
||||
vol=MIN(15,chan[i].std.vol.val);
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
vol=MIN(15,chan[i].std.vol.val);
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
|
|
@ -186,14 +176,18 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
if (ins->c64.dutyIsAbs) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
} else {
|
||||
chan[i].duty-=((signed char)chan[i].std.duty.val)*4;
|
||||
if (multiplyRel) {
|
||||
chan[i].duty-=((signed char)chan[i].std.duty.val)*4;
|
||||
} else {
|
||||
chan[i].duty-=chan[i].std.duty.val;
|
||||
}
|
||||
}
|
||||
rWrite(i*7+2,chan[i].duty&0xff);
|
||||
rWrite(i*7+3,chan[i].duty>>8);
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
chan[i].wave=chan[i].std.wave.val;
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active && chan[i].gate));
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
|
|
@ -204,6 +198,21 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.alg.had) { // new cutoff macro
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_C64);
|
||||
if (ins->c64.filterIsAbs) {
|
||||
filtCut=MIN(2047,chan[i].std.alg.val);
|
||||
} else {
|
||||
if (multiplyRel) {
|
||||
filtCut+=((signed char)chan[i].std.alg.val)*7;
|
||||
} else {
|
||||
filtCut+=chan[i].std.alg.val;
|
||||
}
|
||||
if (filtCut>2047) filtCut=2047;
|
||||
if (filtCut<0) filtCut=0;
|
||||
}
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
if (chan[i].std.ex1.had) {
|
||||
filtControl=chan[i].std.ex1.val&15;
|
||||
willUpdateFilter=true;
|
||||
|
|
@ -212,15 +221,33 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
filtRes=chan[i].std.ex2.val&15;
|
||||
willUpdateFilter=true;
|
||||
}
|
||||
if (chan[i].std.ex3.had) {
|
||||
chan[i].sync=chan[i].std.ex3.val&1;
|
||||
chan[i].ring=chan[i].std.ex3.val&2;
|
||||
chan[i].freqChanged=true;
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
|
||||
}
|
||||
if (chan[i].std.ex4.had) {
|
||||
chan[i].test=chan[i].std.ex4.val&1;
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active));
|
||||
chan[i].gate=chan[i].std.ex4.val&1;
|
||||
chan[i].sync=chan[i].std.ex4.val&2;
|
||||
chan[i].ring=chan[i].std.ex4.val&4;
|
||||
chan[i].test=chan[i].std.ex4.val&8;
|
||||
chan[i].freqChanged=true;
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(int)(chan[i].active && chan[i].gate));
|
||||
}
|
||||
|
||||
if (chan[i].std.ex5.had) {
|
||||
chan[i].attack=chan[i].std.ex5.val&15;
|
||||
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
|
||||
}
|
||||
|
||||
if (chan[i].std.ex6.had) {
|
||||
chan[i].decay=chan[i].std.ex6.val&15;
|
||||
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
|
||||
}
|
||||
|
||||
if (chan[i].std.ex7.had) {
|
||||
chan[i].sustain=chan[i].std.ex7.val&15;
|
||||
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
|
||||
}
|
||||
|
||||
if (chan[i].std.ex8.had) {
|
||||
chan[i].release=chan[i].std.ex8.val&15;
|
||||
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
|
||||
}
|
||||
|
||||
if (sysTick) {
|
||||
|
|
@ -243,7 +270,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
if (chan[i].keyOn) {
|
||||
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
|
||||
rWrite(i*7+6,(chan[i].sustain<<4)|(chan[i].release));
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|1);
|
||||
rWrite(i*7+4,(chan[i].wave<<4)|(chan[i].test<<3)|(chan[i].ring<<2)|(chan[i].sync<<1)|(chan[i].gate?1:0));
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
|
||||
|
|
@ -387,7 +414,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
|
||||
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active && chan[c.chan].gate));
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
|
|
@ -396,7 +423,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta || !chan[c.chan].inPorta) {
|
||||
if (parent->song.resetMacroOnPorta || parent->song.preNoteNoEffect) {
|
||||
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_C64));
|
||||
chan[c.chan].keyOn=true;
|
||||
}
|
||||
|
|
@ -457,23 +484,35 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
switch (c.value>>4) {
|
||||
case 0:
|
||||
chan[c.chan].attack=c.value&15;
|
||||
if (!no1EUpdate) {
|
||||
rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay));
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
chan[c.chan].decay=c.value&15;
|
||||
if (!no1EUpdate) {
|
||||
rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay));
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
chan[c.chan].sustain=c.value&15;
|
||||
if (!no1EUpdate) {
|
||||
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
chan[c.chan].release=c.value&15;
|
||||
if (!no1EUpdate) {
|
||||
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
chan[c.chan].ring=c.value;
|
||||
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
|
||||
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active && chan[c.chan].gate));
|
||||
break;
|
||||
case 5:
|
||||
chan[c.chan].sync=c.value;
|
||||
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active));
|
||||
rWrite(c.chan*7+4,(chan[c.chan].wave<<4)|(chan[c.chan].test<<3)|(chan[c.chan].ring<<2)|(chan[c.chan].sync<<1)|(int)(chan[c.chan].active && chan[c.chan].gate));
|
||||
break;
|
||||
case 6:
|
||||
filtControl&=7;
|
||||
|
|
@ -481,6 +520,16 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_C64_AD:
|
||||
chan[c.chan].attack=c.value>>4;
|
||||
chan[c.chan].decay=c.value&15;
|
||||
rWrite(c.chan*7+5,(chan[c.chan].attack<<4)|(chan[c.chan].decay));
|
||||
break;
|
||||
case DIV_CMD_C64_SR:
|
||||
chan[c.chan].sustain=c.value>>4;
|
||||
chan[c.chan].release=c.value&15;
|
||||
rWrite(c.chan*7+6,(chan[c.chan].sustain<<4)|(chan[c.chan].release));
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
|
|
@ -546,6 +595,24 @@ DivMacroInt* DivPlatformC64::getChanMacroInt(int ch) {
|
|||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformC64::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
ret.count=1;
|
||||
ret.hint[0]=ICON_FA_BELL_SLASH_O;
|
||||
ret.type[0]=0;
|
||||
if (ch==2 && (filtControl&8)) {
|
||||
ret.type[0]=7;
|
||||
} else if (chan[ch].test && !chan[ch].gate) {
|
||||
ret.type[0]=5;
|
||||
} else if (chan[ch].test) {
|
||||
ret.type[0]=6;
|
||||
} else if (!chan[ch].gate) {
|
||||
ret.type[0]=4;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformC64::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
|
@ -594,7 +661,18 @@ void DivPlatformC64::reset() {
|
|||
needInitTables=false;
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->reset();
|
||||
sid_fp->clockSilent(16000);
|
||||
for (int i=0; i<3; i++) {
|
||||
sid_fp->write(i*7+5,testAD);
|
||||
sid_fp->write(i*7+6,testSR);
|
||||
sid_fp->write(i*7+4,8);
|
||||
}
|
||||
sid_fp->clockSilent(30000);
|
||||
for (int i=0; i<3; i++) {
|
||||
sid_fp->write(i*7+5,testAD);
|
||||
sid_fp->write(i*7+6,testSR);
|
||||
sid_fp->write(i*7+4,0);
|
||||
}
|
||||
sid_fp->clockSilent(30000);
|
||||
} else {
|
||||
sid->reset();
|
||||
}
|
||||
|
|
@ -605,7 +683,7 @@ void DivPlatformC64::reset() {
|
|||
filtControl=7;
|
||||
filtRes=0;
|
||||
filtCut=2047;
|
||||
resetTime=1;
|
||||
resetTime=initResetTime;
|
||||
vol=15;
|
||||
|
||||
chanOrder[0]=0;
|
||||
|
|
@ -652,8 +730,12 @@ void DivPlatformC64::setFlags(const DivConfig& flags) {
|
|||
if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
|
||||
}
|
||||
keyPriority=flags.getBool("keyPriority",true);
|
||||
no1EUpdate=flags.getBool("no1EUpdate",false);
|
||||
multiplyRel=flags.getBool("multiplyRel",false);
|
||||
testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15);
|
||||
testSR=((flags.getInt("testSustain",0)&15)<<4)|(flags.getInt("testRelease",0)&15);
|
||||
initResetTime=flags.getInt("initResetTime",2);
|
||||
if (initResetTime<0) initResetTime=1;
|
||||
|
||||
// init fake filter table
|
||||
// taken from dSID
|
||||
|
|
|
|||
|
|
@ -26,13 +26,17 @@
|
|||
#include "sound/c64_fp/SID.h"
|
||||
#include "sound/c64_d/dsid.h"
|
||||
|
||||
// TODO:
|
||||
// - ex3 (special) unify with ex4 (gate/test)
|
||||
// - ex4 (test) compatibility
|
||||
|
||||
class DivPlatformC64: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
int prevFreq, testWhen;
|
||||
unsigned char sweep, wave, attack, decay, sustain, release;
|
||||
short duty;
|
||||
bool sweepChanged, filter;
|
||||
bool resetMask, resetFilter, resetDuty, ring, sync, test;
|
||||
bool resetMask, resetFilter, resetDuty, gate, ring, sync, test;
|
||||
Channel():
|
||||
SharedChannel<signed char>(15),
|
||||
prevFreq(65535),
|
||||
|
|
@ -49,6 +53,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
resetMask(false),
|
||||
resetFilter(false),
|
||||
resetDuty(false),
|
||||
gate(true),
|
||||
ring(false),
|
||||
sync(false),
|
||||
test(false) {}
|
||||
|
|
@ -70,9 +75,9 @@ class DivPlatformC64: public DivDispatch {
|
|||
unsigned char filtControl, filtRes, vol;
|
||||
unsigned char writeOscBuf;
|
||||
unsigned char sidCore;
|
||||
int filtCut, resetTime;
|
||||
int filtCut, resetTime, initResetTime;
|
||||
|
||||
bool keyPriority, sidIs6581, needInitTables;
|
||||
bool keyPriority, sidIs6581, needInitTables, no1EUpdate, multiplyRel;
|
||||
unsigned char chanOrder[3];
|
||||
unsigned char testAD, testSR;
|
||||
|
||||
|
|
@ -108,6 +113,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
bool isVolGlobal();
|
||||
float getPostAmp();
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
|
|
|
|||
|
|
@ -191,22 +191,22 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
const int nextVol=VOL_SCALE_LOG((0xfff*chan[i].vol)/0xff,(0xfff*chan[i].std.vol.val)/chan[i].volMacroMax,0xfff);
|
||||
if (chan[i].outVol!=nextVol) {
|
||||
chan[i].outVol=nextVol;
|
||||
chan[i].volChanged.lVol=1;
|
||||
chan[i].volChanged.rVol=1;
|
||||
chan[i].volChangedES.lVol=1;
|
||||
chan[i].volChangedES.rVol=1;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.panL.had) {
|
||||
const int nextLVol=VOL_SCALE_LOG((0xfff*chan[i].lVol)/0xff,(0xfff*chan[i].std.panL.val)/chan[i].panMacroMax,0xfff);
|
||||
if (chan[i].outLVol!=nextLVol) {
|
||||
chan[i].outLVol=nextLVol;
|
||||
chan[i].volChanged.lVol=1;
|
||||
chan[i].volChangedES.lVol=1;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.panR.had) {
|
||||
const int nextRVol=VOL_SCALE_LOG((0xfff*chan[i].rVol)/0xff,(0xfff*chan[i].std.panR.val)/chan[i].panMacroMax,0xfff);
|
||||
if (chan[i].outRVol!=nextRVol) {
|
||||
chan[i].outRVol=nextRVol;
|
||||
chan[i].volChanged.rVol=1;
|
||||
chan[i].volChangedES.rVol=1;
|
||||
}
|
||||
}
|
||||
// arpeggio/pitch macros, frequency related
|
||||
|
|
@ -340,7 +340,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
if (chan[i].ca!=ca) {
|
||||
chan[i].ca=ca;
|
||||
if (!chan[i].keyOn) {
|
||||
chan[i].volChanged.ca=1;
|
||||
chan[i].volChangedES.ca=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -360,9 +360,9 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
// update registers
|
||||
if (chan[i].volChanged.changed) {
|
||||
if (chan[i].volChangedES.changed) {
|
||||
// calculate volume (16 bit)
|
||||
if (chan[i].volChanged.lVol) {
|
||||
if (chan[i].volChangedES.lVol) {
|
||||
chan[i].resLVol=VOL_SCALE_LOG(chan[i].outVol,chan[i].outLVol,0xfff);
|
||||
chan[i].resLVol-=volScale;
|
||||
if (chan[i].resLVol<0) chan[i].resLVol=0;
|
||||
|
|
@ -371,7 +371,7 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
pageWrite(0x00|i,0x02,chan[i].resLVol);
|
||||
}
|
||||
}
|
||||
if (chan[i].volChanged.rVol) {
|
||||
if (chan[i].volChangedES.rVol) {
|
||||
chan[i].resRVol=VOL_SCALE_LOG(chan[i].outVol,chan[i].outRVol,0xfff);
|
||||
chan[i].resRVol-=volScale;
|
||||
if (chan[i].resRVol<0) chan[i].resRVol=0;
|
||||
|
|
@ -380,10 +380,10 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
pageWrite(0x00|i,0x04,chan[i].resRVol);
|
||||
}
|
||||
}
|
||||
if (chan[i].volChanged.ca) {
|
||||
if (chan[i].volChangedES.ca) {
|
||||
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].ca<<10),0x1c00);
|
||||
}
|
||||
chan[i].volChanged.changed=0;
|
||||
chan[i].volChangedES.changed=0;
|
||||
}
|
||||
if (chan[i].pcmChanged.changed) {
|
||||
if (chan[i].pcmChanged.index) {
|
||||
|
|
@ -750,7 +750,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].pcmChanged.changed=0xff;
|
||||
chan[c.chan].noteChanged.changed=0xff;
|
||||
chan[c.chan].volChanged.changed=0xff;
|
||||
chan[c.chan].volChangedES.changed=0xff;
|
||||
}
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=(0xfff*chan[c.chan].vol)/0xff;
|
||||
|
|
@ -787,7 +787,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=(0xfff*c.value)/0xff;
|
||||
chan[c.chan].volChanged.changed=0xff;
|
||||
chan[c.chan].volChangedES.changed=0xff;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -797,14 +797,14 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
case DIV_CMD_PANNING: {
|
||||
if (chan[c.chan].ca!=0) {
|
||||
chan[c.chan].ca=0;
|
||||
chan[c.chan].volChanged.ca=1;
|
||||
chan[c.chan].volChangedES.ca=1;
|
||||
}
|
||||
// Left volume
|
||||
if (chan[c.chan].lVol!=c.value) {
|
||||
chan[c.chan].lVol=c.value;
|
||||
if (!chan[c.chan].std.panL.has) {
|
||||
chan[c.chan].outLVol=(0xfff*c.value)/0xff;
|
||||
chan[c.chan].volChanged.lVol=1;
|
||||
chan[c.chan].volChangedES.lVol=1;
|
||||
}
|
||||
}
|
||||
// Right volume
|
||||
|
|
@ -812,7 +812,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].rVol=c.value2;
|
||||
if (!chan[c.chan].std.panR.has) {
|
||||
chan[c.chan].outRVol=(0xfff*c.value2)/0xff;
|
||||
chan[c.chan].volChanged.rVol=1;
|
||||
chan[c.chan].volChangedES.rVol=1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -821,7 +821,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
unsigned char ca=CLAMP(c.value>>1,0,5);
|
||||
if (chan[c.chan].ca!=ca) {
|
||||
chan[c.chan].ca=ca;
|
||||
chan[c.chan].volChanged.ca=1;
|
||||
chan[c.chan].volChangedES.ca=1;
|
||||
}
|
||||
if ((c.value&1)==0) {
|
||||
// Left volume
|
||||
|
|
@ -829,7 +829,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].lVol=c.value2;
|
||||
if (!chan[c.chan].std.panL.has) {
|
||||
chan[c.chan].outLVol=(0xfff*c.value2)/0xff;
|
||||
chan[c.chan].volChanged.lVol=1;
|
||||
chan[c.chan].volChangedES.lVol=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -839,7 +839,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
chan[c.chan].rVol=c.value2;
|
||||
if (!chan[c.chan].std.panR.has) {
|
||||
chan[c.chan].outRVol=(0xfff*c.value2)/0xff;
|
||||
chan[c.chan].volChanged.rVol=1;
|
||||
chan[c.chan].volChangedES.rVol=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1041,7 +1041,7 @@ void DivPlatformES5506::forceIns() {
|
|||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].noteChanged.changed=0xff;
|
||||
chan[i].volChanged.changed=0xff;
|
||||
chan[i].volChangedES.changed=0xff;
|
||||
chan[i].filterChanged.changed=0xff;
|
||||
chan[i].envChanged.changed=0xff;
|
||||
chan[i].pcmChanged.changed=0xff;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
|
||||
VolChanged() :
|
||||
changed(0) {}
|
||||
} volChanged;
|
||||
} volChangedES;
|
||||
|
||||
struct FilterChanged { // Filter changed flags
|
||||
union { // pack flag bits in single byte
|
||||
|
|
@ -198,7 +198,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
isReverseLoop(false),
|
||||
cr(0),
|
||||
noteChanged(NoteChanged()),
|
||||
volChanged(VolChanged()),
|
||||
volChangedES(VolChanged()),
|
||||
filterChanged(FilterChanged()),
|
||||
envChanged(EnvChanged()),
|
||||
pcmChanged(PCMChanged()),
|
||||
|
|
|
|||
|
|
@ -800,7 +800,7 @@ DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) {
|
|||
}
|
||||
|
||||
unsigned short DivPlatformGenesisExt::getPan(int ch) {
|
||||
if (ch==csmChan) return 0;
|
||||
if (ch==4+csmChan) return 0;
|
||||
if (ch>=4+extChanOffs) return DivPlatformGenesis::getPan(ch-3);
|
||||
if (ch>=extChanOffs) {
|
||||
if (extMode) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "pce.h"
|
||||
#include "../engine.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
|
||||
//#define rWrite(a,v) pendingWrites[a]=v;
|
||||
|
|
@ -512,6 +513,18 @@ unsigned short DivPlatformPCE::getPan(int ch) {
|
|||
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformPCE::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
if (ch<4) return ret;
|
||||
ret.count=1;
|
||||
ret.hint[0]=ICON_FUR_NOISE;
|
||||
ret.type[0]=0;
|
||||
|
||||
if (chan[ch].noise) ret.type[0]=4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DivSamplePos DivPlatformPCE::getSamplePos(int ch) {
|
||||
if (ch>=6) return DivSamplePos();
|
||||
if (!chan[ch].pcm) return DivSamplePos();
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ class DivPlatformPCE: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "snes.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include "furIcons.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_FREQBASE 131072
|
||||
|
|
@ -710,6 +711,63 @@ DivChannelPair DivPlatformSNES::getPaired(int ch) {
|
|||
return DivChannelPair();
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformSNES::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
ret.count=1;
|
||||
ret.hint[0]="-";
|
||||
ret.type[0]=0;
|
||||
|
||||
const SPC_DSP::voice_t* v=dsp.get_voice(ch);
|
||||
if (v!=NULL) {
|
||||
if (v->regs[5]&128) {
|
||||
switch (v->env_mode) {
|
||||
case SPC_DSP::env_attack:
|
||||
ret.hint[0]=ICON_FUR_ADSR_A;
|
||||
ret.type[0]=12;
|
||||
break;
|
||||
case SPC_DSP::env_decay:
|
||||
ret.hint[0]=ICON_FUR_ADSR_D;
|
||||
ret.type[0]=13;
|
||||
break;
|
||||
case SPC_DSP::env_sustain:
|
||||
ret.hint[0]=ICON_FUR_ADSR_S;
|
||||
ret.type[0]=14;
|
||||
break;
|
||||
case SPC_DSP::env_release:
|
||||
ret.hint[0]=ICON_FUR_ADSR_R;
|
||||
ret.type[0]=15;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (v->regs[7]&128) {
|
||||
switch (v->regs[7]&0x60) {
|
||||
case 0:
|
||||
ret.hint[0]=ICON_FUR_DEC_LINEAR;
|
||||
ret.type[0]=16;
|
||||
break;
|
||||
case 32:
|
||||
ret.hint[0]=ICON_FUR_DEC_EXP;
|
||||
ret.type[0]=17;
|
||||
break;
|
||||
case 64:
|
||||
ret.hint[0]=ICON_FUR_INC_LINEAR;
|
||||
ret.type[0]=18;
|
||||
break;
|
||||
case 96:
|
||||
ret.hint[0]=ICON_FUR_INC_BENT;
|
||||
ret.type[0]=19;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret.hint[0]=ICON_FUR_VOL_DIRECT;
|
||||
ret.type[0]=20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DivSamplePos DivPlatformSNES::getSamplePos(int ch) {
|
||||
if (ch>=8) return DivSamplePos();
|
||||
if (!chan[ch].active) return DivSamplePos();
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelPair getPaired(int chan);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
DivSamplePos getSamplePos(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
|
|
|
|||
|
|
@ -134,6 +134,79 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
}
|
||||
writeControlUpper(i);
|
||||
}
|
||||
|
||||
// run hardware sequence
|
||||
if (chan[i].active) {
|
||||
if (--chan[i].hwSeqDelay<=0) {
|
||||
chan[i].hwSeqDelay=0;
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
int hwSeqCount=0;
|
||||
while (chan[i].hwSeqPos<ins->su.hwSeqLen && hwSeqCount<8) {
|
||||
bool leave=false;
|
||||
unsigned char bound=ins->su.hwSeq[chan[i].hwSeqPos].bound;
|
||||
unsigned char val=ins->su.hwSeq[chan[i].hwSeqPos].val;
|
||||
unsigned short speed=ins->su.hwSeq[chan[i].hwSeqPos].speed;
|
||||
switch (ins->su.hwSeq[chan[i].hwSeqPos].cmd) {
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_VOL:
|
||||
chan[i].volSweepP=speed;
|
||||
chan[i].volSweepV=val;
|
||||
chan[i].volSweepB=bound;
|
||||
chan[i].volSweep=(val>0);
|
||||
chWrite(i,0x14,chan[i].volSweepP&0xff);
|
||||
chWrite(i,0x15,chan[i].volSweepP>>8);
|
||||
chWrite(i,0x16,chan[i].volSweepV);
|
||||
chWrite(i,0x17,chan[i].volSweepB);
|
||||
writeControlUpper(i);
|
||||
break;
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_PITCH:
|
||||
chan[i].freqSweepP=speed;
|
||||
chan[i].freqSweepV=val;
|
||||
chan[i].freqSweepB=bound;
|
||||
chan[i].freqSweep=(val>0);
|
||||
chWrite(i,0x10,chan[i].freqSweepP&0xff);
|
||||
chWrite(i,0x11,chan[i].freqSweepP>>8);
|
||||
chWrite(i,0x12,chan[i].freqSweepV);
|
||||
chWrite(i,0x13,chan[i].freqSweepB);
|
||||
writeControlUpper(i);
|
||||
break;
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_CUT:
|
||||
chan[i].cutSweepP=speed;
|
||||
chan[i].cutSweepV=val;
|
||||
chan[i].cutSweepB=bound;
|
||||
chan[i].cutSweep=(val>0);
|
||||
chWrite(i,0x18,chan[i].cutSweepP&0xff);
|
||||
chWrite(i,0x19,chan[i].cutSweepP>>8);
|
||||
chWrite(i,0x1a,chan[i].cutSweepV);
|
||||
chWrite(i,0x1b,chan[i].cutSweepB);
|
||||
writeControlUpper(i);
|
||||
break;
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT:
|
||||
chan[i].hwSeqDelay=(val+1)*parent->tickMult;
|
||||
leave=true;
|
||||
break;
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_WAIT_REL:
|
||||
if (!chan[i].released) {
|
||||
chan[i].hwSeqPos--;
|
||||
leave=true;
|
||||
}
|
||||
break;
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP:
|
||||
chan[i].hwSeqPos=val-1;
|
||||
break;
|
||||
case DivInstrumentSoundUnit::DIV_SU_HWCMD_LOOP_REL:
|
||||
if (!chan[i].released) {
|
||||
chan[i].hwSeqPos=val-1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
chan[i].hwSeqPos++;
|
||||
if (leave) break;
|
||||
hwSeqCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
|
||||
|
|
@ -160,8 +233,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].pcm) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
int sNum=ins->amiga.getSample(chan[i].note);
|
||||
int sNum=chan[i].sample;
|
||||
DivSample* sample=parent->getSample(sNum);
|
||||
if (sample!=NULL && sNum>=0 && sNum<parent->song.sampleLen) {
|
||||
unsigned int sampleEnd=sampleOffSU[sNum]+(sample->getLoopEndPosition());
|
||||
|
|
@ -220,6 +292,9 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].released=false;
|
||||
chan[c.chan].hwSeqPos=0;
|
||||
chan[c.chan].hwSeqDelay=0;
|
||||
chWrite(c.chan,0x02,chan[c.chan].vol);
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
|
|
@ -231,11 +306,14 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].hwSeqPos=0;
|
||||
chan[c.chan].hwSeqDelay=0;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
chan[c.chan].released=true;
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
|
|
|
|||
|
|
@ -30,12 +30,14 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
signed char pan;
|
||||
unsigned char duty;
|
||||
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
|
||||
bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep;
|
||||
bool pcmLoop, timerSync, freqSweep, volSweep, cutSweep, released;
|
||||
unsigned short freqSweepP, volSweepP, cutSweepP;
|
||||
unsigned char freqSweepB, volSweepB, cutSweepB;
|
||||
unsigned char freqSweepV, volSweepV, cutSweepV;
|
||||
unsigned short syncTimer;
|
||||
signed short wave;
|
||||
unsigned short hwSeqPos;
|
||||
short hwSeqDelay;
|
||||
Channel():
|
||||
SharedChannel<signed char>(127),
|
||||
cutoff(16383),
|
||||
|
|
@ -56,6 +58,7 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
freqSweep(false),
|
||||
volSweep(false),
|
||||
cutSweep(false),
|
||||
released(false),
|
||||
freqSweepP(0),
|
||||
volSweepP(0),
|
||||
cutSweepP(0),
|
||||
|
|
@ -66,7 +69,9 @@ class DivPlatformSoundUnit: public DivDispatch {
|
|||
volSweepV(0),
|
||||
cutSweepV(0),
|
||||
syncTimer(0),
|
||||
wave(0) {}
|
||||
wave(0),
|
||||
hwSeqPos(0),
|
||||
hwSeqDelay(0) {}
|
||||
};
|
||||
Channel chan[8];
|
||||
DivDispatchOscBuffer* oscBuf[8];
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "swan.h"
|
||||
#include "../engine.h"
|
||||
#include "furIcons.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);}}
|
||||
|
|
@ -480,6 +482,30 @@ unsigned short DivPlatformSwan::getPan(int ch) {
|
|||
return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15);
|
||||
}
|
||||
|
||||
DivChannelModeHints DivPlatformSwan::getModeHints(int ch) {
|
||||
DivChannelModeHints ret;
|
||||
|
||||
switch (ch) {
|
||||
case 1: // PCM
|
||||
ret.count=1;
|
||||
ret.hint[0]=ICON_FA_VOLUME_UP;
|
||||
ret.type[0]=pcm?4:0;
|
||||
break;
|
||||
case 2: // sweep
|
||||
ret.count=1;
|
||||
ret.hint[0]=ICON_FUR_SAW;
|
||||
ret.type[0]=sweep?2:0;
|
||||
break;
|
||||
case 3: // noise
|
||||
ret.count=1;
|
||||
ret.hint[0]=ICON_FUR_NOISE;
|
||||
ret.type[0]=noise?4:0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ class DivPlatformSwan: public DivDispatch {
|
|||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
unsigned short getPan(int chan);
|
||||
DivChannelModeHints getModeHints(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
|
|
|
|||
|
|
@ -239,6 +239,9 @@ const char* cmdName[]={
|
|||
|
||||
"EXTERNAL",
|
||||
|
||||
"C64_AD",
|
||||
"C64_SR",
|
||||
|
||||
"ALWAYS_SET_VOLUME"
|
||||
};
|
||||
|
||||
|
|
@ -1263,11 +1266,11 @@ void DivEngine::nextRow() {
|
|||
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (!song.preNoteNoEffect) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x03 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
|
||||
doPreparePreNote=false;
|
||||
break;
|
||||
}
|
||||
if (pat->data[curRow][4+(j<<1)]==0x06) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x06 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
|
||||
doPreparePreNote=false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1294,11 +1297,11 @@ void DivEngine::nextRow() {
|
|||
int addition=0;
|
||||
|
||||
for (int j=0; j<curPat[i].effectCols; j++) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x03) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x03 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
|
||||
doPrepareCut=false;
|
||||
break;
|
||||
}
|
||||
if (pat->data[curRow][4+(j<<1)]==0x06) {
|
||||
if (pat->data[curRow][4+(j<<1)]==0x06 && pat->data[curRow][5+(j<<1)]!=0 && pat->data[curRow][5+(j<<1)]!=-1) {
|
||||
doPrepareCut=false;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -597,7 +597,9 @@ void DivEngine::registerSystems() {
|
|||
{0x1a, {DIV_CMD_C64_RESET_MASK, "1Axx: Disable envelope reset for this channel (1 disables; 0 enables)"}},
|
||||
{0x1b, {DIV_CMD_C64_FILTER_RESET, "1Bxy: Reset cutoff (x: on new note; y: now)"}},
|
||||
{0x1c, {DIV_CMD_C64_DUTY_RESET, "1Cxy: Reset pulse width (x: on new note; y: now)"}},
|
||||
{0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change additional parameters"}},
|
||||
{0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change other parameters (LEGACY)"}},
|
||||
{0x20, {DIV_CMD_C64_AD, "20xy: Set attack/decay (x: attack; y: decay)"}},
|
||||
{0x21, {DIV_CMD_C64_SR, "21xy: Set sustain/release (x: sustain; y: release)"}},
|
||||
};
|
||||
const EffectHandler c64FineDutyHandler(DIV_CMD_C64_FINE_DUTY, "3xxx: Set pulse width (0 to FFF)", effectValLong<12>);
|
||||
const EffectHandler c64FineCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, "4xxx: Set cutoff (0 to 7FF)", effectValLong<11>);
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ void DivEngine::runExportThread() {
|
|||
if (initAudioBackend()) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].setRates(got.rate);
|
||||
disCont[i].setQuality(lowQuality);
|
||||
disCont[i].setQuality(lowQuality,dcHiPass);
|
||||
}
|
||||
if (!output->setRun(true)) {
|
||||
logE("error while activating audio!");
|
||||
|
|
@ -223,7 +223,7 @@ void DivEngine::runExportThread() {
|
|||
if (initAudioBackend()) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].setRates(got.rate);
|
||||
disCont[i].setQuality(lowQuality);
|
||||
disCont[i].setQuality(lowQuality,dcHiPass);
|
||||
}
|
||||
if (!output->setRun(true)) {
|
||||
logE("error while activating audio!");
|
||||
|
|
@ -349,7 +349,7 @@ void DivEngine::runExportThread() {
|
|||
if (initAudioBackend()) {
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].setRates(got.rate);
|
||||
disCont[i].setQuality(lowQuality);
|
||||
disCont[i].setQuality(lowQuality,dcHiPass);
|
||||
}
|
||||
if (!output->setRun(true)) {
|
||||
logE("error while activating audio!");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue