Merge branch 'master' into es5506_alt
This commit is contained in:
commit
f3b9c6dde6
647 changed files with 4326 additions and 256 deletions
|
|
@ -37,7 +37,7 @@
|
|||
#endif
|
||||
#include <math.h>
|
||||
#ifdef HAVE_SNDFILE
|
||||
#include <sndfile.h>
|
||||
#include "sfWrapper.h"
|
||||
#endif
|
||||
#include <fmt/printf.h>
|
||||
|
||||
|
|
@ -200,11 +200,12 @@ void DivEngine::runExportThread() {
|
|||
case DIV_EXPORT_MODE_ONE: {
|
||||
SNDFILE* sf;
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
si.samplerate=got.rate;
|
||||
si.channels=2;
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
||||
|
||||
sf=sf_open(exportPath.c_str(),SFM_WRITE,&si);
|
||||
sf=sfWrap.doOpen(exportPath.c_str(),SFM_WRITE,&si);
|
||||
if (sf==NULL) {
|
||||
logE("could not open file for writing! (%s)",sf_strerror(NULL));
|
||||
exporting=false;
|
||||
|
|
@ -259,7 +260,7 @@ void DivEngine::runExportThread() {
|
|||
delete[] outBuf[1];
|
||||
delete[] outBuf[2];
|
||||
|
||||
if (sf_close(sf)!=0) {
|
||||
if (sfWrap.doClose()!=0) {
|
||||
logE("could not close audio file!");
|
||||
}
|
||||
exporting=false;
|
||||
|
|
@ -280,6 +281,7 @@ void DivEngine::runExportThread() {
|
|||
SNDFILE* sf[32];
|
||||
SF_INFO si[32];
|
||||
String fname[32];
|
||||
SFWrapper sfWrap[32];
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
sf[i]=NULL;
|
||||
si[i].samplerate=got.rate;
|
||||
|
|
@ -294,11 +296,11 @@ void DivEngine::runExportThread() {
|
|||
for (int i=0; i<song.systemLen; i++) {
|
||||
fname[i]=fmt::sprintf("%s_s%02d.wav",exportPath,i+1);
|
||||
logI("- %s",fname[i].c_str());
|
||||
sf[i]=sf_open(fname[i].c_str(),SFM_WRITE,&si[i]);
|
||||
sf[i]=sfWrap[i].doOpen(fname[i].c_str(),SFM_WRITE,&si[i]);
|
||||
if (sf[i]==NULL) {
|
||||
logE("could not open file for writing! (%s)",sf_strerror(NULL));
|
||||
for (int j=0; j<i; j++) {
|
||||
sf_close(sf[i]);
|
||||
sfWrap[i].doClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -369,7 +371,7 @@ void DivEngine::runExportThread() {
|
|||
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
delete[] sysBuf[i];
|
||||
if (sf_close(sf[i])!=0) {
|
||||
if (sfWrap[i].doClose()!=0) {
|
||||
logE("could not close audio file!");
|
||||
}
|
||||
}
|
||||
|
|
@ -402,13 +404,14 @@ void DivEngine::runExportThread() {
|
|||
for (int i=0; i<chans; i++) {
|
||||
SNDFILE* sf;
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
String fname=fmt::sprintf("%s_c%02d.wav",exportPath,i+1);
|
||||
logI("- %s",fname.c_str());
|
||||
si.samplerate=got.rate;
|
||||
si.channels=2;
|
||||
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
|
||||
|
||||
sf=sf_open(fname.c_str(),SFM_WRITE,&si);
|
||||
sf=sfWrap.doOpen(fname.c_str(),SFM_WRITE,&si);
|
||||
if (sf==NULL) {
|
||||
logE("could not open file for writing! (%s)",sf_strerror(NULL));
|
||||
break;
|
||||
|
|
@ -473,7 +476,7 @@ void DivEngine::runExportThread() {
|
|||
}
|
||||
}
|
||||
|
||||
if (sf_close(sf)!=0) {
|
||||
if (sfWrap.doClose()!=0) {
|
||||
logE("could not close audio file!");
|
||||
}
|
||||
|
||||
|
|
@ -482,6 +485,7 @@ void DivEngine::runExportThread() {
|
|||
while (true) {
|
||||
if (i>=chans) break;
|
||||
if (getChannelType(i)!=5) break;
|
||||
i++;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
|
@ -1751,6 +1755,10 @@ bool DivEngine::isPlaying() {
|
|||
return (playing && !freelance);
|
||||
}
|
||||
|
||||
bool DivEngine::isRunning() {
|
||||
return playing;
|
||||
}
|
||||
|
||||
bool DivEngine::isStepping() {
|
||||
return !(stepPlay==0);
|
||||
}
|
||||
|
|
@ -2200,8 +2208,9 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
return -1;
|
||||
#else
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
memset(&si,0,sizeof(SF_INFO));
|
||||
SNDFILE* f=sf_open(path,SFM_READ,&si);
|
||||
SNDFILE* f=sfWrap.doOpen(path,SFM_READ,&si);
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
int err=sf_error(NULL);
|
||||
|
|
@ -2214,7 +2223,7 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
}
|
||||
if (si.frames>16777215) {
|
||||
lastError="this sample is too big! max sample size is 16777215.";
|
||||
sf_close(f);
|
||||
sfWrap.doClose();
|
||||
BUSY_END;
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -2314,7 +2323,7 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
|
||||
if (sample->centerRate<4000) sample->centerRate=4000;
|
||||
if (sample->centerRate>64000) sample->centerRate=64000;
|
||||
sf_close(f);
|
||||
sfWrap.doClose();
|
||||
saveLock.lock();
|
||||
song.sample.push_back(sample);
|
||||
song.sampleLen=sampleCount+1;
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev99"
|
||||
#define DIV_ENGINE_VERSION 99
|
||||
#define DIV_VERSION "0.6pre1"
|
||||
#define DIV_ENGINE_VERSION 100
|
||||
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
|
|
@ -88,7 +88,7 @@ struct DivChannelState {
|
|||
int tremoloDepth, tremoloRate, tremoloPos;
|
||||
unsigned char arp, arpStage, arpTicks, panL, panR;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, noteOnInhibit, resetArp;
|
||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
||||
|
||||
int midiNote, curMidiNote, midiPitch;
|
||||
size_t midiAge;
|
||||
|
|
@ -136,6 +136,7 @@ struct DivChannelState {
|
|||
inPorta(false),
|
||||
scheduledSlideReset(false),
|
||||
shorthandPorta(false),
|
||||
wasShorthandPorta(false),
|
||||
noteOnInhibit(false),
|
||||
resetArp(false),
|
||||
midiNote(-1),
|
||||
|
|
@ -656,6 +657,9 @@ class DivEngine {
|
|||
|
||||
// is playing
|
||||
bool isPlaying();
|
||||
|
||||
// is running
|
||||
bool isRunning();
|
||||
|
||||
// is stepping
|
||||
bool isStepping();
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.newVolumeScaling=false;
|
||||
ds.volMacroLinger=false;
|
||||
ds.brokenOutVol=true; // ???
|
||||
ds.e1e2StopOnSameNote=true;
|
||||
|
||||
// 1.1 compat flags
|
||||
if (ds.version>24) {
|
||||
|
|
@ -1043,6 +1044,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
ds.volMacroLinger=false;
|
||||
ds.brokenOutVol=true;
|
||||
}
|
||||
if (ds.version<100) {
|
||||
ds.e1e2StopOnSameNote=false;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
|
@ -1439,7 +1443,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
reader.readC();
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<9; i++) {
|
||||
if (ds.version>=100) {
|
||||
ds.e1e2StopOnSameNote=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
reader.readC();
|
||||
}
|
||||
}
|
||||
|
|
@ -2709,7 +2718,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
std::vector<int> wavePtr;
|
||||
std::vector<int> samplePtr;
|
||||
std::vector<int> patPtr;
|
||||
size_t ptrSeek, subSongPtrSeek;
|
||||
size_t ptrSeek, subSongPtrSeek, blockStartSeek, blockEndSeek;
|
||||
size_t subSongIndex=0;
|
||||
DivSubSong* subSong=song.subsong[subSongIndex];
|
||||
warnings="";
|
||||
|
|
@ -2788,6 +2797,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
|
||||
/// SONG INFO
|
||||
w->write("INFO",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
|
|
@ -2924,7 +2934,8 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeC(song.newVolumeScaling);
|
||||
w->writeC(song.volMacroLinger);
|
||||
w->writeC(song.brokenOutVol);
|
||||
for (int i=0; i<9; i++) {
|
||||
w->writeC(song.e1e2StopOnSameNote);
|
||||
for (int i=0; i<8; i++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
|
|
@ -2945,11 +2956,17 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI(0);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
|
||||
/// SUBSONGS
|
||||
for (subSongIndex=1; subSongIndex<song.subsong.size(); subSongIndex++) {
|
||||
subSong=song.subsong[subSongIndex];
|
||||
subSongPtr.push_back(w->tell());
|
||||
w->write("SONG",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(subSong->timeBase);
|
||||
|
|
@ -2992,6 +3009,11 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
for (int i=0; i<chans; i++) {
|
||||
w->writeString(subSong->chanShortName[i],false);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
/// INSTRUMENT
|
||||
|
|
@ -3013,6 +3035,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
DivSample* sample=song.sample[i];
|
||||
samplePtr.push_back(w->tell());
|
||||
w->write("SMPL",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeString(sample->name,false);
|
||||
|
|
@ -3025,6 +3048,11 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
w->writeI((sample->loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?-1:sample->loopStart);
|
||||
|
||||
w->write(sample->getCurBuf(),sample->getCurBufLen());
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
/// PATTERN
|
||||
|
|
@ -3032,6 +3060,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
DivPattern* pat=song.subsong[i.subsong]->pat[i.chan].getPattern(i.pat,false);
|
||||
patPtr.push_back(w->tell());
|
||||
w->write("PATR",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(i.chan);
|
||||
|
|
@ -3049,6 +3078,11 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
|
||||
w->writeString(pat->name,false);
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
/// POINTERS
|
||||
|
|
|
|||
|
|
@ -192,7 +192,13 @@ void DivEngine::loadDMP(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
ins->fm.ops=reader.readC()?4:2;
|
||||
}
|
||||
} else {
|
||||
ins->fm.ops=reader.readC()?2:4;
|
||||
// HELP
|
||||
if (reader.size()==49) {
|
||||
ins->fm.ops=4;
|
||||
reader.readC();
|
||||
} else {
|
||||
ins->fm.ops=reader.readC()?2:4;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ins->fm.ops=4;
|
||||
|
|
@ -835,8 +841,9 @@ void DivEngine::loadY12(SafeReader& reader, std::vector<DivInstrument*>& ret, St
|
|||
DivInstrumentFM::Operator& insOp = ins->fm.op[i];
|
||||
uint8_t tmp = reader.readC();
|
||||
insOp.mult = tmp & 0xF;
|
||||
insOp.dt = ((tmp >> 4) & 0x7);
|
||||
insOp.tl = (reader.readC() & 0x3F);
|
||||
// ???
|
||||
insOp.dt = ((3 + (tmp >> 4)) & 0x7);
|
||||
insOp.tl = (reader.readC() & 0x7F);
|
||||
tmp = reader.readC();
|
||||
insOp.rs = ((tmp >> 6) & 0x3);
|
||||
insOp.ar = tmp & 0x1F;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@
|
|||
#include "../fileutils.h"
|
||||
|
||||
void DivInstrument::putInsData(SafeWriter* w) {
|
||||
size_t blockStartSeek, blockEndSeek;
|
||||
|
||||
w->write("INST",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeS(DIV_ENGINE_VERSION);
|
||||
|
|
@ -524,6 +527,11 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
for (int j=0; j<23; j++) { // reserved
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
||||
|
|
|
|||
|
|
@ -104,12 +104,14 @@ class DivPlatformOPN: public DivPlatformFMBase {
|
|||
double fmFreqBase;
|
||||
unsigned int fmDivBase;
|
||||
unsigned int ayDiv;
|
||||
bool extSys;
|
||||
|
||||
DivPlatformOPN(double f=9440540.0, unsigned int d=72, unsigned int a=32):
|
||||
DivPlatformOPN(double f=9440540.0, unsigned int d=72, unsigned int a=32, bool isExtSys=false):
|
||||
DivPlatformFMBase(),
|
||||
fmFreqBase(f),
|
||||
fmDivBase(d),
|
||||
ayDiv(a) {}
|
||||
ayDiv(a),
|
||||
extSys(isExtSys) {}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -344,13 +344,17 @@ void DivPlatformGenesis::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::tick(bool sysTick) {
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int i=0; i<(softPCM?7:6); i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
chan[i].std.next();
|
||||
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
|
||||
for (int j=0; j<4; j++) {
|
||||
int inVol=chan[i].std.vol.val;
|
||||
if (chan[i].furnaceDac && inVol>0) {
|
||||
inVol+=63;
|
||||
}
|
||||
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,inVol),127);
|
||||
if (i<6) for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isMuted[i]) {
|
||||
|
|
@ -383,7 +387,9 @@ 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,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (i<6) {
|
||||
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) {
|
||||
|
|
@ -396,6 +402,8 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
if (i>=6) continue;
|
||||
|
||||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
|
|
@ -640,9 +648,20 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
chan[c.chan].dacPeriod=0;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
chan[c.chan].furnaceDac=true;
|
||||
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
|
||||
// ???
|
||||
//chan[c.chan].keyOn=true;
|
||||
chan[c.chan].active=true;
|
||||
} else { // compatible mode
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -893,6 +912,13 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
if (c.chan>=6) break;
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
if (!extMode) {
|
||||
c.chan=2;
|
||||
return DivPlatformGenesis::dispatch(c);
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
|
@ -173,6 +177,11 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
|
|
@ -489,7 +498,7 @@ void DivPlatformGenesisExt::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==2) { // extended channel
|
||||
if (i==2 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
|
|
@ -592,6 +601,7 @@ int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate, un
|
|||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
extSys=true;
|
||||
|
||||
reset();
|
||||
return 13;
|
||||
|
|
|
|||
|
|
@ -299,18 +299,39 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
if (ch==255) continue;
|
||||
oscBuf[i]->data[oscBuf[i]->needle]=0;
|
||||
if (fm.channel[i].out[0]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
|
||||
if (fm.rhy&0x20) {
|
||||
for (int i=0; i<melodicChans+1; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
if (ch==255) continue;
|
||||
oscBuf[i]->data[oscBuf[i]->needle]=0;
|
||||
if (fm.channel[i].out[0]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
|
||||
}
|
||||
if (fm.channel[i].out[1]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
|
||||
}
|
||||
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
|
||||
oscBuf[i]->needle++;
|
||||
}
|
||||
if (fm.channel[i].out[1]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
|
||||
// special
|
||||
oscBuf[melodicChans+1]->data[oscBuf[melodicChans+1]->needle++]=fm.slot[16].out*6;
|
||||
oscBuf[melodicChans+2]->data[oscBuf[melodicChans+2]->needle++]=fm.slot[14].out*6;
|
||||
oscBuf[melodicChans+3]->data[oscBuf[melodicChans+3]->needle++]=fm.slot[17].out*6;
|
||||
oscBuf[melodicChans+4]->data[oscBuf[melodicChans+4]->needle++]=fm.slot[13].out*6;
|
||||
} else {
|
||||
for (int i=0; i<chans; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
if (ch==255) continue;
|
||||
oscBuf[i]->data[oscBuf[i]->needle]=0;
|
||||
if (fm.channel[i].out[0]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[0];
|
||||
}
|
||||
if (fm.channel[i].out[1]!=NULL) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle]+=*fm.channel[ch].out[1];
|
||||
}
|
||||
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
|
||||
oscBuf[i]->needle++;
|
||||
}
|
||||
oscBuf[i]->data[oscBuf[i]->needle]<<=1;
|
||||
oscBuf[i]->needle++;
|
||||
}
|
||||
|
||||
if (os[0]<-32768) os[0]=-32768;
|
||||
|
|
@ -601,8 +622,9 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq)*2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>131071) chan[i].freq=131071;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
int freqt=toFreq(chan[i].freq);
|
||||
chan[i].freqH=freqt>>8;
|
||||
chan[i].freqL=freqt&0xff;
|
||||
immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL);
|
||||
|
|
@ -1517,7 +1539,7 @@ void DivPlatformOPL::reset() {
|
|||
}
|
||||
*/
|
||||
if (downsample) {
|
||||
const unsigned int downsampledRate=(unsigned int)(49716.0*(double(rate)/chipRateBase));
|
||||
const unsigned int downsampledRate=(unsigned int)((double)rate*rate/chipRateBase);
|
||||
OPL3_Reset(&fm,downsampledRate);
|
||||
} else {
|
||||
OPL3_Reset(&fm,rate);
|
||||
|
|
@ -1647,7 +1669,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
slots=drums?slotsDrums:slotsNonDrums;
|
||||
chanMap=drums?chanMapOPL2Drums:chanMapOPL2;
|
||||
outChanMap=outChanMapOPL2;
|
||||
chipFreqBase=9440540*0.25;
|
||||
chipFreqBase=32768*72;
|
||||
chans=9;
|
||||
melodicChans=drums?6:9;
|
||||
totalChans=drums?11:9;
|
||||
|
|
@ -1661,7 +1683,7 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
slots=drums?slotsDrums:slotsNonDrums;
|
||||
chanMap=drums?chanMapOPL3Drums:chanMapOPL3;
|
||||
outChanMap=outChanMapOPL3;
|
||||
chipFreqBase=9440540;
|
||||
chipFreqBase=32768*288;
|
||||
chans=18;
|
||||
melodicChans=drums?15:18;
|
||||
totalChans=drums?20:18;
|
||||
|
|
@ -1713,9 +1735,6 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
|
|||
default:
|
||||
case 1: case 2: case 8950:
|
||||
switch (flags&0xff) {
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
break;
|
||||
|
|
@ -1731,15 +1750,15 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
|
|||
case 0x05:
|
||||
chipClock=3500000.0;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/72;
|
||||
chipRateBase=double(rate);
|
||||
chipRateBase=rate;
|
||||
break;
|
||||
case 3:
|
||||
switch (flags&0xff) {
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC*4.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*16.0/5.0;
|
||||
break;
|
||||
|
|
@ -1752,33 +1771,36 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
|
|||
case 0x04:
|
||||
chipClock=15000000.0;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC*4.0;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/288;
|
||||
chipRateBase=double(rate);
|
||||
chipRateBase=rate;
|
||||
break;
|
||||
case 4:
|
||||
switch (flags&0xff) {
|
||||
case 0x02:
|
||||
chipClock=33868800.0;
|
||||
break;
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC*8.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*32.0/5.0;
|
||||
break;
|
||||
case 0x02:
|
||||
chipClock=33868800.0;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC*8.0;
|
||||
break;
|
||||
}
|
||||
chipRateBase=double(chipClock)/684.0;
|
||||
rate=chipClock/768;
|
||||
chipRateBase=chipClock/684;
|
||||
break;
|
||||
case 759:
|
||||
rate=48000;
|
||||
chipRateBase=double(rate);
|
||||
chipRateBase=rate;
|
||||
chipClock=rate*288;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i=0; i<18; i++) {
|
||||
for (int i=0; i<20; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
|
@ -1829,7 +1851,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f
|
|||
for (int i=0; i<20; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
for (int i=0; i<18; i++) {
|
||||
for (int i=0; i<20; i++) {
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
|
|
@ -1847,7 +1869,7 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f
|
|||
}
|
||||
|
||||
void DivPlatformOPL::quit() {
|
||||
for (int i=0; i<18; i++) {
|
||||
for (int i=0; i<20; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
if (adpcmChan>=0) {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
}
|
||||
};
|
||||
Channel chan[20];
|
||||
DivDispatchOscBuffer* oscBuf[18];
|
||||
DivDispatchOscBuffer* oscBuf[20];
|
||||
bool isMuted[20];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
|
|
@ -95,7 +95,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
const unsigned char** slots;
|
||||
const unsigned short* chanMap;
|
||||
const unsigned char* outChanMap;
|
||||
double chipFreqBase, chipRateBase;
|
||||
int chipFreqBase, chipRateBase;
|
||||
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank;
|
||||
unsigned char lastBusy;
|
||||
unsigned char drumState;
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@ const unsigned char drumSlot[11]={
|
|||
0, 0, 0, 0, 0, 0, 6, 7, 8, 8, 7
|
||||
};
|
||||
|
||||
const unsigned char visMapOPLL[9]={
|
||||
6, 7, 8, 3, 4, 5, 0, 1, 2
|
||||
};
|
||||
|
||||
void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
static int o[2];
|
||||
static int os;
|
||||
|
|
@ -124,10 +128,18 @@ void DivPlatformOPLL::acquire_nuked(short* bufL, short* bufR, size_t start, size
|
|||
OPLL_Clock(&fm,o);
|
||||
unsigned char nextOut=cycleMapOPLL[fm.cycles];
|
||||
if ((nextOut>=6 && properDrums) || !isMuted[nextOut]) {
|
||||
oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
|
||||
os+=(o[0]+o[1]);
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=(o[0]+o[1])<<6;
|
||||
} else {
|
||||
oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
|
||||
if (vrc7 || (fm.rm_enable&0x20)) oscBuf[nextOut]->data[oscBuf[nextOut]->needle++]=0;
|
||||
}
|
||||
}
|
||||
if (!(vrc7 || (fm.rm_enable&0x20))) for (int i=0; i<9; i++) {
|
||||
unsigned char ch=visMapOPLL[i];
|
||||
if ((i>=6 && properDrums) || !isMuted[ch]) {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=(fm.output_ch[i])<<6;
|
||||
} else {
|
||||
oscBuf[ch]->data[oscBuf[ch]->needle++]=0;
|
||||
}
|
||||
}
|
||||
os*=50;
|
||||
|
|
|
|||
|
|
@ -48,12 +48,15 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) {
|
|||
}
|
||||
|
||||
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
int o=0;
|
||||
int oL=0;
|
||||
int oR=0;
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
if (w.addr==0) {
|
||||
YMPSG_Write(&sn_nuked,w.val);
|
||||
} else if (w.addr==1) {
|
||||
YMPSG_WriteStereo(&sn_nuked,w.val);
|
||||
}
|
||||
writes.pop();
|
||||
}
|
||||
|
|
@ -73,10 +76,13 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
o=YMPSG_GetOutput(&sn_nuked);
|
||||
if (o<-32768) o=-32768;
|
||||
if (o>32767) o=32767;
|
||||
bufL[h]=bufR[h]=o;
|
||||
YMPSG_GetOutput(&sn_nuked,&oL,&oR);
|
||||
if (oL<-32768) oL=-32768;
|
||||
if (oL>32767) oL=32767;
|
||||
if (oR<-32768) oR=-32768;
|
||||
if (oR>32767) oR=32767;
|
||||
bufL[h]=oL;
|
||||
bufR[h]=oR;
|
||||
for (int i=0; i<4; i++) {
|
||||
if (isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ void
|
|||
psg_reset(struct VERA_PSG* psg)
|
||||
{
|
||||
memset(psg->channels, 0, sizeof(psg->channels));
|
||||
psg->noiseState=1;
|
||||
psg->noiseOut=0;
|
||||
psg->noiseOut = 0;
|
||||
psg->noiseState = 1;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -54,10 +54,14 @@ render(struct VERA_PSG* psg, int16_t *left, int16_t *right)
|
|||
{
|
||||
int l = 0;
|
||||
int r = 0;
|
||||
psg->noiseOut=((psg->noiseOut<<1)|(psg->noiseState&1))&63;
|
||||
psg->noiseState=(psg->noiseState<<1)|(((psg->noiseState>>1)^(psg->noiseState>>2)^(psg->noiseState>>4)^(psg->noiseState>>15))&1);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
// In FPGA implementation, noise values are generated every system clock and
|
||||
// the channel update is run sequentially. So, even if both two channels are
|
||||
// fetching a noise value in the same sample, they should have different values
|
||||
psg->noiseOut = ((psg->noiseOut << 1) | (psg->noiseState & 1)) & 63;
|
||||
psg->noiseState = (psg->noiseState << 1) | (((psg->noiseState >> 1) ^ (psg->noiseState >> 2) ^ (psg->noiseState >> 4) ^ (psg->noiseState >> 15)) & 1);
|
||||
|
||||
struct VERAChannel *ch = &psg->channels[i];
|
||||
|
||||
unsigned new_phase = (ch->phase + ch->freq) & 0x1FFFF;
|
||||
|
|
@ -87,11 +91,11 @@ render(struct VERA_PSG* psg, int16_t *left, int16_t *right)
|
|||
r += val;
|
||||
}
|
||||
|
||||
if (ch->left || ch->right) {
|
||||
ch->lastOut=val;
|
||||
} else {
|
||||
ch->lastOut=0;
|
||||
}
|
||||
if (ch->left || ch->right) {
|
||||
ch->lastOut = val;
|
||||
} else {
|
||||
ch->lastOut = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*left = l;
|
||||
|
|
@ -104,6 +108,6 @@ psg_render(struct VERA_PSG* psg, int16_t *bufL, int16_t *bufR, unsigned num_samp
|
|||
while (num_samples--) {
|
||||
render(psg, bufL, bufR);
|
||||
bufL++;
|
||||
bufR++;
|
||||
bufR++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@ const char* DivPlatformSoundUnit::getEffectName(unsigned char effect) {
|
|||
return "1Dxx: Set cutoff sweep boundary";
|
||||
break;
|
||||
case 0x1e:
|
||||
return "17xx: Set phase reset period low byte";
|
||||
return "1Exx: Set phase reset period low byte";
|
||||
break;
|
||||
case 0x1f:
|
||||
return "18xx: Set phase reset period high byte";
|
||||
return "1Fxx: Set phase reset period high byte";
|
||||
break;
|
||||
case 0x20:
|
||||
return "20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)";
|
||||
|
|
|
|||
|
|
@ -659,6 +659,13 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
if (c.chan>2) break;
|
||||
chan[c.chan].state.fb=c.value&7;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
if (!extMode) {
|
||||
c.chan=2;
|
||||
return DivPlatformYM2203::dispatch(c);
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
|
@ -151,6 +155,11 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
@ -514,6 +523,7 @@ int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, uns
|
|||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
extSys=true;
|
||||
|
||||
reset();
|
||||
return 9;
|
||||
|
|
|
|||
|
|
@ -988,6 +988,13 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
if (!extMode) {
|
||||
c.chan=2;
|
||||
return DivPlatformYM2608::dispatch(c);
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
|
@ -151,6 +155,11 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
@ -528,6 +537,7 @@ int DivPlatformYM2608Ext::init(DivEngine* parent, int channels, int sugRate, uns
|
|||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
extSys=true;
|
||||
|
||||
reset();
|
||||
return 19;
|
||||
|
|
|
|||
|
|
@ -1032,6 +1032,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1014,6 +1014,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
if (extSys) {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
if (!extMode) {
|
||||
c.chan=2;
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
|
@ -151,6 +155,11 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
@ -427,7 +436,7 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==2) { // extended channel
|
||||
if (i==2 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
|
|
@ -528,6 +537,7 @@ int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, un
|
|||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
extSys=true;
|
||||
|
||||
reset();
|
||||
return 19;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
}
|
||||
int ch=c.chan-1;
|
||||
int ordch=orderedOps[ch];
|
||||
if (!extMode) {
|
||||
c.chan=2;
|
||||
return DivPlatformYM2610::dispatch(c);
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
|
@ -151,6 +155,11 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_EXTCH: {
|
||||
extMode=c.value;
|
||||
immWrite(0x27,extMode?0x40:0);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
|
|
@ -427,7 +436,7 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==1) { // extended channel
|
||||
if (i==1 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
|
|
@ -528,6 +537,7 @@ int DivPlatformYM2610Ext::init(DivEngine* parent, int channels, int sugRate, uns
|
|||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
extSys=true;
|
||||
|
||||
reset();
|
||||
return 17;
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@
|
|||
#include "engine.h"
|
||||
#include "../ta-log.h"
|
||||
#include <math.h>
|
||||
#ifdef HAVE_SNDFILE
|
||||
#include <sndfile.h>
|
||||
#endif
|
||||
|
||||
constexpr int MASTER_CLOCK_PREC=(sizeof(void*)==8)?8:0;
|
||||
|
||||
|
|
@ -473,6 +470,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].nowYouCanStop=false;
|
||||
chan[i].stopOnOff=false;
|
||||
chan[i].scheduledSlideReset=false;
|
||||
chan[i].wasShorthandPorta=false;
|
||||
chan[i].inPorta=false;
|
||||
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
|
||||
}
|
||||
|
|
@ -492,6 +490,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].nowYouCanStop=false;
|
||||
chan[i].stopOnOff=false;
|
||||
chan[i].scheduledSlideReset=false;
|
||||
chan[i].wasShorthandPorta=false;
|
||||
chan[i].inPorta=false;
|
||||
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
|
||||
}
|
||||
|
|
@ -511,6 +510,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].portaNote=chan[i].note;
|
||||
chan[i].portaSpeed=effectVal;
|
||||
chan[i].inPorta=true;
|
||||
chan[i].wasShorthandPorta=false;
|
||||
}
|
||||
chan[i].portaStop=true;
|
||||
if (chan[i].keyOn) chan[i].doNote=false;
|
||||
|
|
@ -590,6 +590,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
if ((effectVal&15)!=0) {
|
||||
chan[i].inPorta=true;
|
||||
chan[i].shorthandPorta=true;
|
||||
chan[i].wasShorthandPorta=true;
|
||||
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
|
||||
if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ...
|
||||
} else {
|
||||
|
|
@ -607,6 +608,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
if ((effectVal&15)!=0) {
|
||||
chan[i].inPorta=true;
|
||||
chan[i].shorthandPorta=true;
|
||||
chan[i].wasShorthandPorta=true;
|
||||
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,true,0));
|
||||
if (song.e1e2AlsoTakePriority) lastSlide=0x1337; // ...
|
||||
} else {
|
||||
|
|
@ -733,7 +735,14 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
|
||||
} else {
|
||||
if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) {
|
||||
chan[i].portaNote=chan[i].note;
|
||||
if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
|
||||
chan[i].portaSpeed=-1;
|
||||
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
||||
chan[i].wasShorthandPorta=false;
|
||||
chan[i].inPorta=false;
|
||||
} else {
|
||||
chan[i].portaNote=chan[i].note;
|
||||
}
|
||||
} else if (!chan[i].noteOnInhibit) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
|
||||
keyHit[i]=true;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
#include <math.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_SNDFILE
|
||||
#include <sndfile.h>
|
||||
#include "sfWrapper.h"
|
||||
#endif
|
||||
#include "filter.h"
|
||||
|
||||
|
|
@ -49,6 +49,7 @@ bool DivSample::save(const char* path) {
|
|||
#else
|
||||
SNDFILE* f;
|
||||
SF_INFO si;
|
||||
SFWrapper sfWrap;
|
||||
memset(&si,0,sizeof(SF_INFO));
|
||||
|
||||
if (length16<1) return false;
|
||||
|
|
@ -64,7 +65,7 @@ bool DivSample::save(const char* path) {
|
|||
break;
|
||||
}
|
||||
|
||||
f=sf_open(path,SFM_WRITE,&si);
|
||||
f=sfWrap.doOpen(path,SFM_WRITE,&si);
|
||||
|
||||
if (f==NULL) {
|
||||
logE("could not open wave file for saving! %s",sf_error_number(sf_error(f)));
|
||||
|
|
@ -104,7 +105,7 @@ bool DivSample::save(const char* path) {
|
|||
break;
|
||||
}
|
||||
|
||||
sf_close(f);
|
||||
sfWrap.doClose();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
|
|
|
|||
121
src/engine/sfWrapper.cpp
Normal file
121
src/engine/sfWrapper.cpp
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "sfWrapper.h"
|
||||
#include "../fileutils.h"
|
||||
#include "sndfile.h"
|
||||
|
||||
sf_count_t _vioGetSize(void* user) {
|
||||
return ((SFWrapper*)user)->ioGetSize();
|
||||
}
|
||||
|
||||
sf_count_t _vioSeek(sf_count_t offset, int whence, void* user) {
|
||||
return ((SFWrapper*)user)->ioSeek(offset,whence);
|
||||
}
|
||||
|
||||
sf_count_t _vioRead(void* ptr, sf_count_t count, void* user) {
|
||||
return ((SFWrapper*)user)->ioRead(ptr,count);
|
||||
}
|
||||
|
||||
sf_count_t _vioWrite(const void* ptr, sf_count_t count, void* user) {
|
||||
return ((SFWrapper*)user)->ioWrite(ptr,count);
|
||||
}
|
||||
|
||||
sf_count_t _vioTell(void* user) {
|
||||
return ((SFWrapper*)user)->ioTell();
|
||||
}
|
||||
|
||||
sf_count_t SFWrapper::ioGetSize() {
|
||||
sf_count_t ret=(sf_count_t)len;
|
||||
if (fileMode==SFM_WRITE || fileMode==SFM_RDWR) {
|
||||
ssize_t lastTell=ftell(f);
|
||||
fseek(f,0,SEEK_END);
|
||||
ret=(sf_count_t)ftell(f);
|
||||
fseek(f,lastTell,SEEK_SET);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
sf_count_t SFWrapper::ioSeek(sf_count_t offset, int whence) {
|
||||
return fseek(f,offset,whence);
|
||||
}
|
||||
|
||||
sf_count_t SFWrapper::ioRead(void* ptr, sf_count_t count) {
|
||||
return fread(ptr,1,count,f);
|
||||
}
|
||||
|
||||
sf_count_t SFWrapper::ioWrite(const void* ptr, sf_count_t count) {
|
||||
return fwrite(ptr,1,count,f);
|
||||
}
|
||||
|
||||
sf_count_t SFWrapper::ioTell() {
|
||||
return ftell(f);
|
||||
}
|
||||
|
||||
int SFWrapper::doClose() {
|
||||
int ret=sf_close(sf);
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SNDFILE* SFWrapper::doOpen(const char* path, int mode, SF_INFO* sfinfo) {
|
||||
vio.get_filelen=_vioGetSize;
|
||||
vio.read=_vioRead;
|
||||
vio.seek=_vioSeek;
|
||||
vio.tell=_vioTell;
|
||||
vio.write=_vioWrite;
|
||||
|
||||
const char* modeC="rb";
|
||||
if (mode==SFM_WRITE) {
|
||||
modeC="wb";
|
||||
}
|
||||
if (mode==SFM_RDWR) {
|
||||
modeC="rb+";
|
||||
}
|
||||
|
||||
f=ps_fopen(path,modeC);
|
||||
if (f==NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_END)==-1) {
|
||||
fclose(f);
|
||||
f=NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len=ftell(f);
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
len=0;
|
||||
fclose(f);
|
||||
f=NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fseek(f,0,SEEK_SET)==-1) {
|
||||
len=0;
|
||||
fclose(f);
|
||||
f=NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sf=sf_open_virtual(&vio,mode,sfinfo,this);
|
||||
if (sf!=NULL) fileMode=mode;
|
||||
return sf;
|
||||
}
|
||||
56
src/engine/sfWrapper.h
Normal file
56
src/engine/sfWrapper.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
// sfWrapper.h: libsndfile FILE* wrapper to work around Windows issue with
|
||||
// non-ASCII chars in file path
|
||||
// I wanted to do this in sndfile directly, but it's a
|
||||
// submodule...
|
||||
|
||||
#ifndef _SFWRAPPER_H
|
||||
#define _SFWRAPPER_H
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sndfile.h>
|
||||
#include "../ta-utils.h"
|
||||
|
||||
class SFWrapper {
|
||||
FILE* f;
|
||||
size_t len;
|
||||
SF_VIRTUAL_IO vio;
|
||||
SNDFILE* sf;
|
||||
int fileMode;
|
||||
|
||||
public:
|
||||
sf_count_t ioGetSize();
|
||||
sf_count_t ioSeek(sf_count_t offset, int whence);
|
||||
sf_count_t ioRead(void* ptr, sf_count_t count);
|
||||
sf_count_t ioWrite(const void* ptr, sf_count_t count);
|
||||
sf_count_t ioTell();
|
||||
|
||||
int doClose();
|
||||
SNDFILE* doOpen(const char* path, int mode, SF_INFO* sfinfo);
|
||||
SFWrapper():
|
||||
f(NULL),
|
||||
len(0),
|
||||
sf(NULL),
|
||||
fileMode(0) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -114,6 +114,8 @@ enum DivSystem {
|
|||
DIV_SYSTEM_YM2612_FRAC,
|
||||
DIV_SYSTEM_YM2612_FRAC_EXT,
|
||||
DIV_SYSTEM_RESERVED_8,
|
||||
DIV_SYSTEM_T6W28,
|
||||
DIV_SYSTEM_PCM_DAC,
|
||||
DIV_SYSTEM_DUMMY,
|
||||
DIV_SYSTEM_MAX // boundary for max system number
|
||||
};
|
||||
|
|
@ -495,6 +497,7 @@ struct DivSong {
|
|||
bool newVolumeScaling;
|
||||
bool volMacroLinger;
|
||||
bool brokenOutVol;
|
||||
bool e1e2StopOnSameNote;
|
||||
|
||||
std::vector<DivInstrument*> ins;
|
||||
std::vector<DivWavetable*> wave;
|
||||
|
|
@ -592,7 +595,8 @@ struct DivSong {
|
|||
noOPN2Vol(false),
|
||||
newVolumeScaling(true),
|
||||
volMacroLinger(true),
|
||||
brokenOutVol(false) {
|
||||
brokenOutVol(false),
|
||||
e1e2StopOnSameNote(false) {
|
||||
for (int i=0; i<32; i++) {
|
||||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=64;
|
||||
|
|
|
|||
|
|
@ -2160,6 +2160,35 @@ void DivEngine::registerSystems() {
|
|||
fmPostEffectHandler
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_T6W28]=new DivSysDef(
|
||||
"T6W28", NULL, 0xbf, 0, 4, false, true, 0, false,
|
||||
"an SN76489 derivative used in Neo Geo Pocket, has independent stereo volume and noise channel frequency.",
|
||||
{"Square 1", "Square 2", "Square 3", "Noise"},
|
||||
{"S1", "S2", "S3", "NO"},
|
||||
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE},
|
||||
{DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD},
|
||||
{},
|
||||
[this](int ch, unsigned char effect, unsigned char effectVal) -> bool {
|
||||
switch (effect) {
|
||||
case 0x20: // SN noise mode
|
||||
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_PCM_DAC]=new DivSysDef(
|
||||
"Generic PCM DAC", NULL, 0xc0, 0, 1, false, true, 0, false,
|
||||
"as generic sample playback as it gets.",
|
||||
{"Sample"},
|
||||
{"PCM"},
|
||||
{DIV_CH_PCM},
|
||||
{DIV_INS_AMIGA}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
|
||||
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false,
|
||||
"this is a system designed for testing purposes.",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,10 @@
|
|||
#include "../fileutils.h"
|
||||
|
||||
void DivWavetable::putWaveData(SafeWriter* w) {
|
||||
size_t blockStartSeek, blockEndSeek;
|
||||
|
||||
w->write("WAVE",4);
|
||||
blockStartSeek=w->tell();
|
||||
w->writeI(0);
|
||||
|
||||
w->writeC(0); // name
|
||||
|
|
@ -34,6 +37,11 @@ void DivWavetable::putWaveData(SafeWriter* w) {
|
|||
for (int j=0; j<len; j++) {
|
||||
w->writeI(data[j]);
|
||||
}
|
||||
|
||||
blockEndSeek=w->tell();
|
||||
w->seek(blockStartSeek,SEEK_SET);
|
||||
w->writeI(blockEndSeek-blockStartSeek-4);
|
||||
w->seek(0,SEEK_END);
|
||||
}
|
||||
|
||||
DivDataErrors DivWavetable::readWaveData(SafeReader& reader, short version) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue