Merge branch 'master' into ZSMv1

This commit is contained in:
ZeroByteOrg 2022-06-06 09:44:57 -05:00
commit 378f6a957b
3651 changed files with 495554 additions and 653 deletions

View file

@ -524,6 +524,11 @@ class DivDispatch {
// this is a special case definition. only use it for f-num/block-based chips.
#define NOTE_FNUM_BLOCK(x,bits) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits)
// this is for volume scaling calculation.
#define VOL_SCALE_LINEAR_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(((x)*(y))/(range)):(CLAMP(((x)+(y))-(range),0,(range))))
#define VOL_SCALE_LINEAR(x,y,range) (((x)*(y))/(range))
#define VOL_SCALE_LOG(x,y,range) ((parent->song.newVolumeScaling)?(CLAMP(((x)+(y))-(range),0,(range))):(((x)*(y))/(range)))
// these are here for convenience.
// it is encouraged to use these, since you get an exact value this way.
// - NTSC colorburst: 3.58MHz

View file

@ -193,6 +193,9 @@ bool DivEngine::isExporting() {
#ifdef HAVE_SNDFILE
void DivEngine::runExportThread() {
size_t fadeOutSamples=got.rate*exportFadeOut;
size_t curFadeOutSample=0;
bool isFadingOut=false;
switch (exportMode) {
case DIV_EXPORT_MODE_ONE: {
SNDFILE* sf;
@ -220,15 +223,33 @@ void DivEngine::runExportThread() {
logI("rendering to file...");
while (playing) {
size_t total=0;
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
for (int i=0; i<EXPORT_BUFSIZE; i++) {
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]));
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]));
}
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
totalProcessed=EXPORT_BUFSIZE;
}
if (sf_writef_float(sf,outBuf[2],totalProcessed)!=(int)totalProcessed) {
for (int i=0; i<(int)totalProcessed; i++) {
total++;
if (isFadingOut) {
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]))*mul;
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]))*mul;
if (++curFadeOutSample>=fadeOutSamples) {
playing=false;
break;
}
} else {
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]));
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]));
if (lastLoopPos>-1 && i>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
}
}
}
if (sf_writef_float(sf,outBuf[2],total)!=(int)total) {
logE("error: failed to write entire buffer!");
break;
}
@ -286,7 +307,10 @@ void DivEngine::runExportThread() {
float* outBuf[2];
outBuf[0]=new float[EXPORT_BUFSIZE];
outBuf[1]=new float[EXPORT_BUFSIZE];
short* sysBuf=new short[EXPORT_BUFSIZE*2];
short* sysBuf[32];
for (int i=0; i<song.systemLen; i++) {
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
}
// take control of audio output
deinitAudioBackend();
@ -295,20 +319,45 @@ void DivEngine::runExportThread() {
logI("rendering to files...");
while (playing) {
size_t total=0;
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
for (int i=0; i<song.systemLen; i++) {
for (int j=0; j<EXPORT_BUFSIZE; j++) {
if (!disCont[i].dispatch->isStereo()) {
sysBuf[j]=disCont[i].bbOut[0][j];
} else {
sysBuf[j<<1]=disCont[i].bbOut[0][j];
sysBuf[1+(j<<1)]=disCont[i].bbOut[1][j];
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
totalProcessed=EXPORT_BUFSIZE;
}
for (int j=0; j<(int)totalProcessed; j++) {
total++;
if (isFadingOut) {
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
for (int i=0; i<song.systemLen; i++) {
if (!disCont[i].dispatch->isStereo()) {
sysBuf[i][j]=(double)disCont[i].bbOut[0][j]*mul;
} else {
sysBuf[i][j<<1]=(double)disCont[i].bbOut[0][j]*mul;
sysBuf[i][1+(j<<1)]=(double)disCont[i].bbOut[1][j]*mul;
}
}
if (++curFadeOutSample>=fadeOutSamples) {
playing=false;
break;
}
} else {
for (int i=0; i<song.systemLen; i++) {
if (!disCont[i].dispatch->isStereo()) {
sysBuf[i][j]=disCont[i].bbOut[0][j];
} else {
sysBuf[i][j<<1]=disCont[i].bbOut[0][j];
sysBuf[i][1+(j<<1)]=disCont[i].bbOut[1][j];
}
}
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
}
}
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! (%d) %d>%d",i,totalProcessed,EXPORT_BUFSIZE);
}
if (sf_writef_short(sf[i],sysBuf,totalProcessed)!=(int)totalProcessed) {
}
for (int i=0; i<song.systemLen; i++) {
if (sf_writef_short(sf[i],sysBuf[i],total)!=(int)total) {
logE("error: failed to write entire buffer! (%d)",i);
break;
}
@ -317,9 +366,9 @@ void DivEngine::runExportThread() {
delete[] outBuf[0];
delete[] outBuf[1];
delete[] sysBuf;
for (int i=0; i<song.systemLen; i++) {
delete[] sysBuf[i];
if (sf_close(sf[i])!=0) {
logE("could not close audio file!");
}
@ -382,19 +431,43 @@ void DivEngine::runExportThread() {
}
curOrder=0;
remainingLoops=loopCount;
prevOrder=0;
curFadeOutSample=0;
isFadingOut=false;
if (exportFadeOut<=0.01) {
remainingLoops=loopCount;
} else {
remainingLoops=-1;
}
playSub(false);
while (playing) {
size_t total=0;
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
for (int j=0; j<EXPORT_BUFSIZE; j++) {
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]));
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]));
}
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
totalProcessed=EXPORT_BUFSIZE;
}
if (sf_writef_float(sf,outBuf[2],totalProcessed)!=(int)totalProcessed) {
for (int j=0; j<(int)totalProcessed; j++) {
total++;
if (isFadingOut) {
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]))*mul;
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]))*mul;
if (++curFadeOutSample>=fadeOutSamples) {
playing=false;
break;
}
} else {
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]));
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]));
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
}
}
}
if (sf_writef_float(sf,outBuf[2],total)!=(int)total) {
logE("error: failed to write entire buffer!");
break;
}
@ -448,13 +521,14 @@ void DivEngine::runExportThread() {
}
#endif
bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) {
bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime) {
#ifndef HAVE_SNDFILE
logE("Furnace was not compiled with libsndfile. cannot export!");
return false;
#else
exportPath=path;
exportMode=mode;
exportFadeOut=fadeOutTime;
if (exportMode!=DIV_EXPORT_MODE_ONE) {
// remove extension
String lowerCase=exportPath;
@ -471,7 +545,12 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode)
stop();
repeatPattern=false;
setOrder(0);
remainingLoops=loops;
if (exportFadeOut<=0.01) {
remainingLoops=loops;
} else {
remainingLoops=-1;
}
exportLoopCount=loops;
exportThread=new std::thread(_runExportThread,this);
return true;
#endif
@ -780,6 +859,8 @@ void DivEngine::changeSong(size_t songIndex) {
curSubSongIndex=songIndex;
curOrder=0;
curRow=0;
prevOrder=0;
prevRow=0;
}
void DivEngine::swapChannelsP(int src, int dest) {
@ -827,12 +908,51 @@ bool DivEngine::removeSubSong(int index) {
return true;
}
void DivEngine::moveSubSongUp(size_t index) {
if (index<1 || index>=song.subsong.size()) return;
BUSY_BEGIN;
saveLock.lock();
if (index==curSubSongIndex) {
curSubSongIndex--;
} else if (index-1==curSubSongIndex) {
curSubSongIndex++;
}
DivSubSong* prev=song.subsong[index-1];
song.subsong[index-1]=song.subsong[index];
song.subsong[index]=prev;
saveLock.unlock();
BUSY_END;
}
void DivEngine::moveSubSongDown(size_t index) {
if (index>=song.subsong.size()-1) return;
BUSY_BEGIN;
saveLock.lock();
if (index==curSubSongIndex) {
curSubSongIndex++;
} else if (index+1==curSubSongIndex) {
curSubSongIndex--;
}
DivSubSong* prev=song.subsong[index+1];
song.subsong[index+1]=song.subsong[index];
song.subsong[index]=prev;
saveLock.unlock();
BUSY_END;
}
void DivEngine::clearSubSongs() {
BUSY_BEGIN;
saveLock.lock();
song.clearSongData();
changeSong(0);
curOrder=0;
prevOrder=0;
saveLock.unlock();
BUSY_END;
}
@ -987,6 +1107,9 @@ DivInstrument* DivEngine::getIns(int index, DivInstrumentType fallbackType) {
case DIV_INS_OPL:
return &song.nullInsOPL;
break;
case DIV_INS_OPL_DRUMS:
return &song.nullInsOPLDrums;
break;
default:
break;
}
@ -1072,6 +1195,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
int goal=curOrder;
curOrder=0;
curRow=0;
prevOrder=0;
prevRow=0;
stepPlay=0;
int prevDrift;
prevDrift=clockDrift;
@ -1085,6 +1210,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
totalTicks=0;
totalSeconds=0;
totalTicksR=0;
totalLoops=0;
lastLoopPos=-1;
}
speedAB=false;
playing=true;
@ -1121,6 +1248,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
if (!preserveDrift) {
ticks=1;
subticks=1;
prevOrder=curOrder;
prevRow=curRow;
}
skipping=false;
cmdStream.clear();
@ -1247,6 +1376,7 @@ unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int
void DivEngine::play() {
BUSY_BEGIN_SOFT;
curOrder=prevOrder;
sPreview.sample=-1;
sPreview.wave=-1;
sPreview.pos=0;
@ -1471,8 +1601,10 @@ int DivEngine::getEffectiveSampleRate(int rate) {
return rate;
}
void DivEngine::previewSample(int sample, int note) {
void DivEngine::previewSample(int sample, int note, int pStart, int pEnd) {
BUSY_BEGIN;
sPreview.pBegin=pStart;
sPreview.pEnd=pEnd;
if (sample<0 || sample>=(int)song.sample.size()) {
sPreview.sample=-1;
sPreview.pos=0;
@ -1488,7 +1620,7 @@ void DivEngine::previewSample(int sample, int note) {
if (rate<100) rate=100;
blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0;
sPreview.pos=0;
sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
sPreview.sample=sample;
sPreview.wave=-1;
BUSY_END;
@ -1540,11 +1672,11 @@ int DivEngine::getMaxVolumeChan(int ch) {
}
unsigned char DivEngine::getOrder() {
return curOrder;
return prevOrder;
}
int DivEngine::getRow() {
return curRow;
return prevRow;
}
size_t DivEngine::getCurrentSubSong() {
@ -1681,6 +1813,9 @@ int DivEngine::addInstrument(int refChan) {
case DIV_INS_OPL:
*ins=song.nullInsOPL;
break;
case DIV_INS_OPL_DRUMS:
*ins=song.nullInsOPLDrums;
break;
default:
break;
}
@ -1760,7 +1895,7 @@ int DivEngine::addWave() {
return waveCount;
}
bool DivEngine::addWaveFromFile(const char* path) {
bool DivEngine::addWaveFromFile(const char* path, bool addRaw) {
if (song.wave.size()>=256) {
lastError="too many wavetables!";
return false;
@ -1838,6 +1973,9 @@ bool DivEngine::addWaveFromFile(const char* path) {
// read as .dmw
reader.seek(0,SEEK_SET);
int len=reader.readI();
if (len<=0 || len>256) {
throw EndOfFileException(&reader,reader.size());
}
wave->max=(unsigned char)reader.readC();
if (wave->max==255) { // new wavetable format
unsigned char waveVersion=reader.readC();
@ -1856,8 +1994,27 @@ bool DivEngine::addWaveFromFile(const char* path) {
}
} else {
// read as binary
logI("reading binary...");
if (addRaw) {
logI("reading binary...");
len=reader.size();
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
wave->data[i]=(unsigned char)reader.readC();
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
} else {
delete wave;
delete[] buf;
return false;
}
}
} catch (EndOfFileException& e) {
// read as binary
if (addRaw) {
len=reader.size();
logI("reading binary for being too small...");
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
@ -1865,18 +2022,11 @@ bool DivEngine::addWaveFromFile(const char* path) {
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
} else {
delete wave;
delete[] buf;
return false;
}
} catch (EndOfFileException& e) {
// read as binary
len=reader.size();
logI("reading binary for being too small...");
if (len>256) len=256;
reader.seek(0,SEEK_SET);
for (int i=0; i<len; i++) {
wave->data[i]=(unsigned char)reader.readC();
if (wave->max<wave->data[i]) wave->max=wave->data[i];
}
wave->len=len;
}
}
} catch (EndOfFileException& e) {
@ -2129,6 +2279,7 @@ void DivEngine::delSample(int index) {
void DivEngine::addOrder(bool duplicate, bool where) {
unsigned char order[DIV_MAX_CHANS];
if (curSubSong->ordersLen>=0xff) return;
memset(order,0,DIV_MAX_CHANS);
BUSY_BEGIN_SOFT;
if (duplicate) {
for (int i=0; i<DIV_MAX_CHANS; i++) {
@ -2438,7 +2589,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
// 2. find a free channel
do {
if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel)) {
if ((!midiPoly) || (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel))) {
chan[finalChan].midiNote=note;
chan[finalChan].midiAge=midiAgeCounter++;
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
@ -2494,10 +2645,15 @@ void DivEngine::autoNoteOffAll() {
}
}
void DivEngine::setAutoNotePoly(bool poly) {
midiPoly=poly;
}
void DivEngine::setOrder(unsigned char order) {
BUSY_BEGIN_SOFT;
curOrder=order;
if (order>=curSubSong->ordersLen) curOrder=0;
prevOrder=curOrder;
if (playing && !freelance) {
playSub(false);
}
@ -2691,6 +2847,8 @@ void DivEngine::quitDispatch() {
tempoAccum=0;
curRow=0;
curOrder=0;
prevRow=0;
prevOrder=0;
nextSpeed=3;
changeOrd=-1;
changePos=0;
@ -2955,6 +3113,9 @@ bool DivEngine::init() {
oscBuf[0]=new float[32768];
oscBuf[1]=new float[32768];
memset(oscBuf[0],0,32768*sizeof(float));
memset(oscBuf[1],0,32768*sizeof(float));
initDispatch();
renderSamples();
reset();

View file

@ -303,7 +303,7 @@ class DivEngine {
bool systemsRegistered;
bool hasLoadedSomething;
int softLockCount;
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed;
size_t curSubSongIndex;
double divider;
int cycles;
@ -318,6 +318,7 @@ class DivEngine {
DivChannelState chan[DIV_MAX_CHANS];
DivAudioEngines audioEngine;
DivAudioExportModes exportMode;
double exportFadeOut;
std::map<String,String> conf;
std::queue<DivNoteEvent> pendingNotes;
bool isMuted[DIV_MAX_CHANS];
@ -339,16 +340,20 @@ class DivEngine {
int sample;
int wave;
unsigned int pos;
int pBegin, pEnd;
SamplePreview():
sample(-1),
wave(-1),
pos(0) {}
pos(0),
pBegin(-1),
pEnd(-1) {}
} sPreview;
short vibTable[64];
int reversePitchTable[4096];
int pitchTable[4096];
int midiBaseChan;
bool midiPoly;
size_t midiAgeCounter;
blip_buffer_t* samp_bb;
@ -461,7 +466,7 @@ class DivEngine {
// dump to ZSM.
SafeWriter* saveZSM(unsigned int zsmrate=60, bool loop=true);
// export to an audio file
bool saveAudio(const char* path, int loops, DivAudioExportModes mode);
bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0);
// wait for audio export to finish
void waitAudioFile();
// stop audio file export
@ -527,7 +532,7 @@ class DivEngine {
void syncReset();
// trigger sample preview
void previewSample(int sample, int note=-1);
void previewSample(int sample, int note=-1, int pStart=-1, int pEnd=-1);
void stopSamplePreview();
// trigger wave preview
@ -676,7 +681,7 @@ class DivEngine {
int addWave();
// add wavetable from file
bool addWaveFromFile(const char* path);
bool addWaveFromFile(const char* path, bool loadRaw=true);
// delete wavetable
void delWave(int index);
@ -724,6 +729,9 @@ class DivEngine {
void autoNoteOn(int chan, int ins, int note, int vol=-1);
void autoNoteOff(int chan, int note, int vol=-1);
void autoNoteOffAll();
// set whether autoNoteIn is mono or poly
void setAutoNotePoly(bool poly);
// go to order
void setOrder(unsigned char order);
@ -824,6 +832,10 @@ class DivEngine {
// remove subsong
bool removeSubSong(int index);
// move subsong
void moveSubSongUp(size_t index);
void moveSubSongDown(size_t index);
// clear all subsong data
void clearSubSongs();
@ -926,7 +938,12 @@ class DivEngine {
ticks(0),
curRow(0),
curOrder(0),
prevRow(0),
prevOrder(0),
remainingLoops(-1),
totalLoops(0),
lastLoopPos(0),
exportLoopCount(0),
nextSpeed(3),
curSubSongIndex(0),
divider(60),
@ -950,7 +967,9 @@ class DivEngine {
haltOn(DIV_HALT_NONE),
audioEngine(DIV_AUDIO_NULL),
exportMode(DIV_EXPORT_MODE_ONE),
exportFadeOut(0.0),
midiBaseChan(0),
midiPoly(true),
midiAgeCounter(0),
samp_bb(NULL),
samp_bbInLen(0),

View file

@ -870,14 +870,30 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
for (int i=0; i<4; i++) {
DivInstrumentSTD::OpMacro& op=std.opMacros[i];
reader.read(op.damMacro.val,op.damMacro.len);
reader.read(op.dvbMacro.val,op.dvbMacro.len);
reader.read(op.egtMacro.val,op.egtMacro.len);
reader.read(op.kslMacro.val,op.kslMacro.len);
reader.read(op.susMacro.val,op.susMacro.len);
reader.read(op.vibMacro.val,op.vibMacro.len);
reader.read(op.wsMacro.val,op.wsMacro.len);
reader.read(op.ksrMacro.val,op.ksrMacro.len);
for (int j=0; j<op.damMacro.len; j++) {
op.damMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.dvbMacro.len; j++) {
op.dvbMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.egtMacro.len; j++) {
op.egtMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.kslMacro.len; j++) {
op.kslMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.susMacro.len; j++) {
op.susMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.vibMacro.len; j++) {
op.vibMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.wsMacro.len; j++) {
op.wsMacro.val[j]=(unsigned char)reader.readC();
}
for (int j=0; j<op.ksrMacro.len; j++) {
op.ksrMacro.val[j]=(unsigned char)reader.readC();
}
}
}

View file

@ -59,6 +59,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_SNES=29,
DIV_INS_SU=30,
DIV_INS_NAMCO=31,
DIV_INS_OPL_DRUMS=32,
DIV_INS_MAX,
DIV_INS_NULL
};

View file

@ -21,6 +21,12 @@
#include "instrument.h"
#include "engine.h"
void DivMacroStruct::prepare(DivInstrumentMacro& source, DivEngine* e) {
has=had=actualHad=will=true;
mode=source.mode;
linger=(source.name=="vol" && e->song.volMacroLinger);
}
void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tick) {
if (!tick) {
had=false;
@ -46,6 +52,8 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
if (pos>=source.len) {
if (source.loop<source.len && source.loop>=0 && (source.loop>=source.rel || source.rel>=source.len)) {
pos=source.loop;
} else if (linger) {
pos--;
} else {
has=false;
}
@ -93,6 +101,7 @@ void DivMacroInt::init(DivInstrument* which) {
macroListLen=0;
subTick=1;
hasRelease=false;
released=false;
if (ins==NULL) return;
@ -228,7 +237,12 @@ void DivMacroInt::init(DivInstrument* which) {
}
for (size_t i=0; i<macroListLen; i++) {
macroList[i]->prepare(*macroSource[i]);
if (macroSource[i]!=NULL) {
macroList[i]->prepare(*macroSource[i],e);
hasRelease=(macroSource[i]->rel>=0 && macroSource[i]->rel<macroSource[i]->len);
} else {
hasRelease=false;
}
}
}

View file

@ -27,19 +27,17 @@ class DivEngine;
struct DivMacroStruct {
int pos;
int val;
bool has, had, actualHad, finished, will;
bool has, had, actualHad, finished, will, linger;
unsigned int mode;
void doMacro(DivInstrumentMacro& source, bool released, bool tick);
void init() {
pos=mode=0;
has=had=actualHad=will=false;
linger=false;
// TODO: test whether this breaks anything?
val=0;
}
void prepare(DivInstrumentMacro& source) {
has=had=actualHad=will=true;
mode=source.mode;
}
void prepare(DivInstrumentMacro& source, DivEngine* e);
DivMacroStruct():
pos(0),
val(0),
@ -48,6 +46,7 @@ struct DivMacroStruct {
actualHad(false),
finished(false),
will(false),
linger(false),
mode(0) {}
};
@ -97,6 +96,9 @@ class DivMacroInt {
ksr() {}
} op[4];
// state
bool hasRelease;
/**
* trigger macro release.
*/
@ -150,7 +152,8 @@ class DivMacroInt {
ex5(),
ex6(),
ex7(),
ex8() {
ex8(),
hasRelease(false) {
memset(macroList,0,128*sizeof(void*));
memset(macroSource,0,128*sizeof(void*));
}

View file

@ -271,6 +271,9 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
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;
}
if (chan[c.chan].useWave) {
chan[c.chan].ws.init(ins,chan[c.chan].audLen<<1,255,chan[c.chan].insChanged);
}
@ -424,6 +427,10 @@ bool DivPlatformAmiga::keyOffAffectsArp(int ch) {
return true;
}
DivMacroInt* DivPlatformAmiga::getChanMacroInt(int ch) {
return &chan[ch].std;
}
void DivPlatformAmiga::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {

View file

@ -99,6 +99,7 @@ class DivPlatformAmiga: public DivDispatch {
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
void notifyWaveChange(int wave);

View file

@ -284,12 +284,12 @@ void DivPlatformArcade::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -378,7 +378,7 @@ void DivPlatformArcade::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -432,7 +432,7 @@ void DivPlatformArcade::tick(bool sysTick) {
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -528,7 +528,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
DivInstrumentFM::Operator op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -585,7 +585,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -676,7 +676,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -876,7 +876,7 @@ void DivPlatformArcade::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -913,6 +913,10 @@ void* DivPlatformArcade::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformArcade::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformArcade::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -116,6 +116,7 @@ class DivPlatformArcade: public DivDispatch {
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
DivMacroInt* getChanMacroInt(int ch);
void notifyInsChange(int ins);
void setFlags(unsigned int flags);
bool isStereo();

View file

@ -315,6 +315,9 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
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;
}
if (isMuted[c.chan]) {
rWrite(0x08+c.chan,0);
} else if (intellivision && (chan[c.chan].psgMode&4)) {
@ -506,6 +509,10 @@ void* DivPlatformAY8910::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformAY8910::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -102,6 +102,7 @@ class DivPlatformAY8910: public DivDispatch {
void setFlags(unsigned int flags);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
bool getDCOffRequired();
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);

View file

@ -346,6 +346,9 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
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;
}
if (isMuted[c.chan]) {
rWrite(0x08+c.chan,0);
} else {
@ -536,6 +539,10 @@ void* DivPlatformAY8930::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformAY8930::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -98,6 +98,7 @@ class DivPlatformAY8930: public DivDispatch {
void setFlags(unsigned int flags);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -169,6 +169,9 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
rWrite(2+c.chan,(chan[c.chan].wave<<5)|chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
@ -278,6 +281,10 @@ void* DivPlatformBubSysWSG::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformBubSysWSG::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformBubSysWSG::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -78,6 +78,7 @@ class DivPlatformBubSysWSG: public DivDispatch {
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
void setFlags(unsigned int flags);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);

View file

@ -491,6 +491,10 @@ void* DivPlatformC64::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformC64::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformC64::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -97,6 +97,7 @@ class DivPlatformC64: public DivDispatch {
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
bool getDCOffRequired();
DivMacroInt* getChanMacroInt(int ch);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -139,7 +139,7 @@ void DivPlatformFDS::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// ok, why are the volumes like that?
chan[i].outVol=MIN(32,chan[i].std.vol.val)-(32-MIN(32,chan[i].vol));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol,chan[i].std.vol.val,32);
if (chan[i].outVol<0) chan[i].outVol=0;
rWrite(0x4080,0x80|chan[i].outVol);
}
@ -285,6 +285,9 @@ int DivPlatformFDS::dispatch(DivCommand c) {
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;
}
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
ws.changeWave1(chan[c.chan].wave);
@ -433,6 +436,10 @@ void* DivPlatformFDS::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformFDS::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformFDS::getOscBuffer(int ch) {
return oscBuf;
}

View file

@ -88,6 +88,7 @@ class DivPlatformFDS: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -433,6 +433,10 @@ void* DivPlatformGB::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGB::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGB::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -72,6 +72,7 @@ class DivPlatformGB: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -24,10 +24,13 @@
#include "genesisshared.h"
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
static unsigned char konOffs[6]={
0, 1, 2, 4, 5, 6
};
#define CHIP_DIVIDER 72
#define CHIP_FREQBASE 9440540
const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
@ -144,10 +147,13 @@ void DivPlatformGenesis::processDAC() {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (parent->song.noOPN2Vol) {
sample+=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
chan[i].dacOutput=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
sample+=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
chan[i].dacOutput=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
}
sample+=chan[i].dacOutput;
} else {
chan[i].dacOutput=0;
}
chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>=(chipClock/576)) {
@ -249,7 +255,20 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1];
//OPN2_Write(&fm,0,0);
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
if (i==5) {
if (fm.dacen) {
if (softPCM) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
}
}
os[0]=(os[0]<<5);
@ -293,7 +312,20 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
//OPN2_Write(&fm,0,0);
for (int i=0; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
if (i==5) {
if (fm_ymfm->debug_dac_enable()) {
if (softPCM) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
}
}
if (os[0]<-32768) os[0]=-32768;
@ -321,7 +353,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -329,7 +361,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -355,7 +387,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.pitch.had) {
@ -384,7 +416,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -397,11 +429,11 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
if (chan[i].std.fms.had) {
chan[i].state.fms=chan[i].std.fms.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.ams.had) {
chan[i].state.ams=chan[i].std.ams.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
@ -437,7 +469,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -495,8 +527,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
for (int i=0; i<8; i++) {
for (int i=0; i<7; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -545,27 +576,45 @@ void DivPlatformGenesis::tick(bool sysTick) {
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>5) return;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
if (ch>6) return;
if (ch<6) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
} else {
ch--;
}
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==7 && extMode && softPCM) { // CSM
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].portaPause=false;
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].keyOn=true;
chan[c.chan].active=true;
break;
}
if (c.chan>=5) {
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].dacMode=1;
@ -634,7 +683,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -653,7 +702,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
chan[c.chan].insChanged=false;
@ -668,7 +717,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan>=5) {
if (c.chan>=5 && c.chan<7) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) {
@ -706,7 +755,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -731,7 +780,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} else {
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
break;
}
case DIV_CMD_PITCH: {
@ -763,6 +812,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
break;
}
if (c.chan==7) {
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;
}
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
bool return2=false;
@ -809,7 +881,9 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
if (c.chan==7) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
@ -847,7 +921,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1038,7 +1112,7 @@ void DivPlatformGenesis::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1051,7 +1125,7 @@ void DivPlatformGenesis::forceIns() {
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
if (chan[i].active) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
@ -1071,6 +1145,10 @@ void* DivPlatformGenesis::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGenesis::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -51,6 +51,7 @@ class DivPlatformGenesis: public DivDispatch {
bool dacReady;
bool dacDirection;
unsigned char sampleBank;
signed char dacOutput;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
@ -85,7 +86,8 @@ class DivPlatformGenesis: public DivDispatch {
dacDelay(0),
dacReady(true),
dacDirection(false),
sampleBank(0) {}
sampleBank(0),
dacOutput(0) {}
};
Channel chan[10];
DivDispatchOscBuffer* oscBuf[10];
@ -128,6 +130,7 @@ class DivPlatformGenesis: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -23,6 +23,7 @@
#include "genesisshared.h"
#define CHIP_DIVIDER 72
#define CHIP_FREQBASE 9440540
int DivPlatformGenesisExt::dispatch(DivCommand c) {
@ -54,7 +55,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -92,7 +93,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -179,6 +180,11 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
rWrite(0x22,lfoValue);
break;
}
case DIV_CMD_FM_FB: {
chan[2].state.fb=c.value&7;
rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
break;
}
case DIV_CMD_FM_MULT: { // TODO
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
@ -193,7 +199,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[2].state.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -376,8 +382,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (isOutput[chan[2].state.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
immWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
@ -441,9 +447,43 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
opChan[i].keyOn=false;
}
}
if (extMode && softPCM) {
if (chan[7].freqChanged) {
chan[7].freq=parent->calcFreq(chan[7].baseFreq,chan[7].pitch,true,0,chan[7].pitch2,chipClock,CHIP_DIVIDER);
if (chan[7].freq<1) chan[7].freq=1;
if (chan[7].freq>1024) chan[7].freq=1024;
int wf=0x400-chan[7].freq;
immWrite(0x24,wf>>2);
immWrite(0x25,wf&3);
chan[7].freqChanged=false;
}
if (chan[7].keyOff || chan[7].keyOn) {
writeNoteOn=true;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
}
}
}
if (writeNoteOn) {
if (chan[7].active) { // CSM
writeMask^=0xf0;
}
immWrite(0x28,writeMask);
}
if (extMode && softPCM) {
if (chan[7].keyOn) {
immWrite(0x27,0x81);
chan[7].keyOn=false;
}
if (chan[7].keyOff) {
immWrite(0x27,0x40);
chan[7].keyOff=false;
}
}
}
void DivPlatformGenesisExt::forceIns() {
@ -455,7 +495,7 @@ void DivPlatformGenesisExt::forceIns() {
if (isOpMuted[j]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -464,7 +504,7 @@ void DivPlatformGenesisExt::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -503,6 +543,12 @@ void* DivPlatformGenesisExt::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) {
if (ch>=6) return &chan[ch-3].std;
if (ch>=2) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];

View file

@ -55,6 +55,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View file

@ -297,6 +297,9 @@ int DivPlatformLynx::dispatch(DivCommand c) {
chan[c.chan].active=true;
WRITE_VOLUME(c.chan,(isMuted[c.chan]?0:(chan[c.chan].vol&127)));
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_MIKEY));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
}
case DIV_CMD_NOTE_OFF:
@ -421,6 +424,10 @@ void* DivPlatformLynx::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformLynx::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformLynx::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -88,6 +88,7 @@ class DivPlatformLynx: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -107,7 +107,7 @@ void DivPlatformMMC5::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// ok, why are the volumes like that?
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
}
@ -241,6 +241,9 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
rWrite(0x5000+c.chan*4,0x30|chan[c.chan].vol|((chan[c.chan].duty&3)<<6));
break;
case DIV_CMD_NOTE_OFF:
@ -350,6 +353,10 @@ void* DivPlatformMMC5::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformMMC5::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformMMC5::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -74,6 +74,7 @@ class DivPlatformMMC5: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -31,50 +31,73 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
}
const char* DivPlatformMSM6258::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set frequency divider (0-2)";
break;
case 0x21:
return "21xx: Select clock rate (0: full; 1: half)";
break;
}
return NULL;
}
void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) {
short* outs[2]={
&msmOut,
NULL
};
for (size_t h=start; h<start+len; h++) {
short* outs[2]={
&bufL[h],
NULL
};
if (!writes.empty()) {
QueuedWrite& w=writes.front();
switch (w.addr) {
case 0:
msm->ctrl_w(w.val);
break;
}
writes.pop();
}
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4);
if (msm->data_w(nextData)) {
samplePos++;
if (samplePos>=(int)s->lengthVOX) {
sample=-1;
samplePos=0;
msm->ctrl_w(1);
if (--msmClockCount<0) {
if (--msmDividerCount<=0) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
switch (w.addr) {
case 0:
msm->ctrl_w(w.val);
break;
case 2:
msmPan=w.val;
break;
case 8:
msmClock=w.val;
break;
case 12:
msmDivider=4-(w.val&3);
if (msmDivider<2) msmDivider=2;
break;
}
writes.pop();
}
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4);
if (msm->data_w(nextData)) {
samplePos++;
if (samplePos>=(int)s->lengthVOX) {
sample=-1;
samplePos=0;
msm->ctrl_w(1);
}
}
}
msm->sound_stream_update(outs,1);
msmDividerCount=msmDivider;
}
msmClockCount=msmClock;
}
msm->sound_stream_update(outs,1);
if (isMuted[0]) {
bufL[h]=0;
bufR[h]=0;
oscBuf[0]->data[oscBuf[0]->needle++]=0;
} else {
bufL[h]=(msmPan&2)?msmOut:0;
bufR[h]=(msmPan&1)?msmOut:0;
oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?msmOut:0;
}
/*if (++updateOsc>=22) {
updateOsc=0;
// TODO: per-channel osc
for (int i=0; i<1; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
}
}*/
}
}
@ -176,6 +199,23 @@ int DivPlatformMSM6258::dispatch(DivCommand c) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_SAMPLE_FREQ:
rateSel=c.value&3;
rWrite(12,rateSel);
break;
case DIV_CMD_SAMPLE_MODE:
clockSel=c.value&1;
rWrite(8,clockSel);
break;
case DIV_CMD_PANNING: {
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
rWrite(2,chan[c.chan].pan);
break;
}
case DIV_CMD_LEGATO: {
break;
}
@ -205,12 +245,19 @@ void DivPlatformMSM6258::forceIns() {
for (int i=0; i<1; i++) {
chan[i].insChanged=true;
}
rWrite(12,rateSel);
rWrite(8,clockSel);
rWrite(2,chan[0].pan);
}
void* DivPlatformMSM6258::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformMSM6258::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) {
return oscBuf[ch];
}
@ -234,6 +281,14 @@ void DivPlatformMSM6258::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformMSM6258::reset() {
while (!writes.empty()) writes.pop();
msm->device_reset();
msmClock=chipClock;
msmDivider=2;
msmDividerCount=0;
msmClock=0;
msmClockCount=0;
msmPan=3;
rateSel=2;
clockSel=0;
if (dumpWrites) {
addWrite(0xffffffff,0);
}
@ -253,6 +308,10 @@ void DivPlatformMSM6258::reset() {
delay=0;
}
bool DivPlatformMSM6258::isStereo() {
return true;
}
bool DivPlatformMSM6258::keyOffAffectsArp(int ch) {
return false;
}
@ -307,15 +366,23 @@ void DivPlatformMSM6258::renderSamples() {
}
void DivPlatformMSM6258::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=4096000;
} else {
chipClock=4000000;
switch (flags) {
case 3:
chipClock=8192000;
break;
case 2:
chipClock=8000000;
break;
case 1:
chipClock=4096000;
break;
default:
chipClock=4000000;
break;
}
rate=chipClock/256;
rate=chipClock/128;
for (int i=0; i<1; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/256;
oscBuf[i]->rate=rate;
}
}

View file

@ -87,7 +87,9 @@ class DivPlatformMSM6258: public DivDispatch {
unsigned char* adpcmMem;
size_t adpcmMemLen;
unsigned char sampleBank;
unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel;
signed char msmDividerCount, msmClockCount;
short msmOut;
int delay, updateOsc, sample, samplePos;
@ -102,6 +104,7 @@ class DivPlatformMSM6258: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
@ -109,6 +112,7 @@ class DivPlatformMSM6258: public DivDispatch {
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);

View file

@ -23,13 +23,18 @@
#include <string.h>
#include <math.h>
#define rWrite(v) if (!skipRegisterWrites) {writes.emplace(0,v); if (dumpWrites) {addWrite(0,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set chip output rate (0: clock/132; 1: clock/165)";
break;
}
return NULL;
}
@ -42,7 +47,28 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
if (delay<=0) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
msm->command_w(w.val);
switch (w.addr) {
case 0: // command
msm->command_w(w.val);
break;
case 8: // chip clock select (VGM)
case 9:
case 10:
case 11:
break;
case 12: // rate select
msm->ss_w(!w.val);
break;
case 14: // enable bankswitch
break;
case 15: // set bank base
break;
case 16: // switch bank
case 17:
case 18:
case 19:
break;
}
writes.pop();
delay=32;
}
@ -92,9 +118,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
rWrite(0,(8<<c.chan)); // turn off
rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
} else {
break;
}
@ -107,9 +133,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
}
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
chan[c.chan].sample=12*sampleBank+c.value%12;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
rWrite(0,(8<<c.chan)); // turn off
rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
}
break;
}
@ -117,14 +143,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].std.release();
break;
case DIV_CMD_ENV_RELEASE:
@ -153,6 +179,10 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
case DIV_CMD_NOTE_PORTA: {
return 2;
}
case DIV_CMD_SAMPLE_FREQ:
rateSel=c.value;
rWrite(12,!rateSel);
break;
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
@ -190,12 +220,17 @@ void DivPlatformMSM6295::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
}
rWrite(12,!rateSel);
}
void* DivPlatformMSM6295::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformMSM6295::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformMSM6295::getOscBuffer(int ch) {
return oscBuf[ch];
}
@ -219,6 +254,7 @@ void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformMSM6295::reset() {
while (!writes.empty()) writes.pop();
msm->reset();
msm->ss_w(false);
if (dumpWrites) {
addWrite(0xffffffff,0);
}
@ -232,6 +268,7 @@ void DivPlatformMSM6295::reset() {
}
sampleBank=0;
rateSel=false;
delay=0;
}
@ -240,6 +277,10 @@ bool DivPlatformMSM6295::keyOffAffectsArp(int ch) {
return false;
}
float DivPlatformMSM6295::getPostAmp() {
return 3.0f;
}
void DivPlatformMSM6295::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
@ -302,14 +343,52 @@ void DivPlatformMSM6295::renderSamples() {
}
void DivPlatformMSM6295::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=8448000;
} else {
chipClock=8000000;
switch (flags) {
case 0:
chipClock=4000000/4;
break;
case 1:
chipClock=4224000/4;
break;
case 2:
chipClock=4000000;
break;
case 3:
chipClock=4224000;
break;
case 4:
chipClock=COLOR_NTSC;
break;
case 5:
chipClock=COLOR_NTSC/2.0;
break;
case 6:
chipClock=COLOR_NTSC*2.0/7.0;
break;
case 7:
chipClock=COLOR_NTSC/4.0;
break;
case 8:
chipClock=4000000/2;
break;
case 9:
chipClock=4224000/2;
break;
case 10:
chipClock=875000;
break;
case 11:
chipClock=937500;
break;
case 12:
chipClock=1500000;
break;
default:
chipClock=4000000/4;
break;
}
rate=chipClock/((flags&2)?6:24);
rate=chipClock/3;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/22;
}
}

View file

@ -101,6 +101,7 @@ class DivPlatformMSM6295: public DivDispatch {
int delay, updateOsc;
bool extMode;
bool rateSel;
short oldWrites[512];
short pendingWrites[512];
@ -111,6 +112,7 @@ class DivPlatformMSM6295: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
@ -119,6 +121,7 @@ class DivPlatformMSM6295: public DivDispatch {
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
float getPostAmp();
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);

View file

@ -625,6 +625,10 @@ void* DivPlatformN163::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformN163::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformN163::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -95,6 +95,7 @@ class DivPlatformN163: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -349,6 +349,9 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) {
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;
}
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
@ -464,6 +467,10 @@ void* DivPlatformNamcoWSG::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformNamcoWSG::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformNamcoWSG::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -79,6 +79,7 @@ class DivPlatformNamcoWSG: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -230,7 +230,7 @@ void DivPlatformNES::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// ok, why are the volumes like that?
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (i==2) { // triangle
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:255);
@ -446,6 +446,9 @@ int DivPlatformNES::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (c.chan==2) {
rWrite(0x4000+c.chan*4,0xff);
} else {
@ -607,6 +610,10 @@ void* DivPlatformNES::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformNES::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformNES::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -89,6 +89,7 @@ class DivPlatformNES: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -342,7 +342,7 @@ void DivPlatformOPL::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(63,chan[i].std.vol.val))/63;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(63,chan[i].std.vol.val),63);
for (int j=0; j<ops; j++) {
unsigned char slot=slots[j][i];
if (slot==255) continue;
@ -353,7 +353,7 @@ void DivPlatformOPL::tick(bool sysTick) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -480,7 +480,7 @@ void DivPlatformOPL::tick(bool sysTick) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -689,7 +689,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -784,10 +784,23 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL);
DivInstrument* ins=parent->getIns(chan[c.chan].ins,c.chan>melodicChans?DIV_INS_OPL_DRUMS:DIV_INS_OPL);
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
chan[melodicChans+i+1].state.alg=ins->fm.alg;
chan[melodicChans+i+1].state.fb=ins->fm.fb;
chan[melodicChans+i+1].state.opllPreset=ins->fm.opllPreset;
chan[melodicChans+i+1].state.fixedDrums=ins->fm.fixedDrums;
chan[melodicChans+i+1].state.kickFreq=ins->fm.kickFreq;
chan[melodicChans+i+1].state.snareHatFreq=ins->fm.snareHatFreq;
chan[melodicChans+i+1].state.tomTopFreq=ins->fm.tomTopFreq;
chan[melodicChans+i+1].state.op[0]=ins->fm.op[i];
}
} else {
chan[c.chan].state=ins->fm;
}
}
chan[c.chan].macroInit(ins);
@ -795,49 +808,81 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].insChanged) {
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
chan[c.chan].fourOp=(ops==4);
if (chan[c.chan].fourOp) {
chan[c.chan+1].macroInit(NULL);
}
update4OpMask=true;
for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][c.chan];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS) {
for (int i=0; i<4; i++) {
int ch=melodicChans+1+i;
unsigned char slot=slots[0][ch];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[ch].state.op[0];
chan[ch].fourOp=false;
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
if (isMuted[ch]) {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
} else {
oldWrites[chanMap[ch]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&3)<<4));
}
}
} else {
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
chan[c.chan].fourOp=(ops==4);
if (chan[c.chan].fourOp) {
chan[c.chan+1].macroInit(NULL);
}
update4OpMask=true;
for (int i=0; i<ops; i++) {
unsigned char slot=slots[i][c.chan];
if (slot==255) continue;
unsigned short baseAddr=slotMap[slot];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
if (isMuted[c.chan]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
if (oplType>1) {
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
}
}
if (isMuted[c.chan]) {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
}
} else {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
if (isMuted[c.chan]) {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
}
} else {
oldWrites[chanMap[c.chan]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
if (ops==4) {
oldWrites[chanMap[c.chan+1]+ADDR_LR_FB_ALG]=-1;
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
}
}
}
}
@ -845,20 +890,32 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
if (c.chan>=melodicChans && chan[c.chan].state.opllPreset==16 && chan[c.chan].state.fixedDrums) { // drums
if (c.chan==melodicChans) {
chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&1023)<<(chan[c.chan].state.kickFreq>>10);
} else if (c.chan==melodicChans+1 || c.chan==melodicChans+4) {
chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&1023)<<(chan[c.chan].state.snareHatFreq>>10);
} else if (c.chan==melodicChans+2 || c.chan==melodicChans+3) {
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&1023)<<(chan[c.chan].state.tomTopFreq>>10);
if (c.chan>melodicChans && ins->type==DIV_INS_OPL_DRUMS && chan[c.chan].state.fixedDrums) {
chan[melodicChans+1].fixedFreq=(chan[melodicChans+1].state.snareHatFreq&1023)<<(chan[melodicChans+1].state.snareHatFreq>>10);
chan[melodicChans+2].fixedFreq=(chan[melodicChans+2].state.tomTopFreq&1023)<<(chan[melodicChans+2].state.tomTopFreq>>10);
chan[melodicChans+3].fixedFreq=chan[melodicChans+2].fixedFreq;
chan[melodicChans+4].fixedFreq=chan[melodicChans+1].fixedFreq;
chan[melodicChans+1].freqChanged=true;
chan[melodicChans+2].freqChanged=true;
chan[melodicChans+3].freqChanged=true;
chan[melodicChans+4].freqChanged=true;
} else {
if (c.chan>=melodicChans && (chan[c.chan].state.opllPreset==16 || ins->type==DIV_INS_OPL_DRUMS) && chan[c.chan].state.fixedDrums) { // drums
if (c.chan==melodicChans) {
chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&1023)<<(chan[c.chan].state.kickFreq>>10);
} else if (c.chan==melodicChans+1 || c.chan==melodicChans+4) {
chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&1023)<<(chan[c.chan].state.snareHatFreq>>10);
} else if (c.chan==melodicChans+2 || c.chan==melodicChans+3) {
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&1023)<<(chan[c.chan].state.tomTopFreq>>10);
} else {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].fixedFreq=0;
}
} else {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].fixedFreq=0;
}
} else {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
chan[c.chan].fixedFreq=0;
}
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
@ -914,7 +971,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1063,7 +1120,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1293,7 +1350,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1310,7 +1367,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1384,7 +1441,7 @@ void DivPlatformOPL::forceIns() {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1426,6 +1483,10 @@ void* DivPlatformOPL::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
if (ch>=18) return NULL;
return oscBuf[ch];

View file

@ -125,6 +125,7 @@ class DivPlatformOPL: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -149,9 +149,9 @@ void DivPlatformOPLL::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(15,chan[i].std.vol.val))/15;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(15,chan[i].std.vol.val),15);
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
}
@ -174,7 +174,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (chan[i].std.wave.had && chan[i].state.opllPreset!=16) {
chan[i].state.opllPreset=chan[i].std.wave.val;
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
}
@ -244,7 +244,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
op.tl=((j==1)?15:63)-m.tl.val;
if (j==1) {
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
} else {
rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(op.tl&63));
@ -384,21 +384,6 @@ int DivPlatformOPLL::toFreq(int freq) {
void DivPlatformOPLL::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
/*
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));*/
}
int DivPlatformOPLL::dispatch(DivCommand c) {
@ -483,7 +468,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
}
}
if (c.chan<9) {
rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4));
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
}
@ -553,7 +538,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
break;
} else if (c.chan<6 || !drums) {
if (c.chan<9) {
rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4));
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
break;
@ -647,7 +632,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
car.tl=c.value2&15;
if (c.chan<9) {
rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4));
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
break;
@ -862,7 +847,7 @@ void DivPlatformOPLL::forceIns() {
rWrite(0x07,(car.sl<<4)|(car.rr));
}
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
if (!(i>=6 && properDrums)) {
if (chan[i].active) {
@ -911,6 +896,10 @@ void* DivPlatformOPLL::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformOPLL::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) {
if (ch>=9) return NULL;
return oscBuf[ch];

View file

@ -102,6 +102,7 @@ class DivPlatformOPLL: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -154,7 +154,7 @@ void DivPlatformPCE::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&31)*MIN(31,chan[i].std.vol.val))>>5;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31);
if (chan[i].furnaceDac && chan[i].pcm) {
// ignore for now
} else {
@ -328,6 +328,9 @@ int DivPlatformPCE::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chWrite(c.chan,0x04,0x80|chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
@ -474,6 +477,10 @@ void* DivPlatformPCE::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformPCE::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -89,6 +89,7 @@ class DivPlatformPCE: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -19,6 +19,7 @@
#include "pcspkr.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#ifdef __linux__
@ -28,6 +29,8 @@
#include <unistd.h>
#include <linux/input.h>
#include <linux/kd.h>
#include <time.h>
#include <sys/io.h>
#endif
#define PCSPKR_DIVIDER 4
@ -38,6 +41,137 @@ const char* regCheatSheetPCSpeaker[]={
NULL
};
void _pcSpeakerThread(void* inst) {
((DivPlatformPCSpeaker*)inst)->pcSpeakerThread();
}
void DivPlatformPCSpeaker::pcSpeakerThread() {
std::unique_lock<std::mutex> unique(realOutSelfLock);
RealQueueVal r(0,0,0);
logD("starting PC speaker out thread");
while (!realOutQuit) {
realQueueLock.lock();
if (realQueue.empty()) {
realQueueLock.unlock();
realOutCond.wait(unique);
continue;
} else {
r=realQueue.front();
realQueue.pop();
}
realQueueLock.unlock();
#ifdef __linux__
static struct timespec ts, tSleep, rSleep;
if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) {
logW("could not get time!");
tSleep.tv_sec=0;
tSleep.tv_nsec=0;
} else {
tSleep.tv_sec=r.tv_sec-ts.tv_sec;
tSleep.tv_nsec=r.tv_nsec-ts.tv_nsec;
if (tSleep.tv_nsec<0) {
tSleep.tv_sec--;
tSleep.tv_nsec+=1000000000;
}
}
if (tSleep.tv_nsec>0 || tSleep.tv_sec>0) {
nanosleep(&tSleep,&rSleep);
}
if (beepFD>=0) {
switch (realOutMethod) {
case 0: { // evdev
static struct input_event ie;
ie.time.tv_sec=r.tv_sec;
ie.time.tv_usec=r.tv_nsec/1000;
ie.type=EV_SND;
ie.code=SND_TONE;
if (r.val>0) {
ie.value=chipClock/r.val;
} else {
ie.value=0;
}
if (write(beepFD,&ie,sizeof(struct input_event))<0) {
logW("error while writing frequency! %s",strerror(errno));
} else {
//logV("writing freq: %d",r.val);
}
break;
}
case 1: // KIOCSOUND (on tty)
if (ioctl(beepFD,KIOCSOUND,r.val)<0) {
logW("ioctl error! %s",strerror(errno));
}
break;
case 2: { // /dev/port
unsigned char bOut;
bOut=0;
if (r.val==0) {
lseek(beepFD,0x61,SEEK_SET);
if (read(beepFD,&bOut,1)<1) {
logW("read from 0x61: %s",strerror(errno));
}
bOut&=(~3);
lseek(beepFD,0x61,SEEK_SET);
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x61: %s",strerror(errno));
}
} else {
lseek(beepFD,0x43,SEEK_SET);
bOut=0xb6;
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x43: %s",strerror(errno));
}
lseek(beepFD,0x42,SEEK_SET);
bOut=r.val&0xff;
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x42: %s",strerror(errno));
}
lseek(beepFD,0x42,SEEK_SET);
bOut=r.val>>8;
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x42: %s",strerror(errno));
}
lseek(beepFD,0x61,SEEK_SET);
if (read(beepFD,&bOut,1)<1) {
logW("read from 0x61: %s",strerror(errno));
}
bOut|=3;
lseek(beepFD,0x61,SEEK_SET);
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x61: %s",strerror(errno));
}
}
break;
}
case 3: // KIOCSOUND (on stdout)
if (ioctl(beepFD,KIOCSOUND,r.val)<0) {
logW("ioctl error! %s",strerror(errno));
}
break;
case 4: // outb()
if (r.val==0) {
outb(inb(0x61)&(~3),0x61);
realOutEnabled=false;
} else {
outb(0xb6,0x43);
outb(r.val&0xff,0x42);
outb(r.val>>8,0x42);
if (!realOutEnabled) {
outb(inb(0x61)|3,0x61);
realOutEnabled=true;
}
}
break;
}
} else {
//logV("not writing because fd is less than 0");
}
#endif
}
logD("stopping PC speaker out thread");
}
const char** DivPlatformPCSpeaker::getRegisterSheet() {
return regCheatSheetPCSpeaker;
}
@ -126,25 +260,28 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start,
}
}
void DivPlatformPCSpeaker::beepFreq(int freq) {
void DivPlatformPCSpeaker::beepFreq(int freq, int delay) {
realQueueLock.lock();
#ifdef __linux__
static struct input_event ie;
if (beepFD>=0) {
gettimeofday(&ie.time,NULL);
ie.type=EV_SND;
ie.code=SND_TONE;
if (freq>0) {
ie.value=chipClock/freq;
} else {
ie.value=0;
}
if (write(beepFD,&ie,sizeof(struct input_event))<0) {
perror("error while writing frequency!");
} else {
//printf("writing freq: %d\n",freq);
struct timespec ts;
double addition=1000000000.0*(double)delay/(double)rate;
addition+=1500000000.0*((double)parent->getAudioDescGot().bufsize/parent->getAudioDescGot().rate);
if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) {
ts.tv_sec=0;
ts.tv_nsec=0;
} else {
ts.tv_nsec+=addition;
while (ts.tv_nsec>=1000000000) {
ts.tv_sec++;
ts.tv_nsec-=1000000000;
}
}
realQueue.push(RealQueueVal(ts.tv_sec,ts.tv_nsec,freq));
#else
realQueue.push(RealQueueVal(0,0,freq));
#endif
realQueueLock.unlock();
realOutCond.notify_one();
}
void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start, size_t len) {
@ -152,7 +289,7 @@ void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start,
if (lastOn!=on || lastFreq!=freq) {
lastOn=on;
lastFreq=freq;
beepFreq((on && !isMuted[0])?freq:0);
beepFreq((on && !isMuted[0])?freq:0,start);
}
for (size_t i=start; i<start+len; i++) {
if (on) {
@ -251,6 +388,9 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) {
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -345,6 +485,10 @@ void* DivPlatformPCSpeaker::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformPCSpeaker::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformPCSpeaker::getOscBuffer(int ch) {
return oscBuf;
}
@ -382,18 +526,51 @@ void DivPlatformPCSpeaker::reset() {
low=0;
band=0;
if (speakerType==3) {
//if (speakerType==3) {
#ifdef __linux__
if (beepFD==-1) {
beepFD=open("/dev/input/by-path/platform-pcspkr-event-spkr",O_WRONLY);
switch (realOutMethod) {
case 0: // evdev
beepFD=open("/dev/input/by-path/platform-pcspkr-event-spkr",O_WRONLY);
break;
case 1: // KIOCSOUND (on tty)
beepFD=open("/dev/tty1",O_WRONLY);
break;
case 2: // /dev/port
beepFD=open("/dev/port",O_WRONLY);
break;
case 3: // KIOCSOUND (on stdout)
beepFD=STDOUT_FILENO;
break;
case 4: // outb()
beepFD=-1;
if (ioperm(0x61,8,1)<0) {
logW("ioperm 0x61: %s",strerror(errno));
break;
}
if (ioperm(0x43,8,1)<0) {
logW("ioperm 0x43: %s",strerror(errno));
break;
}
if (ioperm(0x42,8,1)<0) {
logW("ioperm 0x42: %s",strerror(errno));
break;
}
beepFD=STDOUT_FILENO;
break;
}
if (beepFD<0) {
perror("error while opening PC speaker");
logW("error while opening PC speaker! %s",strerror(errno));
}
}
#endif
beepFreq(0);
} else {
/*} else {
beepFreq(0);
}*/
if (realOutThread==NULL) {
realOutThread=new std::thread(_pcSpeakerThread,this);
}
memset(regPool,0,2);
@ -433,6 +610,10 @@ int DivPlatformPCSpeaker::init(DivEngine* p, int channels, int sugRate, unsigned
dumpWrites=false;
skipRegisterWrites=false;
beepFD=-1;
realOutQuit=false;
realOutThread=NULL;
realOutMethod=parent->getConfInt("pcSpeakerOutMethod",0);
realOutEnabled=false;
for (int i=0; i<1; i++) {
isMuted[i]=false;
}
@ -447,8 +628,14 @@ void DivPlatformPCSpeaker::quit() {
if (speakerType==3) {
beepFreq(0);
}
if (realOutThread!=NULL) {
realOutQuit=true;
realOutCond.notify_one();
realOutThread->join();
delete realOutThread;
}
#ifdef __linux__
if (beepFD>=0) close(beepFD);
if (beepFD>=0 && realOutMethod<3) close(beepFD);
#endif
delete oscBuf;
}

View file

@ -22,6 +22,10 @@
#include "../dispatch.h"
#include "../macroInt.h"
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
class DivPlatformPCSpeaker: public DivDispatch {
struct Channel {
@ -57,9 +61,23 @@ class DivPlatformPCSpeaker: public DivDispatch {
};
Channel chan[1];
DivDispatchOscBuffer* oscBuf;
std::thread* realOutThread;
std::mutex realOutSelfLock;
std::condition_variable realOutCond;
bool realOutQuit;
struct RealQueueVal {
int tv_sec, tv_nsec;
unsigned short val;
RealQueueVal(int sec, int nsec, unsigned short v):
tv_sec(sec),
tv_nsec(nsec),
val(v) {}
};
std::queue<RealQueueVal> realQueue;
std::mutex realQueueLock;
bool isMuted[1];
bool on, flip, lastOn;
int pos, speakerType, beepFD;
bool on, flip, lastOn, realOutEnabled;
int pos, speakerType, beepFD, realOutMethod;
float low, band;
float low2, high2, band2;
float low3, band3;
@ -68,7 +86,7 @@ class DivPlatformPCSpeaker: public DivDispatch {
friend void putDispatchChan(void*,int,int);
void beepFreq(int freq);
void beepFreq(int freq, int delay=0);
void acquire_unfilt(short* bufL, short* bufR, size_t start, size_t len);
void acquire_cone(short* bufL, short* bufR, size_t start, size_t len);
@ -76,9 +94,11 @@ class DivPlatformPCSpeaker: public DivDispatch {
void acquire_real(short* bufL, short* bufR, size_t start, size_t len);
public:
void pcSpeakerThread();
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -149,6 +149,9 @@ int DivPlatformPET::dispatch(DivCommand c) {
chan.active=true;
chan.keyOn=true;
chan.macroInit(ins);
if (!parent->song.brokenOutVol && !chan.std.vol.will) {
chan.outVol=chan.vol;
}
break;
}
case DIV_CMD_NOTE_OFF:
@ -246,6 +249,10 @@ void* DivPlatformPET::getChanState(int ch) {
return &chan;
}
DivMacroInt* DivPlatformPET::getChanMacroInt(int ch) {
return &chan.std;
}
DivDispatchOscBuffer* DivPlatformPET::getOscBuffer(int ch) {
return oscBuf;
}

View file

@ -66,6 +66,7 @@ class DivPlatformPET: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -404,6 +404,9 @@ int DivPlatformQSound::dispatch(DivCommand c) {
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;
}
break;
}
case DIV_CMD_NOTE_OFF:
@ -527,6 +530,10 @@ void* DivPlatformQSound::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformQSound::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -78,6 +78,7 @@ class DivPlatformQSound: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -189,6 +189,9 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
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;
}
break;
}
case DIV_CMD_NOTE_OFF:
@ -297,6 +300,10 @@ void* DivPlatformRF5C68::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformRF5C68::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformRF5C68::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -75,6 +75,7 @@ class DivPlatformRF5C68: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -136,7 +136,7 @@ void DivPlatformSAA1099::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (isMuted[i]) {
rWrite(i,0);
@ -264,6 +264,9 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
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;
}
if (isMuted[c.chan]) {
rWrite(c.chan,0);
} else {
@ -398,6 +401,10 @@ void* DivPlatformSAA1099::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSAA1099::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSAA1099::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -91,6 +91,7 @@ class DivPlatformSAA1099: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -184,6 +184,9 @@ int DivPlatformSCC::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (!isMuted[c.chan]) {
rWrite(regBase+15,regPool[regBase+15]|(1<<c.chan));
}
@ -307,6 +310,10 @@ void* DivPlatformSCC::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSCC::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSCC::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -70,6 +70,7 @@ class DivPlatformSCC: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// TODO: new macro formula
#include "segapcm.h"
#include "../engine.h"
#include <string.h>
@ -84,10 +85,17 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
// TODO: fix
/*if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
}*/
if (parent->song.newSegaPCM) {
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(64,chan[i].std.vol.val))>>6;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
if (dumpWrites) {
addWrite(0x10002+(i<<3),chan[i].chVolL);
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
@ -106,14 +114,24 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
}
if (chan[i].std.panL.had) {
chan[i].chVolL=chan[i].std.panL.val&127;
if (parent->song.newSegaPCM) {
chan[i].chPanL=chan[i].std.panL.val&127;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
} else {
chan[i].chVolL=chan[i].std.panL.val&127;
}
if (dumpWrites) {
addWrite(0x10002+(i<<3),chan[i].chVolL);
}
}
if (chan[i].std.panR.had) {
chan[i].chVolR=chan[i].std.panR.val&127;
if (parent->song.newSegaPCM) {
chan[i].chPanR=chan[i].std.panR.val&127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
} else {
chan[i].chVolR=chan[i].std.panR.val&127;
}
if (dumpWrites) {
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
@ -169,6 +187,9 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
addWrite(0x10086+(c.chan<<3),3);
}
chan[c.chan].macroInit(NULL);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
}
chan[c.chan].pcm.pos=0;
@ -257,8 +278,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value;
if (parent->song.newSegaPCM) {
chan[c.chan].chVolL=(c.value*chan[c.chan].chPanL)/127;
chan[c.chan].chVolR=(c.value*chan[c.chan].chPanR)/127;
} else {
chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value;
}
if (dumpWrites) {
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
@ -276,8 +302,15 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
chan[c.chan].chVolL=c.value>>1;
chan[c.chan].chVolR=c.value2>>1;
if (parent->song.newSegaPCM) {
chan[c.chan].chPanL=c.value>>1;
chan[c.chan].chPanR=c.value2>>1;
chan[c.chan].chVolL=(chan[c.chan].outVol*chan[c.chan].chPanL)/127;
chan[c.chan].chVolR=(chan[c.chan].outVol*chan[c.chan].chPanR)/127;
} else {
chan[c.chan].chVolL=c.value>>1;
chan[c.chan].chVolR=c.value2>>1;
}
if (dumpWrites) {
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
@ -367,6 +400,10 @@ void* DivPlatformSegaPCM::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSegaPCM::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -34,6 +34,7 @@ class DivPlatformSegaPCM: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM;
int vol, outVol;
unsigned char chVolL, chVolR;
unsigned char chPanL, chPanR;
struct PCMChannel {
int sample;
@ -46,7 +47,29 @@ class DivPlatformSegaPCM: public DivDispatch {
std.init(which);
pitch2=0;
}
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {}
Channel():
freqH(0),
freqL(0),
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
note(0),
ins(-1),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),
portaPause(false),
furnacePCM(false),
vol(0),
outVol(0),
chVolL(127),
chVolR(127),
chPanL(127),
chPanR(127) {}
};
Channel chan[16];
DivDispatchOscBuffer* oscBuf[16];
@ -78,6 +101,7 @@ class DivPlatformSegaPCM: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -69,14 +69,13 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
if (o<-32768) o=-32768;
if (o>32767) o=32767;
bufL[h]=o;
/*
for (int i=0; i<4; i++) {
if (isMuted[i]) {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i);
oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]];
}
}*/
}
}
}
@ -245,6 +244,9 @@ int DivPlatformSMS::dispatch(DivCommand c) {
chan[c.chan].active=true;
rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -316,6 +318,8 @@ int DivPlatformSMS::dispatch(DivCommand c) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
chan[c.chan].inPorta=c.value;
// TODO: pre porta cancel arp compat flag
//if (chan[c.chan].inPorta) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
break;
case DIV_CMD_GET_VOLMAX:
return 15;
@ -348,6 +352,10 @@ void* DivPlatformSMS::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSMS::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -76,6 +76,7 @@ class DivPlatformSMS: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Konami K005289 emulation core
This chip is used at infamous Konami Bubble System, for part of Wavetable sound generator.

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Konami K005289 emulation core
See k005289.cpp for more info.

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900, tildearrow
Namco 163 Sound emulation core
This chip is one of NES mapper with sound expansion, This one is by Namco.
@ -99,7 +100,7 @@ void n163_core::tick()
m_ram[m_voice_cycle + 3] = bitfield(accum, 8, 8);
m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8);
const u8 prev_voice_cycle = m_voice_cycle;
const u8 prev_voice_cycle = m_voice_cycle;
// update voice cycle
bool flush = m_multiplex ? true : false;
@ -112,8 +113,8 @@ void n163_core::tick()
}
// output 4 bit waveform and volume, multiplexed
const u8 chan_index = ((0x78-prev_voice_cycle)>>3)&7;
m_ch_out[chan_index]=wave * volume;
const u8 chan_index = ((0x78-prev_voice_cycle)>>3)&7;
m_ch_out[chan_index]=wave * volume;
m_acc += m_ch_out[chan_index];
if (flush)
{
@ -127,12 +128,12 @@ void n163_core::reset()
// reset this chip
m_disable = false;
m_multiplex = true;
memset(m_ram,0,sizeof(m_ram));
memset(m_ram,0,sizeof(m_ram));
m_voice_cycle = 0x78;
m_addr_latch.reset();
m_out = 0;
m_acc = 0;
memset(m_ch_out,0,sizeof(m_ch_out));
memset(m_ch_out,0,sizeof(m_ch_out));
}
// accessor

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900, tildearrow
Namco 163 Sound emulation core
*/
@ -46,11 +47,11 @@ public:
// sound output pin
s16 out() { return m_out; }
// get channel output
s16 chan_out(u8 ch) { return m_ch_out[ch]; }
// get channel output
s16 chan_out(u8 ch) { return m_ch_out[ch]; }
// get voice cycle
u8 voice_cycle() { return m_voice_cycle; }
// get voice cycle
u8 voice_cycle() { return m_voice_cycle; }
// register pool
u8 reg(u8 addr) { return m_ram[addr & 0x7f]; }
@ -63,7 +64,7 @@ private:
addr_latch_t()
: addr(0)
, incr(0)
{ };
{ }
void reset()
{
@ -80,7 +81,7 @@ private:
u8 m_voice_cycle = 0x78; // Voice cycle for processing
addr_latch_t m_addr_latch; // address latch
s16 m_out = 0; // output
s16 m_ch_out[8] = {0}; // per channel output
s16 m_ch_out[8] = {0}; // per channel output
// demultiplex related
bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate!
s16 m_acc = 0; // accumulated output

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: tildearrow
OKI MSM6295 emulation core
It is 4 channel ADPCM playback chip from OKI semiconductor.

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: tildearrow
OKI MSM6295 emulation core
See msm6295.cpp for more info.
@ -71,7 +72,7 @@ public:
msm6295_core &m_host;
u16 m_clock = 0; // clock counter
bool m_busy = false; // busy status
bool m_muted = false; // muted
bool m_muted = false; // muted
u8 m_command = 0; // current command
u32 m_addr = 0; // current address
s8 m_nibble = 0; // current nibble

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holders: cam900
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: tildearrow
Various core utilities for vgsound_emu
*/

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/main/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: tildearrow
Dialogic ADPCM core
*/

View file

@ -1,8 +1,10 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst
Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw
Konami SCC emulation core
Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator.
@ -373,12 +375,12 @@ void scc_core::reset()
m_test.reset();
m_out = 0;
memset(m_reg,0,sizeof(m_reg));
memset(m_reg,0,sizeof(m_reg));
}
void scc_core::voice_t::reset()
{
memset(wave,0,sizeof(wave));
memset(wave,0,sizeof(wave));
enable = false;
pitch = 0;
volume = 0;

View file

@ -1,8 +1,10 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Contributor(s): Natt Akuma, James Alan Nguyen, Laurens Holst
Modifiers and Contributors for Furnace: Natt Akuma, tildearrow, Grauw
Konami SCC emulation core
See scc.cpp for more info.

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900, tildearrow
Konami VRC VI sound emulation core
It's one of NES mapper with built-in sound chip, and also one of 2 Konami VRCs with this feature. (rest one has OPLL derivatives.)

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900, tildearrow
Konami VRC VI sound emulation core
See vrcvi.cpp to more infos.

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holders: cam900
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900, tildearrow
Seta/Allumer X1-010 Emulation core
the chip has 16 voices, all voices can be switchable to Wavetable or PCM sample playback mode.

View file

@ -1,8 +1,9 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/LICENSE for more details
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holders: cam900
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900, tildearrow
Seta/Allumer X1-010 Emulation core
See x1_010.cpp for more info.
@ -63,7 +64,7 @@ public:
// getters
s32 output(u8 channel) { return m_out[channel & 1]; }
s32 chan_out(u8 channel) { return (m_voice[channel].data * (m_voice[channel].vol_out[0]+m_voice[channel].vol_out[1]))<<2; }
s32 chan_out(u8 channel) { return (m_voice[channel].data * (m_voice[channel].vol_out[0]+m_voice[channel].vol_out[1]))<<2; }
// internal state
void reset();

View file

@ -778,6 +778,10 @@ public:
// get the engine
fm_engine* debug_engine() { return &m_fm; }
// get DAC state
uint16_t debug_dac_data() { return m_dac_data; }
uint8_t debug_dac_enable() { return m_dac_enable; }
protected:
// simulate the DAC discontinuity
constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); }

View file

@ -264,6 +264,9 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chWrite(c.chan,0x02,chan[c.chan].vol);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
chan[c.chan].insChanged=false;
break;
}
@ -481,6 +484,10 @@ void* DivPlatformSoundUnit::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSoundUnit::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSoundUnit::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -112,6 +112,7 @@ class DivPlatformSoundUnit: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -313,6 +313,9 @@ int DivPlatformSwan::dispatch(DivCommand c) {
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;
}
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
@ -461,6 +464,10 @@ void* DivPlatformSwan::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSwan::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -79,6 +79,7 @@ class DivPlatformSwan: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -88,7 +88,7 @@ void DivPlatformTIA::tick(bool sysTick) {
for (int i=0; i<2; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (isMuted[i]) {
rWrite(0x19+i,0);
@ -170,6 +170,9 @@ int DivPlatformTIA::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
rWrite(0x15+c.chan,chan[c.chan].shape);
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (isMuted[c.chan]) {
rWrite(0x19+c.chan,0);
} else {
@ -290,6 +293,10 @@ void* DivPlatformTIA::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformTIA::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformTIA::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -52,6 +52,7 @@ class DivPlatformTIA: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -270,7 +270,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -278,7 +278,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -354,7 +354,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -407,7 +407,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -499,7 +499,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -529,7 +529,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -594,7 +594,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -694,7 +694,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -985,7 +985,7 @@ void DivPlatformTX81Z::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1027,6 +1027,10 @@ void* DivPlatformTX81Z::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformTX81Z::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformTX81Z::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -103,6 +103,7 @@ class DivPlatformTX81Z: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -285,6 +285,9 @@ int DivPlatformVERA::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_VERA));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
break;
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
@ -396,6 +399,10 @@ void* DivPlatformVERA::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformVERA::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformVERA::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -66,6 +66,7 @@ class DivPlatformVERA: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -278,6 +278,10 @@ void* DivPlatformVIC20::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformVIC20::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformVIC20::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -68,6 +68,7 @@ class DivPlatformVIC20: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -264,6 +264,9 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
}
chan[c.chan].active=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].keyOn=true;
chan[c.chan].furnaceDac=true;
} else {
@ -435,6 +438,10 @@ void* DivPlatformVRC6::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformVRC6::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformVRC6::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -85,6 +85,7 @@ class DivPlatformVRC6: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -609,6 +609,9 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
chan[c.chan].keyOn=true;
chan[c.chan].envChanged=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
@ -839,6 +842,10 @@ void* DivPlatformX1_010::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformX1_010::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -129,6 +129,7 @@ class DivPlatformX1_010: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -338,7 +338,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -346,7 +346,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -396,7 +396,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -441,7 +441,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -553,7 +553,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -612,7 +612,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -690,7 +690,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -882,7 +882,7 @@ void DivPlatformYM2203::muteChannel(int ch, bool mute) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -899,7 +899,7 @@ void DivPlatformYM2203::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -933,6 +933,11 @@ void* DivPlatformYM2203::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2203::getChanMacroInt(int ch) {
if (ch>=3) return ay->getChanMacroInt(ch-3);
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2203::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -108,6 +108,7 @@ class DivPlatformYM2203: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View file

@ -45,7 +45,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -84,7 +84,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -155,6 +155,11 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
break;
}
case DIV_CMD_FM_FB: {
chan[2].state.fb=c.value&7;
rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
break;
}
case DIV_CMD_FM_MULT: { // TODO
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
@ -411,7 +416,7 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
if (isOpMuted[ch-2]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -426,7 +431,7 @@ void DivPlatformYM2203Ext::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -466,6 +471,12 @@ void* DivPlatformYM2203Ext::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2203Ext::getChanMacroInt(int ch) {
if (ch>=6) return ay->getChanMacroInt(ch-6);
if (ch>=2) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];

View file

@ -40,6 +40,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View file

@ -500,12 +500,12 @@ void DivPlatformYM2608::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -559,7 +559,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -609,7 +609,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -836,7 +836,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -916,7 +916,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1020,7 +1020,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1223,7 +1223,7 @@ void DivPlatformYM2608::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1257,6 +1257,11 @@ void* DivPlatformYM2608::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2608::getChanMacroInt(int ch) {
if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6);
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2608::getOscBuffer(int ch) {
return oscBuf[ch];
}

View file

@ -121,6 +121,7 @@ class DivPlatformYM2608: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

Some files were not shown because too many files have changed in this diff Show more