Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
* 'master' of https://github.com/tildearrow/furnace: (46 commits) PCE: fix two issues SegaPCM: fix samples bigger than 64KB SCC: implement VGM soft reset GUI: add option to clear orders GUI: implement "clear all subsongs" GUI: fix crash when deleting current subsong CI: only 1 core for MinGW Fix AY8910 envelope hangs OPL: fix fixed frequency drums pick nits: the sequel pick nits AY: fix possible hang hide .ftm format Add x2 icon variations as well Install more size variations on Linux OPLL: fix fixed drums freq GUI: make backupTimer atomic Have OPN* platforms set the correct YM2149 chip type. update to-do list ZX beeper: clarify effects (will be done later) ... # Conflicts: # src/engine/platform/segapcm.cpp
This commit is contained in:
commit
f7ba60bfa9
|
@ -97,6 +97,10 @@ jobs:
|
|||
if [ '${{ runner.os }}' == 'macOS' ]; then
|
||||
amount=3
|
||||
fi
|
||||
# the Actions runner does not seem to be happy with two jobs at once on MinGW
|
||||
if [ '${{ matrix.config.compiler }}' == 'mingw' ]; then
|
||||
amount=1
|
||||
fi
|
||||
|
||||
echo "Amount of cores we can build with: ${amount}"
|
||||
|
||||
|
|
|
@ -565,6 +565,11 @@ if (NOT ANDROID)
|
|||
install(DIRECTORY papers DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/furnace)
|
||||
install(DIRECTORY demos DESTINATION ${CMAKE_INSTALL_DATADIR}/furnace)
|
||||
foreach(num 16 32 64 128 256 512)
|
||||
set(res ${num}x${num})
|
||||
install(FILES res/icon.iconset/icon_${res}.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}/apps)
|
||||
install(FILES res/icon.iconset/icon_${res}@2x.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/${res}@2/apps)
|
||||
endforeach()
|
||||
install(FILES res/logo.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/1024x1024/apps)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -814,6 +814,16 @@ bool DivEngine::removeSubSong(int index) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void DivEngine::clearSubSongs() {
|
||||
BUSY_BEGIN;
|
||||
saveLock.lock();
|
||||
song.clearSongData();
|
||||
changeSong(0);
|
||||
curOrder=0;
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
}
|
||||
|
||||
void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
||||
int chanCount=chans;
|
||||
quitDispatch();
|
||||
|
@ -1945,13 +1955,13 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
}
|
||||
extS+=i;
|
||||
}
|
||||
if (extS==String(".dmc")) { // read as .dmc
|
||||
if (extS==".dmc") { // read as .dmc
|
||||
size_t len=0;
|
||||
DivSample* sample=new DivSample;
|
||||
int sampleCount=(int)song.sample.size();
|
||||
sample->name=stripPath;
|
||||
|
||||
FILE* f=fopen(path,"rb");
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
||||
|
|
|
@ -399,8 +399,11 @@ class DivEngine {
|
|||
void loadOPNI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadY12(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadBNK(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadGYB(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadFF(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadWOPL(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
void loadWOPN(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||
|
||||
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
|
||||
|
||||
|
@ -820,6 +823,9 @@ class DivEngine {
|
|||
// remove subsong
|
||||
bool removeSubSong(int index);
|
||||
|
||||
// clear all subsong data
|
||||
void clearSubSongs();
|
||||
|
||||
// change system
|
||||
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
||||
|
||||
|
|
|
@ -2682,16 +2682,19 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
if (song.ins.size()>256) {
|
||||
logE("maximum number of instruments is 256!");
|
||||
lastError="maximum number of instruments is 256";
|
||||
saveLock.unlock();
|
||||
return NULL;
|
||||
}
|
||||
if (song.wave.size()>256) {
|
||||
logE("maximum number of wavetables is 256!");
|
||||
lastError="maximum number of wavetables is 256";
|
||||
saveLock.unlock();
|
||||
return NULL;
|
||||
}
|
||||
if (song.sample.size()>256) {
|
||||
logE("maximum number of samples is 256!");
|
||||
lastError="maximum number of samples is 256";
|
||||
saveLock.unlock();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -660,6 +660,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
|
|||
break;
|
||||
}
|
||||
ay->device_start();
|
||||
ay->device_reset();
|
||||
|
||||
stereo=(flags>>6)&1;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,15 @@
|
|||
rWrite((a),__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define pageReadMask(p,pm,a,...) \
|
||||
if (!skipRegisterWrites) { \
|
||||
if ((curPage&(pm))!=((p)&(pm))) { \
|
||||
curPage=(curPage&~(pm))|((p)&(pm)); \
|
||||
rWrite(0xf,curPage,(pm)); \
|
||||
} \
|
||||
rRead((a),__VA_ARGS__); \
|
||||
}
|
||||
|
||||
|
||||
const char* regCheatSheetES5506[]={
|
||||
"CR", "00|00",
|
||||
|
@ -167,6 +176,8 @@ const char* DivPlatformES5506::getEffectName(unsigned char effect) {
|
|||
return "3xxx: Set filter coefficient K1";
|
||||
} else if ((effect&0xf0)==0x40) {
|
||||
return "4xxx: Set filter coefficient K2";
|
||||
} else if ((effect&0xf0)==0x50) {
|
||||
return "5xxx: Set transwave slice point";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -268,6 +279,101 @@ void DivPlatformES5506::e_pin(bool state)
|
|||
pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0000:0x0040)|0x08,0x78);
|
||||
chan[ch].isReverseLoop=false;
|
||||
}
|
||||
if (chan[ch].transwaveIRQ) {
|
||||
if ((chan[ch].cr&0x37)==0x34) { // IRQE = 1, BLE = 1, LPE = 0, LEI = 1
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||
if (!ins->amiga.useNoteMap && ins->amiga.transWave.enable) {
|
||||
const int next=chan[ch].pcm.next;
|
||||
bool sampleVaild=false;
|
||||
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
|
||||
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
|
||||
int sample=transWaveInd.ind;
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
sampleVaild=true;
|
||||
chan[ch].pcm.index=sample;
|
||||
chan[ch].transWave.ind=next;
|
||||
DivSample* s=parent->getSample(sample);
|
||||
// get frequency offset
|
||||
double off=1.0;
|
||||
double center=s->centerRate;
|
||||
if (center<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)center/8363.0;
|
||||
}
|
||||
// get loop mode, transwave loop
|
||||
double loopStart=(double)s->loopStart;
|
||||
double loopEnd=(double)s->loopEnd;
|
||||
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
|
||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||
loopMode=transWaveInd.loopMode;
|
||||
} else if ((chan[ch].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
|
||||
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||
}
|
||||
// get loop position
|
||||
loopStart=(double)transWaveInd.loopStart;
|
||||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[ch].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[ch].transWave.slice=transWaveInd.slice;
|
||||
chan[ch].transWave.slicePos(chan[ch].transWave.slice);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
// get reversed
|
||||
bool reversed=ins->amiga.reversed;
|
||||
if (transWaveInd.reversed!=2) {
|
||||
reversed=transWaveInd.reversed;
|
||||
}
|
||||
const unsigned int start=s->offES5506<<10;
|
||||
const unsigned int length=s->samples-1;
|
||||
const unsigned int end=start+(length<<11);
|
||||
chan[ch].pcm.loopMode=loopMode;
|
||||
chan[ch].pcm.nextFreqOffs=PITCH_OFFSET*off;
|
||||
chan[ch].pcm.reversed=reversed;
|
||||
chan[ch].pcm.bank=(s->offES5506>>22)&3;
|
||||
chan[ch].pcm.start=start;
|
||||
chan[ch].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
|
||||
chan[ch].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
|
||||
chan[ch].pcm.end=end;
|
||||
chan[ch].pcm.length=length;
|
||||
pageWrite(0x20|ch,0x01,chan[ch].pcm.loopStart);
|
||||
pageWrite(0x20|ch,0x02,chan[ch].pcm.loopEnd);
|
||||
pageWrite(0x20|ch,0x03,(chan[ch].pcm.reversed)?chan[ch].pcm.loopEnd:chan[ch].pcm.loopStart);
|
||||
unsigned int loopFlag=(chan[ch].pcm.bank<<14)|(chan[ch].pcm.reversed?0x0040:0x0000);
|
||||
chan[ch].isReverseLoop=false;
|
||||
switch (chan[ch].pcm.loopMode) {
|
||||
case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop)
|
||||
default:
|
||||
break;
|
||||
case DIV_SAMPLE_LOOPMODE_FORWARD: // Foward loop
|
||||
loopFlag|=0x0008;
|
||||
break;
|
||||
case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable
|
||||
loopFlag|=0x0038;
|
||||
chan[ch].isReverseLoop=true;
|
||||
break;
|
||||
case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support
|
||||
loopFlag|=0x0018;
|
||||
break;
|
||||
}
|
||||
// Set loop mode & Bank
|
||||
pageWriteMask(0x00|ch,0x5f,0x00,loopFlag,0xfcfc);
|
||||
}
|
||||
}
|
||||
if (sampleVaild) {
|
||||
chan[ch].noteChanged.offs=1;
|
||||
}
|
||||
chan[ch].pcmChanged.changed=0;
|
||||
}
|
||||
}
|
||||
chan[ch].transwaveIRQ=false;
|
||||
}
|
||||
if (chan[ch].isTransWave) {
|
||||
pageReadMask(0x00|ch,0x5f,0x00,&chan[ch].cr);
|
||||
chan[ch].transwaveIRQ=true;
|
||||
chan[ch].isTransWave=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -460,14 +566,56 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
// control macros
|
||||
if (!chan[i].keyOn) {
|
||||
if (chan[i].active && chan[i].std.alg.had) {
|
||||
if (chan[i].pcm.pause!=(bool)(chan[i].std.alg.val&1)) {
|
||||
chan[i].pcm.pause=chan[i].std.alg.val&1;
|
||||
if (chan[i].active && chan[i].std.alg.had) {
|
||||
if (chan[i].pcm.pause!=(bool)(chan[i].std.alg.val&1)) {
|
||||
chan[i].pcm.pause=chan[i].std.alg.val&1;
|
||||
if (!chan[i].keyOn) {
|
||||
pageWriteMask(0x00|i,0x5f,0x00,chan[i].pcm.pause?0x0002:0x0000,0x0002);
|
||||
}
|
||||
}
|
||||
}
|
||||
// transwave macros
|
||||
if (chan[i].transWave.enable) {
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<ins->amiga.transWaveMap.size()) {
|
||||
if (chan[i].pcm.next!=chan[i].std.wave.val) {
|
||||
chan[i].pcm.next=chan[i].std.wave.val;
|
||||
chan[i].pcmChanged.transwaveInd=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].std.fb.had) {
|
||||
if (chan[i].transWave.sliceEnable!=(bool)(chan[i].std.fb.val&1)) {
|
||||
chan[i].transWave.sliceEnable=chan[i].std.fb.val&1;
|
||||
chan[i].pcmChanged.slice=1;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.fms.had) {
|
||||
if (chan[i].transWave.slice!=(unsigned short)(chan[i].std.fms.val&0xfff)) {
|
||||
chan[i].transWave.slice=chan[i].std.fms.val&0xfff;
|
||||
chan[i].pcmChanged.slice=1;
|
||||
}
|
||||
}
|
||||
} else if (chan[i].pcm.isNoteMap) {
|
||||
// note map macros
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<120) {
|
||||
if (chan[i].pcm.next!=chan[i].std.wave.val) {
|
||||
chan[i].pcm.next=chan[i].std.wave.val;
|
||||
chan[i].pcmChanged.index=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!chan[i].transWave.enable && !chan[i].pcm.isNoteMap) {
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].std.wave.val>=0 && chan[i].std.wave.val<parent->song.sampleLen) {
|
||||
if (chan[i].pcm.next!=chan[i].std.wave.val) {
|
||||
chan[i].pcm.next=chan[i].std.wave.val;
|
||||
chan[i].pcmChanged.index=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// update registers
|
||||
if (chan[i].volChanged.changed) {
|
||||
if (!isMuted[i]) { // calculate volume (16 bit)
|
||||
|
@ -489,34 +637,193 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
}
|
||||
chan[i].volChanged.changed=0;
|
||||
}
|
||||
if (chan[i].pcmChanged) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins);
|
||||
if (!ins->amiga.useNoteMap) {
|
||||
double off=1.0;
|
||||
if (chan[i].pcm.next>=0 && chan[i].pcm.next<parent->song.sampleLen) {
|
||||
chan[i].pcm.index=chan[i].pcm.next;
|
||||
DivSample* s=parent->getSample(chan[i].pcm.next);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/8363.0;
|
||||
if (chan[i].pcmChanged.changed) {
|
||||
if (!chan[i].isTransWave) {
|
||||
if (chan[i].pcmChanged.transwaveInd && (!ins->amiga.useNoteMap && ins->amiga.transWave.enable)) {
|
||||
const int next=chan[i].pcm.next;
|
||||
bool sampleVaild=false;
|
||||
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
|
||||
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
|
||||
int sample=transWaveInd.ind;
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
if (chan[i].pcm.index!=sample) {
|
||||
pageWriteMask(0x00|i,0x5f,0x00,0x0034,0x00ff); // Set IRQ
|
||||
chan[i].isTranswave=true;
|
||||
} else {
|
||||
chan[i].transWave.ind=next;
|
||||
DivSample* s=parent->getSample(sample);
|
||||
// get loop mode, transwave loop
|
||||
double loopStart=(double)s->loopStart;
|
||||
double loopEnd=(double)s->loopEnd;
|
||||
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
|
||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||
loopMode=transWaveInd.loopMode;
|
||||
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
|
||||
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||
}
|
||||
// get loop position
|
||||
loopStart=(double)transWaveInd.loopStart;
|
||||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[i].transWave.slice=transWaveInd.slice;
|
||||
chan[i].transWave.slicePos(transWaveInd.slice);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
// get reversed
|
||||
bool reversed=ins->amiga.reversed;
|
||||
if (transWaveInd.reversed!=2) {
|
||||
reversed=transWaveInd.reversed;
|
||||
}
|
||||
chan[i].pcm.loopMode=loopMode;
|
||||
chan[i].pcm.reversed=reversed;
|
||||
if (sampleVaild) {
|
||||
chan[i].pcmChanged.slice=1;
|
||||
chan[i].pcmChanged.loopBank=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const unsigned int start=s->offES5506<<10;
|
||||
const unsigned int length=s->samples-1;
|
||||
const unsigned int end=start+(length<<11);
|
||||
chan[i].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
|
||||
chan[i].pcm.freqOffs=PITCH_OFFSET*off;
|
||||
chan[i].pcm.reversed=ins->amiga.reversed;
|
||||
chan[i].pcm.bank=(s->offES5506>>22)&3;
|
||||
chan[i].pcm.start=start;
|
||||
chan[i].pcm.end=end;
|
||||
chan[i].pcm.length=length;
|
||||
chan[i].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800;
|
||||
chan[i].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80;
|
||||
chan[i].keyOn=true;
|
||||
chan[i].pcmChanged.transwaveInd=0;
|
||||
}
|
||||
if ((!chan[i].pcmChanged.transwaveInd) && (!chan[i].isTransWave)) {
|
||||
if (chan[i].pcmChanged.index) {
|
||||
const int next=chan[i].pcm.next;
|
||||
bool sampleVaild=false;
|
||||
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (next>=0 && next<120)) ||
|
||||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (next>=0 && next<ins->amiga.transWaveMap.size())) ||
|
||||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (next>=0 && next<parent->song.sampleLen))) {
|
||||
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[next];
|
||||
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
|
||||
int sample=next;
|
||||
if (ins->amiga.transWave.enable) {
|
||||
sample=transWaveInd.ind;
|
||||
} else if (ins->amiga.useNoteMap) {
|
||||
sample=noteMapind.ind;
|
||||
}
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
sampleVaild=true;
|
||||
chan[i].pcm.index=sample;
|
||||
chan[i].pcm.isNoteMap=ins->amiga.useNoteMap && !ins->amiga.transWave.enable;
|
||||
chan[i].transWave.enable=!ins->amiga.useNoteMap && ins->amiga.transWave.enable;
|
||||
chan[i].transWave.ind=next;
|
||||
DivSample* s=parent->getSample(sample);
|
||||
// get frequency offset
|
||||
double off=1.0;
|
||||
double center=s->centerRate;
|
||||
if (center<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)center/8363.0;
|
||||
}
|
||||
if (ins->amiga.useNoteMap) {
|
||||
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)next-48.0)/12.0));
|
||||
chan[i].pcm.note=next;
|
||||
}
|
||||
// get loop mode, transwave loop
|
||||
double loopStart=(double)s->loopStart;
|
||||
double loopEnd=(double)s->loopEnd;
|
||||
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
|
||||
if (ins->amiga.transWave.enable) {
|
||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||
loopMode=transWaveInd.loopMode;
|
||||
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
|
||||
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||
}
|
||||
// get loop position
|
||||
loopStart=(double)transWaveInd.loopStart;
|
||||
loopEnd=(double)transWaveInd.loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[i].transWave.slice=transWaveInd.slice;
|
||||
chan[i].transWave.slicePos(transWaveInd.slice);
|
||||
loopStart=transWaveInd.sliceStart;
|
||||
loopEnd=transWaveInd.sliceEnd;
|
||||
}
|
||||
}
|
||||
// get reversed
|
||||
bool reversed=ins->amiga.reversed;
|
||||
if (ins->amiga.transWave.enable&&transWaveInd.reversed!=2) {
|
||||
reversed=transWaveInd.reversed;
|
||||
} else if (ins->amiga.useNoteMap&¬eMapind.reversed!=2) {
|
||||
reversed=noteMapind.reversed;
|
||||
}
|
||||
const unsigned int start=s->offES5506<<10;
|
||||
const unsigned int length=s->samples-1;
|
||||
const unsigned int end=start+(length<<11);
|
||||
chan[i].pcm.loopMode=loopMode;
|
||||
chan[i].pcm.nextFreqOffs=PITCH_OFFSET*off;
|
||||
chan[i].pcm.reversed=reversed;
|
||||
chan[i].pcm.bank=(s->offES5506>>22)&3;
|
||||
chan[i].pcm.start=start;
|
||||
chan[i].pcm.end=end;
|
||||
chan[i].pcm.length=length;
|
||||
chan[i].keyOn=true;
|
||||
}
|
||||
}
|
||||
if (sampleVaild) {
|
||||
chan[i].pcmChanged.slice=1;
|
||||
chan[i].pcmChanged.loopBank=1;
|
||||
chan[i].noteChanged.offs=1;
|
||||
}
|
||||
chan[i].pcmChanged.index=0;
|
||||
}
|
||||
if (chan[i].pcmChanged.slice) {
|
||||
if (!chan[i].keyOn) {
|
||||
if (chan[i].pcm.index>=0 && chan[i].pcm.index<parent->song.sampleLen) {
|
||||
// get loop mode, transwave loop
|
||||
DivSample* s=parent->getSample(chan[i].pcm.index);
|
||||
double loopStart=(double)s->loopStart;
|
||||
double loopEnd=(double)s->loopEnd;
|
||||
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
|
||||
chan[i].transWave.updateSize(s->samples,loopStart,loopEnd);
|
||||
chan[i].transWave.slicePos(chan[i].transWave.slice);
|
||||
loopStart=chan[i].transWave.sliceStart;
|
||||
loopEnd=chan[i].transWave.sliceEnd;
|
||||
}
|
||||
const unsigned int start=s->offES5506<<10;
|
||||
chan[i].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
|
||||
chan[i].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
|
||||
chan[i].pcmChanged.position=1;
|
||||
}
|
||||
}
|
||||
chan[i].pcmChanged.slice=0;
|
||||
}
|
||||
if (chan[i].pcmChanged.position) {
|
||||
if (!chan[i].keyOn) {
|
||||
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart);
|
||||
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd);
|
||||
}
|
||||
chan[i].pcmChanged.position=0;
|
||||
}
|
||||
if (chan[i].pcmChanged.loopBank) {
|
||||
if (!chan[i].keyOn) {
|
||||
unsigned int loopFlag=(chan[i].pcm.bank<<14)|(chan[i].pcm.reversed?0x0040:0x0000);
|
||||
chan[i].isReverseLoop=false;
|
||||
switch (chan[i].pcm.loopMode) {
|
||||
case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop)
|
||||
default:
|
||||
break;
|
||||
case DIV_SAMPLE_LOOPMODE_FORWARD: // Foward loop
|
||||
loopFlag|=0x0008;
|
||||
break;
|
||||
case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable
|
||||
loopFlag|=0x0038;
|
||||
chan[i].isReverseLoop=true;
|
||||
break;
|
||||
case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support
|
||||
loopFlag|=0x0018;
|
||||
break;
|
||||
}
|
||||
// Set loop mode & Bank
|
||||
pageWriteMask(0x00|i,0x5f,0x00,loopFlag,0xfcfd);
|
||||
}
|
||||
chan[i].pcmChanged.loopBank=0;
|
||||
}
|
||||
chan[i].pcmChanged.dummy=0;
|
||||
}
|
||||
}
|
||||
chan[i].pcmChanged=false;
|
||||
}
|
||||
if (chan[i].filterChanged.changed) {
|
||||
if (!chan[i].keyOn) {
|
||||
|
@ -564,12 +871,13 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
if (chan[i].noteChanged.offs) {
|
||||
if (chan[i].pcm.freqOffs!=chan[i].pcm.nextFreqOffs) {
|
||||
chan[i].pcm.freqOffs=chan[i].pcm.nextFreqOffs;
|
||||
const int nextFreq=NOTE_ES5506(i,chan[i].nextNote);
|
||||
const int nextFreq=NOTE_ES5506(i,chan[i].prevNote);
|
||||
if (chan[i].nextFreq!=nextFreq) {
|
||||
chan[i].nextFreq=nextFreq;
|
||||
chan[i].noteChanged.freq=1;
|
||||
}
|
||||
}
|
||||
chan[i].noteChanged.offs=0;
|
||||
}
|
||||
if (chan[i].noteChanged.note) { // note value changed or frequency offset is changed
|
||||
if (chan[i].prevNote!=chan[i].nextNote) {
|
||||
|
@ -580,17 +888,19 @@ void DivPlatformES5506::tick(bool sysTick) {
|
|||
chan[i].noteChanged.freq=1;
|
||||
}
|
||||
}
|
||||
chan[i].noteChanged.note=0;
|
||||
}
|
||||
if (chan[i].noteChanged.freq) {
|
||||
if (chan[i].baseFreq!=chan[i].nextFreq) {
|
||||
chan[i].baseFreq=chan[i].nextFreq;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
chan[i].noteChanged.freq=0;
|
||||
}
|
||||
chan[i].noteChanged.changed=0;
|
||||
chan[i].noteChanged.dummy=0;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=CLAMP_VAL(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,chan[c].pcm.freqOffs),0,0x1ffff);
|
||||
chan[i].freq=CLAMP_VAL(parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,chan[i].pcm.freqOffs),0,0x1ffff);
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].pcm.index>=0 && chan[i].pcm.index<parent->song.sampleLen) {
|
||||
chan[i].k1Prev=0xffff;
|
||||
|
@ -693,7 +1003,8 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
sampleVaild=true;
|
||||
chan[c.chan].pcm.index=sample;
|
||||
chan[c.chan].pcm.pause=(chan[c.chan].std.alg.will)?(chan[c.chan].std.alg.val&1):false;
|
||||
chan[c.chan].transWave.enable=ins->amiga.transWave.enable;
|
||||
chan[c.chan].pcm.isNoteMap=ins->amiga.useNoteMap && !ins->amiga.transWave.enable;
|
||||
chan[c.chan].transWave.enable=!ins->amiga.useNoteMap && ins->amiga.transWave.enable;
|
||||
chan[c.chan].transWave.sliceEnable=ins->amiga.transWave.sliceEnable;
|
||||
chan[c.chan].transWave.ind=ins->amiga.transWave.ind;
|
||||
DivSample* s=parent->getSample(sample);
|
||||
|
@ -716,7 +1027,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
if (ins->amiga.transWave.enable) {
|
||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||
loopMode=transWaveInd.loopMode;
|
||||
} else if (!s->isLoopable()) { // default
|
||||
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
|
||||
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||
}
|
||||
// get loop position
|
||||
|
@ -936,10 +1247,13 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
|||
case DIV_CMD_SAMPLE_POS: {
|
||||
if (chan[c.chan].useWave) break;
|
||||
if (chan[c.chan].active) {
|
||||
const unsigned int pos=chan[c.chan].pcm.reversed?(chan[c.chan].pcm.length-c.value):c.value;
|
||||
if ((chan[c.chan].pcm.reversed && pos>0) || ((!chan[c.chan].pcm.reversed) && pos<chan[c.chan].pcm.length)) {
|
||||
pageWrite(0x20|c.chan,0x03,chan[c.chan].pcm.start+(pos<<11));
|
||||
const unsigned int start=chan[c.chan].transWave.enable?chan[c.chan].pcm.loopStart:chan[c.chan].pcm.start;
|
||||
const unsigned int end=chan[c.chan].transWave.enable?chan[c.chan].pcm.loopEnd:chan[c.chan].pcm.length;
|
||||
const unsigned int pos=chan[c.chan].pcm.reversed?(end-c.value):c.value;
|
||||
if ((chan[c.chan].pcm.reversed && pos>0) || ((!chan[c.chan].pcm.reversed) && pos<end)) {
|
||||
pageWrite(0x20|c.chan,0x03,start+(pos<<11));
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -995,6 +1309,7 @@ void DivPlatformES5506::reset() {
|
|||
isMasked=false;
|
||||
isReaded=false;
|
||||
irqTrigger=false;
|
||||
transwaveCh=0;
|
||||
prevChanCycle=0;
|
||||
chanMax=initChanMax;
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||
struct Channel {
|
||||
struct PCM {
|
||||
bool isNoteMap;
|
||||
int index, next;
|
||||
int note;
|
||||
double freqOffs;
|
||||
|
@ -45,6 +46,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
unsigned int loopEnd;
|
||||
DivSampleLoopMode loopMode;
|
||||
PCM():
|
||||
isNoteMap(false),
|
||||
index(-1),
|
||||
next(-1),
|
||||
note(0),
|
||||
|
@ -62,7 +64,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
} pcm;
|
||||
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, prevNote, ins, wave;
|
||||
unsigned int volMacroMax, panMacroMax;
|
||||
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
|
||||
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop, isTranswave, transwaveIRQ;
|
||||
unsigned int cr;
|
||||
|
||||
struct NoteChanged { // Note changed flags
|
||||
union { // pack flag bits in single byte
|
||||
|
@ -125,6 +128,20 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
changed(0) {}
|
||||
} envChanged;
|
||||
|
||||
struct PcmChanged {
|
||||
union {
|
||||
struct {
|
||||
unsigned char index: 1; // sample index
|
||||
unsigned char slice: 1; // transwave slice
|
||||
unsigned char position: 1; // sample position in memory
|
||||
unsigned char loopBank: 1; // Loop mode and Bank
|
||||
unsigned char transwaveInd: 1; // transwave index
|
||||
unsigned char dummy: 4; // dummy for bit padding
|
||||
};
|
||||
unsigned char changed;
|
||||
};
|
||||
} pcmChanged;
|
||||
|
||||
signed int k1Offs, k2Offs;
|
||||
signed int k1Slide, k2Slide;
|
||||
signed int k1Prev, k2Prev;
|
||||
|
@ -170,6 +187,9 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
inPorta(false),
|
||||
useWave(false),
|
||||
isReverseLoop(false),
|
||||
isTranswave(false),
|
||||
transwaveIRQ(false),
|
||||
cr(0),
|
||||
k1Offs(0),
|
||||
k2Offs(0),
|
||||
k1Slide(0),
|
||||
|
@ -225,6 +245,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
|||
unsigned int irqv;
|
||||
bool isMasked, isReaded;
|
||||
bool irqTrigger;
|
||||
unsigned int curCR;
|
||||
unsigned char transwaveCh;
|
||||
unsigned char prevChanCycle;
|
||||
|
||||
unsigned char initChanMax, chanMax;
|
||||
|
|
|
@ -357,7 +357,9 @@ void DivPlatformN163::tick(bool sysTick) {
|
|||
chan[i].waveUpdated=false;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
// TODO: what is this mess?
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
chan[i].freq=(((chan[i].freq*chan[i].waveLen)*(chanMax+1))/16);
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
|
@ -595,6 +595,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
for (int i=0; i<totalChans; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq;
|
||||
if (chan[i].freq>131071) chan[i].freq=131071;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
chan[i].freqH=freqt>>8;
|
||||
|
@ -844,16 +845,18 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
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].baseFreq=(chan[c.chan].state.kickFreq&1023)<<(chan[c.chan].state.kickFreq>>10);
|
||||
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].baseFreq=(chan[c.chan].state.snareHatFreq&1023)<<(chan[c.chan].state.snareHatFreq>>10);
|
||||
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].baseFreq=(chan[c.chan].state.tomTopFreq&1023)<<(chan[c.chan].state.tomTopFreq>>10);
|
||||
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;
|
||||
}
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
|
@ -40,7 +40,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
DivInstrumentFM state;
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins, sample;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins, sample, fixedFreq;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnacePCM, inPorta, fourOp, hardReset;
|
||||
int vol, outVol;
|
||||
unsigned char pan;
|
||||
|
@ -58,6 +58,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
note(0),
|
||||
ins(-1),
|
||||
sample(-1),
|
||||
fixedFreq(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
|
|
|
@ -301,6 +301,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
for (int i=0; i<11; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].fixedFreq>0) chan[i].freq=chan[i].fixedFreq;
|
||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
chan[i].freqL=freqt&0xff;
|
||||
|
@ -420,15 +421,16 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].state.opllPreset==16 && chan[c.chan].state.fixedDrums) {
|
||||
switch (c.chan) {
|
||||
case 6:
|
||||
chan[c.chan].baseFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9);
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.kickFreq&511)<<(chan[c.chan].state.kickFreq>>9);
|
||||
break;
|
||||
case 7: case 10:
|
||||
chan[c.chan].baseFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9);
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.snareHatFreq&511)<<(chan[c.chan].state.snareHatFreq>>9);
|
||||
break;
|
||||
case 8: case 9:
|
||||
chan[c.chan].baseFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
chan[c.chan].fixedFreq=(chan[c.chan].state.tomTopFreq&511)<<(chan[c.chan].state.tomTopFreq>>9);
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].fixedFreq=0;
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
DivInstrumentFM state;
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins, fixedFreq;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta;
|
||||
int vol, outVol;
|
||||
unsigned char pan;
|
||||
|
@ -50,6 +50,7 @@ class DivPlatformOPLL: public DivDispatch {
|
|||
pitch2(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
fixedFreq(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
|
|
|
@ -155,7 +155,7 @@ void DivPlatformPCE::tick(bool sysTick) {
|
|||
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;
|
||||
if (chan[i].furnaceDac) {
|
||||
if (chan[i].furnaceDac && chan[i].pcm) {
|
||||
// ignore for now
|
||||
} else {
|
||||
chWrite(i,0x04,0x80|chan[i].outVol);
|
||||
|
@ -228,7 +228,7 @@ void DivPlatformPCE::tick(bool sysTick) {
|
|||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].furnaceDac) {
|
||||
if (chan[i].furnaceDac && chan[i].pcm) {
|
||||
double off=1.0;
|
||||
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].dacSample);
|
||||
|
@ -268,8 +268,9 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
chan[c.chan].pcm=false;
|
||||
}
|
||||
if (chan[c.chan].pcm) {
|
||||
if (skipRegisterWrites) break;
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].furnaceDac=true;
|
||||
if (skipRegisterWrites) break;
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
|
@ -291,8 +292,9 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
chan[c.chan].active=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
//chan[c.chan].keyOn=true;
|
||||
chan[c.chan].furnaceDac=true;
|
||||
} else {
|
||||
chan[c.chan].furnaceDac=false;
|
||||
if (skipRegisterWrites) break;
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
|
@ -311,7 +313,6 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
chWrite(c.chan,0x04,0xdf);
|
||||
addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dacRate);
|
||||
}
|
||||
chan[c.chan].furnaceDac=false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -181,11 +181,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].macroInit(ins);
|
||||
if (dumpWrites) { // Sega PCM writes
|
||||
DivSample* s=parent->getSample(chan[c.chan].pcm.sample);
|
||||
int actualLength=(int)(s->isLoopable()?s->loopEnd:s->length8);
|
||||
if (actualLength>0xfeff) actualLength=0xfeff;
|
||||
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
|
||||
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
||||
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+s->length8-1)>>8));
|
||||
if (!s->isLoopable()) {
|
||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8));
|
||||
if ((s->loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (s->loopStart<0 || s->loopStart>=actualLength || s->loopEnd<=s->loopStart || s->loopEnd>=actualLength)) {
|
||||
addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
|
||||
} else {
|
||||
int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP;
|
||||
|
@ -212,11 +214,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].furnacePCM=false;
|
||||
if (dumpWrites) { // Sega PCM writes
|
||||
DivSample* s=parent->getSample(chan[c.chan].pcm.sample);
|
||||
int actualLength=(int)(s->isLoopable()?s->loopEnd:s->length8);
|
||||
if (actualLength>65536) actualLength=65536;
|
||||
addWrite(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
|
||||
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
||||
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+s->length8-1)>>8));
|
||||
if (!s->isLoopable()) {
|
||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8));
|
||||
if ((s->loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (s->loopStart<0 || s->loopStart>=actualLength || s->loopEnd<=s->loopStart || s->loopEnd>=actualLength)) {
|
||||
addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
|
||||
} else {
|
||||
int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP;
|
||||
|
|
|
@ -1061,7 +1061,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
|
|||
for (int chan = 0; chan < NUM_CHANNELS; chan++)
|
||||
{
|
||||
tone = &m_tone[chan];
|
||||
const int period = tone->period * (m_step_mul << 1);
|
||||
const int period = std::max<int>(1, tone->period) * (m_step_mul << 1);
|
||||
tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2);
|
||||
while (tone->count >= period)
|
||||
{
|
||||
|
@ -1115,7 +1115,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
|
|||
envelope = &m_envelope[chan];
|
||||
if (envelope->holding == 0)
|
||||
{
|
||||
const int period = envelope->period * m_env_step_mul;
|
||||
const int period = std::max<int>(1, envelope->period) * m_env_step_mul;
|
||||
if ((++envelope->count) >= period)
|
||||
{
|
||||
envelope->count = 0;
|
||||
|
|
|
@ -207,13 +207,13 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
||||
if (sample!=NULL) {
|
||||
unsigned int sampleEnd=sample->offSU+sample->samples;
|
||||
unsigned int sampleEnd=sample->offSU+(s->isLoopable()?sample->loopEnd:sample->samples);
|
||||
if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1;
|
||||
chWrite(i,0x0a,sample->offSU&0xff);
|
||||
chWrite(i,0x0b,sample->offSU>>8);
|
||||
chWrite(i,0x0c,sampleEnd&0xff);
|
||||
chWrite(i,0x0d,sampleEnd>>8);
|
||||
if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples) {
|
||||
if (s->isLoopable()) {
|
||||
unsigned int sampleLoop=sample->offSU+sample->loopStart;
|
||||
if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1;
|
||||
chWrite(i,0x0e,sampleLoop&0xff);
|
||||
|
|
|
@ -1057,19 +1057,19 @@ void DivPlatformYM2203::setSkipRegisterWrites(bool value) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2203::setFlags(unsigned int flags) {
|
||||
unsigned char ayFlags=32;
|
||||
unsigned char ayFlags=16;
|
||||
if (flags==3) {
|
||||
chipClock=3000000.0;
|
||||
ayFlags=36;
|
||||
ayFlags=20;
|
||||
} else if (flags==2) {
|
||||
chipClock=4000000.0;
|
||||
ayFlags=35;
|
||||
ayFlags=19;
|
||||
} else if (flags==1) {
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
ayFlags=33;
|
||||
ayFlags=17;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
ayFlags=32;
|
||||
ayFlags=16;
|
||||
}
|
||||
ay->setFlags(ayFlags);
|
||||
rate=fm->sample_rate(chipClock);
|
||||
|
@ -1090,7 +1090,7 @@ int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
fm->set_fidelity(ymfm::OPN_FIDELITY_MIN);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,35);
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay->toggleRegisterDump(true);
|
||||
setFlags(flags);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "ym2203shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
||||
|
@ -489,9 +489,6 @@ void DivPlatformYM2203Ext::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
for (int i=3; i<6; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
|
||||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
|
|
|
@ -1459,7 +1459,7 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
}
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,35);
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay->toggleRegisterDump(true);
|
||||
reset();
|
||||
return 16;
|
||||
|
|
|
@ -1454,7 +1454,7 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
fm=new ymfm::ym2610(iface);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,35);
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay->toggleRegisterDump(true);
|
||||
reset();
|
||||
return 14;
|
||||
|
|
|
@ -1432,7 +1432,7 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i
|
|||
fm=new ymfm::ym2610b(iface);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,35);
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay->toggleRegisterDump(true);
|
||||
reset();
|
||||
return 16;
|
||||
|
|
|
@ -29,20 +29,11 @@ const char** DivPlatformZXBeeper::getRegisterSheet() {
|
|||
|
||||
const char* DivPlatformZXBeeper::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x10:
|
||||
return "10xx: Change waveform";
|
||||
break;
|
||||
case 0x11:
|
||||
return "11xx: Toggle noise mode";
|
||||
break;
|
||||
case 0x12:
|
||||
return "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)";
|
||||
break;
|
||||
case 0x13:
|
||||
return "13xx: Set LFO speed";
|
||||
return "12xx: Set pulse width";
|
||||
break;
|
||||
case 0x17:
|
||||
return "17xx: Toggle PCM mode";
|
||||
return "17xx: Trigger overlay drum";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
|
|
|
@ -170,8 +170,8 @@ String SafeReader::readStringLine() {
|
|||
unsigned char c;
|
||||
if (isEOF()) throw EndOfFileException(this, len);
|
||||
|
||||
while (!isEOF() && (c = readC()) != 0) {
|
||||
if (c=='\r'||c=='\n') {
|
||||
while (!isEOF() && (c=readC())!=0) {
|
||||
if (c=='\r' || c=='\n') {
|
||||
break;
|
||||
}
|
||||
ret.push_back(c);
|
||||
|
@ -179,17 +179,17 @@ String SafeReader::readStringLine() {
|
|||
return ret;
|
||||
}
|
||||
|
||||
String SafeReader::readStringToken(unsigned char delim) {
|
||||
String SafeReader::readStringToken(unsigned char delim, bool stripContiguous) {
|
||||
String ret;
|
||||
unsigned char c;
|
||||
if (isEOF()) throw EndOfFileException(this, len);
|
||||
|
||||
while (!isEOF() && (c=readC())!=0) {
|
||||
if (c == '\r' || c == '\n') {
|
||||
if (c=='\r' || c=='\n') {
|
||||
break;
|
||||
}
|
||||
if (c == delim) {
|
||||
if (ret.length() == 0) {
|
||||
if (c==delim) {
|
||||
if (ret.length()==0 && stripContiguous) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
@ -200,5 +200,6 @@ String SafeReader::readStringToken(unsigned char delim) {
|
|||
}
|
||||
|
||||
String SafeReader::readStringToken() {
|
||||
return readStringToken(' ');
|
||||
// This will strip LHS whitespace and only return contents after it.
|
||||
return readStringToken(' ', true);
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ class SafeReader {
|
|||
String readString();
|
||||
String readString(size_t len);
|
||||
String readStringLine();
|
||||
String readStringToken(unsigned char delim);
|
||||
String readStringToken(unsigned char delim, bool stripContiguous);
|
||||
String readStringToken();
|
||||
inline bool isEOF() { return curSeek >= len; };
|
||||
|
||||
|
|
|
@ -443,6 +443,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0x04);
|
||||
w->writeC(0x00);
|
||||
break;
|
||||
case DIV_SYSTEM_SCC:
|
||||
case DIV_SYSTEM_SCC_PLUS:
|
||||
w->writeC(0xd2);
|
||||
w->writeC(baseAddr2|3);
|
||||
w->writeC(0);
|
||||
w->writeC(0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1395,16 +1402,17 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
|||
size_t memPos=0;
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* sample=song.sample[i];
|
||||
if ((memPos&0xff0000)!=((memPos+sample->length8)&0xff0000)) {
|
||||
unsigned int alignedSize=(sample->length8+0xff)&(~0xff);
|
||||
if (alignedSize>65536) alignedSize=65536;
|
||||
if ((memPos&0xff0000)!=((memPos+alignedSize)&0xff0000)) {
|
||||
memPos=(memPos+0xffff)&0xff0000;
|
||||
}
|
||||
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
|
||||
if (memPos>=16777216) break;
|
||||
sample->offSegaPCM=memPos;
|
||||
unsigned int alignedSize=(sample->length8+0xff)&(~0xff);
|
||||
unsigned int readPos=0;
|
||||
if (alignedSize>65536) alignedSize=65536;
|
||||
for (unsigned int j=0; j<alignedSize; j++) {
|
||||
if ((sample->loopMode && readPos>=sample->loopEnd) || readPos>=sample->length8) {
|
||||
if (((sample->loopMode != DIV_SAMPLE_LOOPMODE_ONESHOT) && readPos>=sample->loopEnd) || readPos>=sample->length8) {
|
||||
if (sample->isLoopable()) {
|
||||
readPos=sample->loopStart;
|
||||
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
||||
|
|
|
@ -1221,9 +1221,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
||||
hasOpened=fileDialog->openLoad(
|
||||
"Open File",
|
||||
{"compatible files", "*.fur *.dmf *.mod *.ftm",
|
||||
{"compatible files", "*.fur *.dmf *.mod",
|
||||
"all files", ".*"},
|
||||
"compatible files{.fur,.dmf,.mod,.ftm},.*",
|
||||
"compatible files{.fur,.dmf,.mod},.*",
|
||||
workingDirSong,
|
||||
dpiScale
|
||||
);
|
||||
|
@ -1261,9 +1261,25 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||
hasOpened=fileDialog->openLoad(
|
||||
"Load Instrument",
|
||||
{"compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.opm",
|
||||
// TODO supply loadable formats in a dynamic, scalable, "DRY" way.
|
||||
{"all compatible files", "*.fui *.dmp *.tfi *.vgi *.s3i *.sbi *.opli *.opni *.y12 *.bnk *.ff *.gyb *.opm *.wopl *.wopn",
|
||||
"Furnace instrument", "*.fui",
|
||||
"DefleMask preset", "*.dmp",
|
||||
"TFM Music Maker instrument", "*.tfi",
|
||||
"VGM Music Maker instrument", "*.vgi",
|
||||
"Scream Tracker 3 instrument", "*.s3i",
|
||||
"SoundBlaster instrument", "*.sbi",
|
||||
"Wohlstand OPL instrument", "*.opli",
|
||||
"Wohlstand OPN instrument", "*.opni",
|
||||
"Gens KMod patch dump", "*.y12",
|
||||
"BNK file (AdLib)", "*.bnk",
|
||||
"FF preset bank", "*.ff",
|
||||
"2612edit GYB preset bank", "*.gyb",
|
||||
"VOPM preset bank", "*.opm",
|
||||
"Wohlstand WOPL bank", "*.wopl",
|
||||
"Wohlstand WOPN bank", "*.wopn",
|
||||
"all files", ".*"},
|
||||
"compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.opm},.*",
|
||||
"all compatible files{.fui,.dmp,.tfi,.vgi,.s3i,.sbi,.opli,.opni,.y12,.bnk,.ff,.gyb,.opm,.wopl,.wopn},.*",
|
||||
workingDirIns,
|
||||
dpiScale,
|
||||
[this](const char* path) {
|
||||
|
@ -3476,10 +3492,34 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
break;
|
||||
case GUI_WARN_CLEAR:
|
||||
if (ImGui::Button("Song (orders and patterns)")) {
|
||||
if (ImGui::Button("All subsongs")) {
|
||||
stop();
|
||||
e->clearSubSongs();
|
||||
curOrder=0;
|
||||
oldOrder=0;
|
||||
oldOrder1=0;
|
||||
MARK_MODIFIED;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Current subsong")) {
|
||||
stop();
|
||||
e->lockEngine([this]() {
|
||||
e->song.clearSongData();
|
||||
e->curSubSong->clearData();
|
||||
});
|
||||
e->setOrder(0);
|
||||
curOrder=0;
|
||||
oldOrder=0;
|
||||
oldOrder1=0;
|
||||
MARK_MODIFIED;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Orders")) {
|
||||
stop();
|
||||
e->lockEngine([this]() {
|
||||
memset(e->curOrders->ord,0,DIV_MAX_CHANS*256);
|
||||
e->curSubSong->ordersLen=1;
|
||||
});
|
||||
e->setOrder(0);
|
||||
curOrder=0;
|
||||
|
@ -3550,7 +3590,7 @@ bool FurnaceGUI::loop() {
|
|||
// backup trigger
|
||||
if (modified) {
|
||||
if (backupTimer>0) {
|
||||
backupTimer-=ImGui::GetIO().DeltaTime;
|
||||
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
|
||||
if (backupTimer<=0) {
|
||||
backupTask=std::async(std::launch::async,[this]() -> bool {
|
||||
if (backupPath==curFileName) {
|
||||
|
|
|
@ -801,7 +801,7 @@ class FurnaceGUI {
|
|||
double aboutScroll, aboutSin;
|
||||
float aboutHue;
|
||||
|
||||
double backupTimer;
|
||||
std::atomic<double> backupTimer;
|
||||
std::future<bool> backupTask;
|
||||
std::mutex backupLock;
|
||||
String backupPath;
|
||||
|
|
|
@ -2790,6 +2790,11 @@ void FurnaceGUI::drawInsEdit() {
|
|||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Transwave Macros")) {
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Transwave control",&ins->std.fbMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,transwaveControlModes));
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Transwave slice",&ins->std.fmsMacro,0,4095,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::EndTabItem();
|
||||
|
@ -3438,8 +3443,6 @@ void FurnaceGUI::drawInsEdit() {
|
|||
macroList.push_back(FurnaceGUIMacroDesc("Envelope K2 ramp",&ins->std.ex7Macro,-128,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Envelope mode",&ins->std.ex8Macro,0,2,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,es5506EnvelopeModes));
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,es5506ControlModes));
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Transwave control",&ins->std.fbMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,transwaveControlModes));
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Transwave slice",&ins->std.fmsMacro,0,4095,160,uiColors[GUI_COLOR_MACRO_OTHER]));
|
||||
}
|
||||
if (ins->type==DIV_INS_SU) {
|
||||
macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex3Macro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,suControlBits));
|
||||
|
|
Loading…
Reference in New Issue