mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-04 20:05:05 +00:00
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
30 changed files with 1287 additions and 260 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -97,6 +97,10 @@ jobs:
|
||||||
if [ '${{ runner.os }}' == 'macOS' ]; then
|
if [ '${{ runner.os }}' == 'macOS' ]; then
|
||||||
amount=3
|
amount=3
|
||||||
fi
|
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}"
|
echo "Amount of cores we can build with: ${amount}"
|
||||||
|
|
||||||
|
|
|
@ -565,6 +565,11 @@ if (NOT ANDROID)
|
||||||
install(DIRECTORY papers DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
install(DIRECTORY papers DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||||
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/furnace)
|
install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DATADIR}/licenses/furnace)
|
||||||
install(DIRECTORY demos DESTINATION ${CMAKE_INSTALL_DATADIR}/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)
|
install(FILES res/logo.png RENAME furnace.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/1024x1024/apps)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -814,6 +814,16 @@ bool DivEngine::removeSubSong(int index) {
|
||||||
return true;
|
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) {
|
void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
||||||
int chanCount=chans;
|
int chanCount=chans;
|
||||||
quitDispatch();
|
quitDispatch();
|
||||||
|
@ -1945,13 +1955,13 @@ int DivEngine::addSampleFromFile(const char* path) {
|
||||||
}
|
}
|
||||||
extS+=i;
|
extS+=i;
|
||||||
}
|
}
|
||||||
if (extS==String(".dmc")) { // read as .dmc
|
if (extS==".dmc") { // read as .dmc
|
||||||
size_t len=0;
|
size_t len=0;
|
||||||
DivSample* sample=new DivSample;
|
DivSample* sample=new DivSample;
|
||||||
int sampleCount=(int)song.sample.size();
|
int sampleCount=(int)song.sample.size();
|
||||||
sample->name=stripPath;
|
sample->name=stripPath;
|
||||||
|
|
||||||
FILE* f=fopen(path,"rb");
|
FILE* f=ps_fopen(path,"rb");
|
||||||
if (f==NULL) {
|
if (f==NULL) {
|
||||||
BUSY_END;
|
BUSY_END;
|
||||||
lastError=fmt::sprintf("could not open file! (%s)",strerror(errno));
|
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 loadOPNI(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadY12(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 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 loadOPM(SafeReader& reader, std::vector<DivInstrument*>& ret, String& stripPath);
|
||||||
void loadFF(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);
|
int loadSampleROM(String path, ssize_t expectedSize, unsigned char*& ret);
|
||||||
|
|
||||||
|
@ -820,6 +823,9 @@ class DivEngine {
|
||||||
// remove subsong
|
// remove subsong
|
||||||
bool removeSubSong(int index);
|
bool removeSubSong(int index);
|
||||||
|
|
||||||
|
// clear all subsong data
|
||||||
|
void clearSubSongs();
|
||||||
|
|
||||||
// change system
|
// change system
|
||||||
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
void changeSystem(int index, DivSystem which, bool preserveOrder=true);
|
||||||
|
|
||||||
|
|
|
@ -2682,16 +2682,19 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
||||||
if (song.ins.size()>256) {
|
if (song.ins.size()>256) {
|
||||||
logE("maximum number of instruments is 256!");
|
logE("maximum number of instruments is 256!");
|
||||||
lastError="maximum number of instruments is 256";
|
lastError="maximum number of instruments is 256";
|
||||||
|
saveLock.unlock();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (song.wave.size()>256) {
|
if (song.wave.size()>256) {
|
||||||
logE("maximum number of wavetables is 256!");
|
logE("maximum number of wavetables is 256!");
|
||||||
lastError="maximum number of wavetables is 256";
|
lastError="maximum number of wavetables is 256";
|
||||||
|
saveLock.unlock();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (song.sample.size()>256) {
|
if (song.sample.size()>256) {
|
||||||
logE("maximum number of samples is 256!");
|
logE("maximum number of samples is 256!");
|
||||||
lastError="maximum number of samples is 256";
|
lastError="maximum number of samples is 256";
|
||||||
|
saveLock.unlock();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -660,6 +660,7 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ay->device_start();
|
ay->device_start();
|
||||||
|
ay->device_reset();
|
||||||
|
|
||||||
stereo=(flags>>6)&1;
|
stereo=(flags>>6)&1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,15 @@
|
||||||
rWrite((a),__VA_ARGS__); \
|
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[]={
|
const char* regCheatSheetES5506[]={
|
||||||
"CR", "00|00",
|
"CR", "00|00",
|
||||||
|
@ -167,6 +176,8 @@ const char* DivPlatformES5506::getEffectName(unsigned char effect) {
|
||||||
return "3xxx: Set filter coefficient K1";
|
return "3xxx: Set filter coefficient K1";
|
||||||
} else if ((effect&0xf0)==0x40) {
|
} else if ((effect&0xf0)==0x40) {
|
||||||
return "4xxx: Set filter coefficient K2";
|
return "4xxx: Set filter coefficient K2";
|
||||||
|
} else if ((effect&0xf0)==0x50) {
|
||||||
|
return "5xxx: Set transwave slice point";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -268,6 +279,101 @@ void DivPlatformES5506::e_pin(bool state)
|
||||||
pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0000:0x0040)|0x08,0x78);
|
pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0000:0x0040)|0x08,0x78);
|
||||||
chan[ch].isReverseLoop=false;
|
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
|
// control macros
|
||||||
if (!chan[i].keyOn) {
|
if (chan[i].active && chan[i].std.alg.had) {
|
||||||
if (chan[i].active && chan[i].std.alg.had) {
|
if (chan[i].pcm.pause!=(bool)(chan[i].std.alg.val&1)) {
|
||||||
if (chan[i].pcm.pause!=(bool)(chan[i].std.alg.val&1)) {
|
chan[i].pcm.pause=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);
|
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
|
// update registers
|
||||||
if (chan[i].volChanged.changed) {
|
if (chan[i].volChanged.changed) {
|
||||||
if (!isMuted[i]) { // calculate volume (16 bit)
|
if (!isMuted[i]) { // calculate volume (16 bit)
|
||||||
|
@ -489,34 +637,193 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
chan[i].volChanged.changed=0;
|
chan[i].volChanged.changed=0;
|
||||||
}
|
}
|
||||||
if (chan[i].pcmChanged) {
|
if (chan[i].pcmChanged.changed) {
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins);
|
if (!chan[i].isTransWave) {
|
||||||
if (!ins->amiga.useNoteMap) {
|
if (chan[i].pcmChanged.transwaveInd && (!ins->amiga.useNoteMap && ins->amiga.transWave.enable)) {
|
||||||
double off=1.0;
|
const int next=chan[i].pcm.next;
|
||||||
if (chan[i].pcm.next>=0 && chan[i].pcm.next<parent->song.sampleLen) {
|
bool sampleVaild=false;
|
||||||
chan[i].pcm.index=chan[i].pcm.next;
|
if (next>=0 && next<ins->amiga.transWaveMap.size()) {
|
||||||
DivSample* s=parent->getSample(chan[i].pcm.next);
|
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
|
||||||
if (s->centerRate<1) {
|
int sample=transWaveInd.ind;
|
||||||
off=1.0;
|
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||||
} else {
|
if (chan[i].pcm.index!=sample) {
|
||||||
off=(double)s->centerRate/8363.0;
|
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;
|
chan[i].pcmChanged.transwaveInd=0;
|
||||||
const unsigned int length=s->samples-1;
|
}
|
||||||
const unsigned int end=start+(length<<11);
|
if ((!chan[i].pcmChanged.transwaveInd) && (!chan[i].isTransWave)) {
|
||||||
chan[i].pcm.loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
|
if (chan[i].pcmChanged.index) {
|
||||||
chan[i].pcm.freqOffs=PITCH_OFFSET*off;
|
const int next=chan[i].pcm.next;
|
||||||
chan[i].pcm.reversed=ins->amiga.reversed;
|
bool sampleVaild=false;
|
||||||
chan[i].pcm.bank=(s->offES5506>>22)&3;
|
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (next>=0 && next<120)) ||
|
||||||
chan[i].pcm.start=start;
|
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (next>=0 && next<ins->amiga.transWaveMap.size())) ||
|
||||||
chan[i].pcm.end=end;
|
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (next>=0 && next<parent->song.sampleLen))) {
|
||||||
chan[i].pcm.length=length;
|
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[next];
|
||||||
chan[i].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800;
|
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
|
||||||
chan[i].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80;
|
int sample=next;
|
||||||
chan[i].keyOn=true;
|
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].filterChanged.changed) {
|
||||||
if (!chan[i].keyOn) {
|
if (!chan[i].keyOn) {
|
||||||
|
@ -564,12 +871,13 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
if (chan[i].noteChanged.offs) {
|
if (chan[i].noteChanged.offs) {
|
||||||
if (chan[i].pcm.freqOffs!=chan[i].pcm.nextFreqOffs) {
|
if (chan[i].pcm.freqOffs!=chan[i].pcm.nextFreqOffs) {
|
||||||
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) {
|
if (chan[i].nextFreq!=nextFreq) {
|
||||||
chan[i].nextFreq=nextFreq;
|
chan[i].nextFreq=nextFreq;
|
||||||
chan[i].noteChanged.freq=1;
|
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].noteChanged.note) { // note value changed or frequency offset is changed
|
||||||
if (chan[i].prevNote!=chan[i].nextNote) {
|
if (chan[i].prevNote!=chan[i].nextNote) {
|
||||||
|
@ -580,17 +888,19 @@ void DivPlatformES5506::tick(bool sysTick) {
|
||||||
chan[i].noteChanged.freq=1;
|
chan[i].noteChanged.freq=1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
chan[i].noteChanged.note=0;
|
||||||
}
|
}
|
||||||
if (chan[i].noteChanged.freq) {
|
if (chan[i].noteChanged.freq) {
|
||||||
if (chan[i].baseFreq!=chan[i].nextFreq) {
|
if (chan[i].baseFreq!=chan[i].nextFreq) {
|
||||||
chan[i].baseFreq=chan[i].nextFreq;
|
chan[i].baseFreq=chan[i].nextFreq;
|
||||||
chan[i].freqChanged=true;
|
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) {
|
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].keyOn) {
|
||||||
if (chan[i].pcm.index>=0 && chan[i].pcm.index<parent->song.sampleLen) {
|
if (chan[i].pcm.index>=0 && chan[i].pcm.index<parent->song.sampleLen) {
|
||||||
chan[i].k1Prev=0xffff;
|
chan[i].k1Prev=0xffff;
|
||||||
|
@ -693,7 +1003,8 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
||||||
sampleVaild=true;
|
sampleVaild=true;
|
||||||
chan[c.chan].pcm.index=sample;
|
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].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.sliceEnable=ins->amiga.transWave.sliceEnable;
|
||||||
chan[c.chan].transWave.ind=ins->amiga.transWave.ind;
|
chan[c.chan].transWave.ind=ins->amiga.transWave.ind;
|
||||||
DivSample* s=parent->getSample(sample);
|
DivSample* s=parent->getSample(sample);
|
||||||
|
@ -716,7 +1027,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
||||||
if (ins->amiga.transWave.enable) {
|
if (ins->amiga.transWave.enable) {
|
||||||
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
|
||||||
loopMode=transWaveInd.loopMode;
|
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;
|
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
|
||||||
}
|
}
|
||||||
// get loop position
|
// get loop position
|
||||||
|
@ -936,10 +1247,13 @@ int DivPlatformES5506::dispatch(DivCommand c) {
|
||||||
case DIV_CMD_SAMPLE_POS: {
|
case DIV_CMD_SAMPLE_POS: {
|
||||||
if (chan[c.chan].useWave) break;
|
if (chan[c.chan].useWave) break;
|
||||||
if (chan[c.chan].active) {
|
if (chan[c.chan].active) {
|
||||||
const unsigned int pos=chan[c.chan].pcm.reversed?(chan[c.chan].pcm.length-c.value):c.value;
|
const unsigned int start=chan[c.chan].transWave.enable?chan[c.chan].pcm.loopStart:chan[c.chan].pcm.start;
|
||||||
if ((chan[c.chan].pcm.reversed && pos>0) || ((!chan[c.chan].pcm.reversed) && pos<chan[c.chan].pcm.length)) {
|
const unsigned int end=chan[c.chan].transWave.enable?chan[c.chan].pcm.loopEnd:chan[c.chan].pcm.length;
|
||||||
pageWrite(0x20|c.chan,0x03,chan[c.chan].pcm.start+(pos<<11));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -995,6 +1309,7 @@ void DivPlatformES5506::reset() {
|
||||||
isMasked=false;
|
isMasked=false;
|
||||||
isReaded=false;
|
isReaded=false;
|
||||||
irqTrigger=false;
|
irqTrigger=false;
|
||||||
|
transwaveCh=0;
|
||||||
prevChanCycle=0;
|
prevChanCycle=0;
|
||||||
chanMax=initChanMax;
|
chanMax=initChanMax;
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
struct Channel {
|
struct Channel {
|
||||||
struct PCM {
|
struct PCM {
|
||||||
|
bool isNoteMap;
|
||||||
int index, next;
|
int index, next;
|
||||||
int note;
|
int note;
|
||||||
double freqOffs;
|
double freqOffs;
|
||||||
|
@ -45,6 +46,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
unsigned int loopEnd;
|
unsigned int loopEnd;
|
||||||
DivSampleLoopMode loopMode;
|
DivSampleLoopMode loopMode;
|
||||||
PCM():
|
PCM():
|
||||||
|
isNoteMap(false),
|
||||||
index(-1),
|
index(-1),
|
||||||
next(-1),
|
next(-1),
|
||||||
note(0),
|
note(0),
|
||||||
|
@ -62,7 +64,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
} pcm;
|
} pcm;
|
||||||
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, prevNote, ins, wave;
|
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, prevNote, ins, wave;
|
||||||
unsigned int volMacroMax, panMacroMax;
|
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
|
struct NoteChanged { // Note changed flags
|
||||||
union { // pack flag bits in single byte
|
union { // pack flag bits in single byte
|
||||||
|
@ -125,6 +128,20 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
changed(0) {}
|
changed(0) {}
|
||||||
} envChanged;
|
} 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 k1Offs, k2Offs;
|
||||||
signed int k1Slide, k2Slide;
|
signed int k1Slide, k2Slide;
|
||||||
signed int k1Prev, k2Prev;
|
signed int k1Prev, k2Prev;
|
||||||
|
@ -170,6 +187,9 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
inPorta(false),
|
inPorta(false),
|
||||||
useWave(false),
|
useWave(false),
|
||||||
isReverseLoop(false),
|
isReverseLoop(false),
|
||||||
|
isTranswave(false),
|
||||||
|
transwaveIRQ(false),
|
||||||
|
cr(0),
|
||||||
k1Offs(0),
|
k1Offs(0),
|
||||||
k2Offs(0),
|
k2Offs(0),
|
||||||
k1Slide(0),
|
k1Slide(0),
|
||||||
|
@ -225,6 +245,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
|
||||||
unsigned int irqv;
|
unsigned int irqv;
|
||||||
bool isMasked, isReaded;
|
bool isMasked, isReaded;
|
||||||
bool irqTrigger;
|
bool irqTrigger;
|
||||||
|
unsigned int curCR;
|
||||||
|
unsigned char transwaveCh;
|
||||||
unsigned char prevChanCycle;
|
unsigned char prevChanCycle;
|
||||||
|
|
||||||
unsigned char initChanMax, chanMax;
|
unsigned char initChanMax, chanMax;
|
||||||
|
|
|
@ -357,7 +357,9 @@ void DivPlatformN163::tick(bool sysTick) {
|
||||||
chan[i].waveUpdated=false;
|
chan[i].waveUpdated=false;
|
||||||
}
|
}
|
||||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
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<0) chan[i].freq=0;
|
||||||
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
|
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
|
||||||
if (chan[i].keyOn) {
|
if (chan[i].keyOn) {
|
||||||
|
|
|
@ -595,6 +595,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
||||||
for (int i=0; i<totalChans; i++) {
|
for (int i=0; i<totalChans; i++) {
|
||||||
if (chan[i].freqChanged) {
|
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);
|
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;
|
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].pitch2;
|
||||||
chan[i].freqH=freqt>>8;
|
chan[i].freqH=freqt>>8;
|
||||||
|
@ -844,16 +845,18 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
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].state.opllPreset==16 && chan[c.chan].state.fixedDrums) { // drums
|
||||||
if (c.chan==melodicChans) {
|
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) {
|
} 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) {
|
} 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 {
|
} else {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
|
chan[c.chan].fixedFreq=0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
|
chan[c.chan].fixedFreq=0;
|
||||||
}
|
}
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
|
|
|
@ -40,7 +40,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
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;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnacePCM, inPorta, fourOp, hardReset;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
|
@ -58,6 +58,7 @@ class DivPlatformOPL: public DivDispatch {
|
||||||
note(0),
|
note(0),
|
||||||
ins(-1),
|
ins(-1),
|
||||||
sample(-1),
|
sample(-1),
|
||||||
|
fixedFreq(0),
|
||||||
active(false),
|
active(false),
|
||||||
insChanged(true),
|
insChanged(true),
|
||||||
freqChanged(false),
|
freqChanged(false),
|
||||||
|
|
|
@ -301,6 +301,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
||||||
for (int i=0; i<11; i++) {
|
for (int i=0; i<11; i++) {
|
||||||
if (chan[i].freqChanged) {
|
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);
|
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;
|
if (chan[i].freq>262143) chan[i].freq=262143;
|
||||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||||
chan[i].freqL=freqt&0xff;
|
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) {
|
if (chan[c.chan].state.opllPreset==16 && chan[c.chan].state.fixedDrums) {
|
||||||
switch (c.chan) {
|
switch (c.chan) {
|
||||||
case 6:
|
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;
|
break;
|
||||||
case 7: case 10:
|
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;
|
break;
|
||||||
case 8: case 9:
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
|
chan[c.chan].fixedFreq=0;
|
||||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ class DivPlatformOPLL: public DivDispatch {
|
||||||
DivInstrumentFM state;
|
DivInstrumentFM state;
|
||||||
DivMacroInt std;
|
DivMacroInt std;
|
||||||
unsigned char freqH, freqL;
|
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;
|
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta;
|
||||||
int vol, outVol;
|
int vol, outVol;
|
||||||
unsigned char pan;
|
unsigned char pan;
|
||||||
|
@ -50,6 +50,7 @@ class DivPlatformOPLL: public DivDispatch {
|
||||||
pitch2(0),
|
pitch2(0),
|
||||||
note(0),
|
note(0),
|
||||||
ins(-1),
|
ins(-1),
|
||||||
|
fixedFreq(0),
|
||||||
active(false),
|
active(false),
|
||||||
insChanged(true),
|
insChanged(true),
|
||||||
freqChanged(false),
|
freqChanged(false),
|
||||||
|
|
|
@ -155,7 +155,7 @@ void DivPlatformPCE::tick(bool sysTick) {
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
if (chan[i].std.vol.had) {
|
if (chan[i].std.vol.had) {
|
||||||
chan[i].outVol=((chan[i].vol&31)*MIN(31,chan[i].std.vol.val))>>5;
|
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
|
// ignore for now
|
||||||
} else {
|
} else {
|
||||||
chWrite(i,0x04,0x80|chan[i].outVol);
|
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) {
|
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
//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);
|
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;
|
double off=1.0;
|
||||||
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
|
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[i].dacSample);
|
DivSample* s=parent->getSample(chan[i].dacSample);
|
||||||
|
@ -268,8 +268,9 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
chan[c.chan].pcm=false;
|
chan[c.chan].pcm=false;
|
||||||
}
|
}
|
||||||
if (chan[c.chan].pcm) {
|
if (chan[c.chan].pcm) {
|
||||||
if (skipRegisterWrites) break;
|
|
||||||
if (ins->type==DIV_INS_AMIGA) {
|
if (ins->type==DIV_INS_AMIGA) {
|
||||||
|
chan[c.chan].furnaceDac=true;
|
||||||
|
if (skipRegisterWrites) break;
|
||||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||||
chan[c.chan].dacSample=-1;
|
chan[c.chan].dacSample=-1;
|
||||||
|
@ -291,8 +292,9 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
chan[c.chan].active=true;
|
chan[c.chan].active=true;
|
||||||
chan[c.chan].macroInit(ins);
|
chan[c.chan].macroInit(ins);
|
||||||
//chan[c.chan].keyOn=true;
|
//chan[c.chan].keyOn=true;
|
||||||
chan[c.chan].furnaceDac=true;
|
|
||||||
} else {
|
} else {
|
||||||
|
chan[c.chan].furnaceDac=false;
|
||||||
|
if (skipRegisterWrites) break;
|
||||||
if (c.value!=DIV_NOTE_NULL) {
|
if (c.value!=DIV_NOTE_NULL) {
|
||||||
chan[c.chan].note=c.value;
|
chan[c.chan].note=c.value;
|
||||||
}
|
}
|
||||||
|
@ -311,7 +313,6 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
||||||
chWrite(c.chan,0x04,0xdf);
|
chWrite(c.chan,0x04,0xdf);
|
||||||
addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dacRate);
|
addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dacRate);
|
||||||
}
|
}
|
||||||
chan[c.chan].furnaceDac=false;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,11 +181,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
chan[c.chan].macroInit(ins);
|
chan[c.chan].macroInit(ins);
|
||||||
if (dumpWrites) { // Sega PCM writes
|
if (dumpWrites) { // Sega PCM writes
|
||||||
DivSample* s=parent->getSample(chan[c.chan].pcm.sample);
|
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(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
|
||||||
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
||||||
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
||||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+s->length8-1)>>8));
|
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8));
|
||||||
if (!s->isLoopable()) {
|
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));
|
addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
|
||||||
} else {
|
} else {
|
||||||
int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP;
|
int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP;
|
||||||
|
@ -212,11 +214,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
||||||
chan[c.chan].furnacePCM=false;
|
chan[c.chan].furnacePCM=false;
|
||||||
if (dumpWrites) { // Sega PCM writes
|
if (dumpWrites) { // Sega PCM writes
|
||||||
DivSample* s=parent->getSample(chan[c.chan].pcm.sample);
|
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(0x10086+(c.chan<<3),3+((s->offSegaPCM>>16)<<3));
|
||||||
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
addWrite(0x10084+(c.chan<<3),(s->offSegaPCM)&0xff);
|
||||||
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
addWrite(0x10085+(c.chan<<3),(s->offSegaPCM>>8)&0xff);
|
||||||
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+s->length8-1)>>8));
|
addWrite(0x10006+(c.chan<<3),MIN(255,((s->offSegaPCM&0xffff)+actualLength-1)>>8));
|
||||||
if (!s->isLoopable()) {
|
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));
|
addWrite(0x10086+(c.chan<<3),2+((s->offSegaPCM>>16)<<3));
|
||||||
} else {
|
} else {
|
||||||
int loopPos=(s->offSegaPCM&0xffff)+s->loopStart+s->loopOffP;
|
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++)
|
for (int chan = 0; chan < NUM_CHANNELS; chan++)
|
||||||
{
|
{
|
||||||
tone = &m_tone[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);
|
tone->count += is_expanded_mode() ? 32 : ((m_feature & PSG_HAS_EXPANDED_MODE) ? 1 : 2);
|
||||||
while (tone->count >= period)
|
while (tone->count >= period)
|
||||||
{
|
{
|
||||||
|
@ -1115,7 +1115,7 @@ void ay8910_device::sound_stream_update(short** outputs, int outLen)
|
||||||
envelope = &m_envelope[chan];
|
envelope = &m_envelope[chan];
|
||||||
if (envelope->holding == 0)
|
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)
|
if ((++envelope->count) >= period)
|
||||||
{
|
{
|
||||||
envelope->count = 0;
|
envelope->count = 0;
|
||||||
|
|
|
@ -207,13 +207,13 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
||||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||||
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
||||||
if (sample!=NULL) {
|
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;
|
if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1;
|
||||||
chWrite(i,0x0a,sample->offSU&0xff);
|
chWrite(i,0x0a,sample->offSU&0xff);
|
||||||
chWrite(i,0x0b,sample->offSU>>8);
|
chWrite(i,0x0b,sample->offSU>>8);
|
||||||
chWrite(i,0x0c,sampleEnd&0xff);
|
chWrite(i,0x0c,sampleEnd&0xff);
|
||||||
chWrite(i,0x0d,sampleEnd>>8);
|
chWrite(i,0x0d,sampleEnd>>8);
|
||||||
if (sample->loopStart>=0 && sample->loopStart<(int)sample->samples) {
|
if (s->isLoopable()) {
|
||||||
unsigned int sampleLoop=sample->offSU+sample->loopStart;
|
unsigned int sampleLoop=sample->offSU+sample->loopStart;
|
||||||
if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1;
|
if (sampleLoop>=getSampleMemCapacity(0)) sampleLoop=getSampleMemCapacity(0)-1;
|
||||||
chWrite(i,0x0e,sampleLoop&0xff);
|
chWrite(i,0x0e,sampleLoop&0xff);
|
||||||
|
|
|
@ -1057,19 +1057,19 @@ void DivPlatformYM2203::setSkipRegisterWrites(bool value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformYM2203::setFlags(unsigned int flags) {
|
void DivPlatformYM2203::setFlags(unsigned int flags) {
|
||||||
unsigned char ayFlags=32;
|
unsigned char ayFlags=16;
|
||||||
if (flags==3) {
|
if (flags==3) {
|
||||||
chipClock=3000000.0;
|
chipClock=3000000.0;
|
||||||
ayFlags=36;
|
ayFlags=20;
|
||||||
} else if (flags==2) {
|
} else if (flags==2) {
|
||||||
chipClock=4000000.0;
|
chipClock=4000000.0;
|
||||||
ayFlags=35;
|
ayFlags=19;
|
||||||
} else if (flags==1) {
|
} else if (flags==1) {
|
||||||
chipClock=COLOR_PAL*4.0/5.0;
|
chipClock=COLOR_PAL*4.0/5.0;
|
||||||
ayFlags=33;
|
ayFlags=17;
|
||||||
} else {
|
} else {
|
||||||
chipClock=COLOR_NTSC;
|
chipClock=COLOR_NTSC;
|
||||||
ayFlags=32;
|
ayFlags=16;
|
||||||
}
|
}
|
||||||
ay->setFlags(ayFlags);
|
ay->setFlags(ayFlags);
|
||||||
rate=fm->sample_rate(chipClock);
|
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);
|
fm->set_fidelity(ymfm::OPN_FIDELITY_MIN);
|
||||||
// YM2149, 2MHz
|
// YM2149, 2MHz
|
||||||
ay=new DivPlatformAY8910;
|
ay=new DivPlatformAY8910;
|
||||||
ay->init(p,3,sugRate,35);
|
ay->init(p,3,sugRate,19);
|
||||||
ay->toggleRegisterDump(true);
|
ay->toggleRegisterDump(true);
|
||||||
setFlags(flags);
|
setFlags(flags);
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include "../engine.h"
|
#include "../engine.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "ym2610shared.h"
|
#include "ym2203shared.h"
|
||||||
#include "fmshared_OPN.h"
|
#include "fmshared_OPN.h"
|
||||||
|
|
||||||
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
||||||
|
@ -489,9 +489,6 @@ void DivPlatformYM2203Ext::forceIns() {
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i=3; i<6; i++) {
|
|
||||||
chan[i].insChanged=true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ay->forceIns();
|
ay->forceIns();
|
||||||
ay->flushWrites();
|
ay->flushWrites();
|
||||||
|
|
|
@ -1459,7 +1459,7 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, unsigned in
|
||||||
}
|
}
|
||||||
// YM2149, 2MHz
|
// YM2149, 2MHz
|
||||||
ay=new DivPlatformAY8910;
|
ay=new DivPlatformAY8910;
|
||||||
ay->init(p,3,sugRate,35);
|
ay->init(p,3,sugRate,19);
|
||||||
ay->toggleRegisterDump(true);
|
ay->toggleRegisterDump(true);
|
||||||
reset();
|
reset();
|
||||||
return 16;
|
return 16;
|
||||||
|
|
|
@ -1454,7 +1454,7 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in
|
||||||
fm=new ymfm::ym2610(iface);
|
fm=new ymfm::ym2610(iface);
|
||||||
// YM2149, 2MHz
|
// YM2149, 2MHz
|
||||||
ay=new DivPlatformAY8910;
|
ay=new DivPlatformAY8910;
|
||||||
ay->init(p,3,sugRate,35);
|
ay->init(p,3,sugRate,19);
|
||||||
ay->toggleRegisterDump(true);
|
ay->toggleRegisterDump(true);
|
||||||
reset();
|
reset();
|
||||||
return 14;
|
return 14;
|
||||||
|
|
|
@ -1432,7 +1432,7 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i
|
||||||
fm=new ymfm::ym2610b(iface);
|
fm=new ymfm::ym2610b(iface);
|
||||||
// YM2149, 2MHz
|
// YM2149, 2MHz
|
||||||
ay=new DivPlatformAY8910;
|
ay=new DivPlatformAY8910;
|
||||||
ay->init(p,3,sugRate,35);
|
ay->init(p,3,sugRate,19);
|
||||||
ay->toggleRegisterDump(true);
|
ay->toggleRegisterDump(true);
|
||||||
reset();
|
reset();
|
||||||
return 16;
|
return 16;
|
||||||
|
|
|
@ -29,20 +29,11 @@ const char** DivPlatformZXBeeper::getRegisterSheet() {
|
||||||
|
|
||||||
const char* DivPlatformZXBeeper::getEffectName(unsigned char effect) {
|
const char* DivPlatformZXBeeper::getEffectName(unsigned char effect) {
|
||||||
switch (effect) {
|
switch (effect) {
|
||||||
case 0x10:
|
|
||||||
return "10xx: Change waveform";
|
|
||||||
break;
|
|
||||||
case 0x11:
|
|
||||||
return "11xx: Toggle noise mode";
|
|
||||||
break;
|
|
||||||
case 0x12:
|
case 0x12:
|
||||||
return "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)";
|
return "12xx: Set pulse width";
|
||||||
break;
|
|
||||||
case 0x13:
|
|
||||||
return "13xx: Set LFO speed";
|
|
||||||
break;
|
break;
|
||||||
case 0x17:
|
case 0x17:
|
||||||
return "17xx: Toggle PCM mode";
|
return "17xx: Trigger overlay drum";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -170,8 +170,8 @@ String SafeReader::readStringLine() {
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
if (isEOF()) throw EndOfFileException(this, len);
|
if (isEOF()) throw EndOfFileException(this, len);
|
||||||
|
|
||||||
while (!isEOF() && (c = readC()) != 0) {
|
while (!isEOF() && (c=readC())!=0) {
|
||||||
if (c=='\r'||c=='\n') {
|
if (c=='\r' || c=='\n') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ret.push_back(c);
|
ret.push_back(c);
|
||||||
|
@ -179,17 +179,17 @@ String SafeReader::readStringLine() {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
String SafeReader::readStringToken(unsigned char delim) {
|
String SafeReader::readStringToken(unsigned char delim, bool stripContiguous) {
|
||||||
String ret;
|
String ret;
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
if (isEOF()) throw EndOfFileException(this, len);
|
if (isEOF()) throw EndOfFileException(this, len);
|
||||||
|
|
||||||
while (!isEOF() && (c=readC())!=0) {
|
while (!isEOF() && (c=readC())!=0) {
|
||||||
if (c == '\r' || c == '\n') {
|
if (c=='\r' || c=='\n') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (c == delim) {
|
if (c==delim) {
|
||||||
if (ret.length() == 0) {
|
if (ret.length()==0 && stripContiguous) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -200,5 +200,6 @@ String SafeReader::readStringToken(unsigned char delim) {
|
||||||
}
|
}
|
||||||
|
|
||||||
String SafeReader::readStringToken() {
|
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();
|
||||||
String readString(size_t len);
|
String readString(size_t len);
|
||||||
String readStringLine();
|
String readStringLine();
|
||||||
String readStringToken(unsigned char delim);
|
String readStringToken(unsigned char delim, bool stripContiguous);
|
||||||
String readStringToken();
|
String readStringToken();
|
||||||
inline bool isEOF() { return curSeek >= len; };
|
inline bool isEOF() { return curSeek >= len; };
|
||||||
|
|
||||||
|
|
|
@ -443,6 +443,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
w->writeC(0x04);
|
w->writeC(0x04);
|
||||||
w->writeC(0x00);
|
w->writeC(0x00);
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1395,16 +1402,17 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
||||||
size_t memPos=0;
|
size_t memPos=0;
|
||||||
for (int i=0; i<song.sampleLen; i++) {
|
for (int i=0; i<song.sampleLen; i++) {
|
||||||
DivSample* sample=song.sample[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;
|
memPos=(memPos+0xffff)&0xff0000;
|
||||||
}
|
}
|
||||||
|
logV("- sample %d will be at %x with length %x",i,memPos,alignedSize);
|
||||||
if (memPos>=16777216) break;
|
if (memPos>=16777216) break;
|
||||||
sample->offSegaPCM=memPos;
|
sample->offSegaPCM=memPos;
|
||||||
unsigned int alignedSize=(sample->length8+0xff)&(~0xff);
|
|
||||||
unsigned int readPos=0;
|
unsigned int readPos=0;
|
||||||
if (alignedSize>65536) alignedSize=65536;
|
|
||||||
for (unsigned int j=0; j<alignedSize; j++) {
|
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()) {
|
if (sample->isLoopable()) {
|
||||||
readPos=sample->loopStart;
|
readPos=sample->loopStart;
|
||||||
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
pcmMem[memPos++]=((unsigned char)sample->data8[readPos]+0x80);
|
||||||
|
|
|
@ -1221,9 +1221,9 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
if (!dirExists(workingDirSong)) workingDirSong=getHomeDir();
|
||||||
hasOpened=fileDialog->openLoad(
|
hasOpened=fileDialog->openLoad(
|
||||||
"Open File",
|
"Open File",
|
||||||
{"compatible files", "*.fur *.dmf *.mod *.ftm",
|
{"compatible files", "*.fur *.dmf *.mod",
|
||||||
"all files", ".*"},
|
"all files", ".*"},
|
||||||
"compatible files{.fur,.dmf,.mod,.ftm},.*",
|
"compatible files{.fur,.dmf,.mod},.*",
|
||||||
workingDirSong,
|
workingDirSong,
|
||||||
dpiScale
|
dpiScale
|
||||||
);
|
);
|
||||||
|
@ -1261,9 +1261,25 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
||||||
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
if (!dirExists(workingDirIns)) workingDirIns=getHomeDir();
|
||||||
hasOpened=fileDialog->openLoad(
|
hasOpened=fileDialog->openLoad(
|
||||||
"Load Instrument",
|
"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", ".*"},
|
"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,
|
workingDirIns,
|
||||||
dpiScale,
|
dpiScale,
|
||||||
[this](const char* path) {
|
[this](const char* path) {
|
||||||
|
@ -3476,10 +3492,34 @@ bool FurnaceGUI::loop() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GUI_WARN_CLEAR:
|
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();
|
stop();
|
||||||
e->lockEngine([this]() {
|
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);
|
e->setOrder(0);
|
||||||
curOrder=0;
|
curOrder=0;
|
||||||
|
@ -3550,7 +3590,7 @@ bool FurnaceGUI::loop() {
|
||||||
// backup trigger
|
// backup trigger
|
||||||
if (modified) {
|
if (modified) {
|
||||||
if (backupTimer>0) {
|
if (backupTimer>0) {
|
||||||
backupTimer-=ImGui::GetIO().DeltaTime;
|
backupTimer=(backupTimer-ImGui::GetIO().DeltaTime);
|
||||||
if (backupTimer<=0) {
|
if (backupTimer<=0) {
|
||||||
backupTask=std::async(std::launch::async,[this]() -> bool {
|
backupTask=std::async(std::launch::async,[this]() -> bool {
|
||||||
if (backupPath==curFileName) {
|
if (backupPath==curFileName) {
|
||||||
|
|
|
@ -801,7 +801,7 @@ class FurnaceGUI {
|
||||||
double aboutScroll, aboutSin;
|
double aboutScroll, aboutSin;
|
||||||
float aboutHue;
|
float aboutHue;
|
||||||
|
|
||||||
double backupTimer;
|
std::atomic<double> backupTimer;
|
||||||
std::future<bool> backupTask;
|
std::future<bool> backupTask;
|
||||||
std::mutex backupLock;
|
std::mutex backupLock;
|
||||||
String backupPath;
|
String backupPath;
|
||||||
|
|
|
@ -2790,6 +2790,11 @@ void FurnaceGUI::drawInsEdit() {
|
||||||
}
|
}
|
||||||
ImGui::EndTable();
|
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::EndDisabled();
|
||||||
ImGui::EndTabItem();
|
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 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("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("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) {
|
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));
|
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 a new issue