diff --git a/CMakeLists.txt b/CMakeLists.txt index bbaf81eb1..fe6a12fe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -494,6 +494,7 @@ src/gui/doAction.cpp src/gui/editing.cpp src/gui/editControls.cpp src/gui/effectList.cpp +src/gui/findReplace.cpp src/gui/insEdit.cpp src/gui/log.cpp src/gui/mixer.cpp diff --git a/TODO.md b/TODO.md index 1b212d95b..5bd4e8925 100644 --- a/TODO.md +++ b/TODO.md @@ -1,19 +1,12 @@ # to-do for 0.6pre1 -- additional YM2612 features - - CSM -- MSM6258 pitch and clock select -- the last compat flags - - newSegaPCM - - newVolumeScaling -- collapse/expand pattern and song -- Game Boy envelope macro/sequence - rewrite the system name detection function anyway -- volume commands should work on Game Boy - add another FM editor layout -- if macros have release, note off should release them - add ability to move selection by dragging -- Apply button in settings - find and replace -- add mono/poly note preview button -- (maybe) add default patch selection +- implement Defle slide bug when using E1xy/E2xy and repeating origin note (requires format change) + +# to-do for 0.6pre2 (as this requires new data structures) + +- Game Boy envelope macro/sequence +- volume commands should work on Game Boy diff --git a/demos/beeper_torture.fur b/demos/beeper_torture.fur new file mode 100644 index 000000000..72886c766 Binary files /dev/null and b/demos/beeper_torture.fur differ diff --git a/papers/doc/7-systems/n163.md b/papers/doc/7-systems/n163.md index 76b750cb3..6057fa3c9 100644 --- a/papers/doc/7-systems/n163.md +++ b/papers/doc/7-systems/n163.md @@ -1,12 +1,12 @@ # Namco C163 -This is Namco's one of NES mapper, with up to 8 wavetable channels. It has also 128 byte of internal RAM, both channel register and wavetables are stored here. Wavetables are variable size and freely allocable anywhere in RAM, it means it can be uses part of or continuously pre-loaded waveform and/or its sequences in RAM. But waveform RAM area becomes smaller as much as activating more channels; Channel register consumes 8 byte for each channels. You must avoid conflict with channel register area and waveform for avoid channel playback broken. +This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 128 byte of internal RAM, and both channel register and wavetables are stored here. Wavetables are variable size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. But waveform RAM area becomes smaller as more channels are activated; as channel registers consumes 8 bytes for each channel. You must avoid conflict with channel register area and waveform for avoid broken channel playback. -It has can be outputs only single channel at clock; so it's sound quality is more crunchy as much as activating more channels. +It outputs only a single channel at clock; so its sound quality gets more crunchy as more channels are activated. -Furnace supports both load waveform into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands. -You must load waveform to RAM first for playback or do something, its load behavior is changeable to auto-update when every waveform changes or manual update. -Both waveform playback and load command is works independently per each channel columns, (Global) commands are don't care about the channel columns for work commands and its load behavior is independent with per-channel column load commands. +Furnace supports loading waveforms into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands. +You must load waveform to RAM first for playback, as its load behavior auto-updates when every waveform changes. +Both waveform playback and load command works independently per each channel columns, (Global) commands don't care about the channel columns for work commands and its load behavior is independent with per-channel column load commands. # effects diff --git a/papers/doc/7-systems/pcspkr.md b/papers/doc/7-systems/pcspkr.md index 6c2e1a94c..50940a5a7 100644 --- a/papers/doc/7-systems/pcspkr.md +++ b/papers/doc/7-systems/pcspkr.md @@ -2,6 +2,30 @@ 40 years of one square beep - and still going! Single channel, no volume control... +# real output + +so far this is the only system in Furnace which has a real hardware output option. +to enable it, select file > configure system... > PC Speaker > Use system beeper. + +be noted that this will only work on Linux as Windows does not provide any user-space APIs to address the PC speaker directly! + +you may configure the output method by going in Settings > Emulation > PC Speaker strategy: + +- `evdev SND_TONE`: uses input events to control the beeper. + - requires write permission to `/dev/input/by-path/platform-pcspkr-event-spkr`. + - is not 100% frequency-accurate as `SND_TONE` demands frequencies, but Furnace uses raw timer periods... +- `KIOCSOUND on /dev/tty1`: sends the `KIOCSOUND` ioctl to control the beeper. + - may require running Furnace as root. +- `/dev/port`: writes to `/dev/port` to control the beeper. + - requires read/write permission to `/dev/port`. +- `KIOCSOUND on standard output`: sends the `KIOCSOUND` ioctl to control the beeper. + - requires running Furnace on a TTY. +- `outb()`: uses the low-level kernel port API to control the beeper. + - requires running Furnace as root, or granting it `CAP_SYS_RAWIO` to the Furnace executable: `sudo setcap cap_sys_rawio=ep ./furnace`. + +real hardware output only works on BIOS/UEFI (non-Mac) x86-based machines! attempting to do this under any other device **will not work**, or may even brick the device (if using `/dev/port` or `outb()`)! +oh, and of course you also need the beeper to be present in your machine. some laptops connect the beeper output to the built-in speakers (or the audio output jack), and some other don't do this at all. + # effects ha! effects... diff --git a/src/audio/jack.cpp b/src/audio/jack.cpp index 33a464d51..6e7b7bf1f 100644 --- a/src/audio/jack.cpp +++ b/src/audio/jack.cpp @@ -19,6 +19,7 @@ #include #include "jack.h" +#include "../ta-log.h" int taJACKonSampleRate(jack_nframes_t rate, void* inst) { TAAudioJACK* in=(TAAudioJACK*)inst; @@ -74,7 +75,10 @@ bool TAAudioJACK::quit() { if (running) { running=false; - if (jack_deactivate(ac)) return false; + if (jack_deactivate(ac)) { + logE("could not deactivate!"); + return false; + } } for (int i=0; icalcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits) +// this is for volume scaling calculation. +#define VOL_SCALE_LINEAR_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(((x)*(y))/(range)):(CLAMP(((x)+(y))-(range),0,(range)))) +#define VOL_SCALE_LINEAR(x,y,range) (((x)*(y))/(range)) +#define VOL_SCALE_LOG(x,y,range) ((parent->song.newVolumeScaling)?(CLAMP(((x)+(y))-(range),0,(range))):(((x)*(y))/(range))) + // these are here for convenience. // it is encouraged to use these, since you get an exact value this way. // - NTSC colorburst: 3.58MHz diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index b20662057..76b2cd11a 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -193,6 +193,9 @@ bool DivEngine::isExporting() { #ifdef HAVE_SNDFILE void DivEngine::runExportThread() { + size_t fadeOutSamples=got.rate*exportFadeOut; + size_t curFadeOutSample=0; + bool isFadingOut=false; switch (exportMode) { case DIV_EXPORT_MODE_ONE: { SNDFILE* sf; @@ -220,15 +223,33 @@ void DivEngine::runExportThread() { logI("rendering to file..."); while (playing) { + size_t total=0; nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE); - for (int i=0; iEXPORT_BUFSIZE) { logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE); + totalProcessed=EXPORT_BUFSIZE; } - if (sf_writef_float(sf,outBuf[2],totalProcessed)!=(int)totalProcessed) { + for (int i=0; i<(int)totalProcessed; i++) { + total++; + if (isFadingOut) { + double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples)); + outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]))*mul; + outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]))*mul; + if (++curFadeOutSample>=fadeOutSamples) { + playing=false; + break; + } + } else { + outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i])); + outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i])); + if (lastLoopPos>-1 && i>=lastLoopPos && totalLoops>=exportLoopCount) { + logD("start fading out..."); + isFadingOut=true; + } + } + } + + if (sf_writef_float(sf,outBuf[2],total)!=(int)total) { logE("error: failed to write entire buffer!"); break; } @@ -286,7 +307,10 @@ void DivEngine::runExportThread() { float* outBuf[2]; outBuf[0]=new float[EXPORT_BUFSIZE]; outBuf[1]=new float[EXPORT_BUFSIZE]; - short* sysBuf=new short[EXPORT_BUFSIZE*2]; + short* sysBuf[32]; + for (int i=0; iisStereo()) { - sysBuf[j]=disCont[i].bbOut[0][j]; - } else { - sysBuf[j<<1]=disCont[i].bbOut[0][j]; - sysBuf[1+(j<<1)]=disCont[i].bbOut[1][j]; + if (totalProcessed>EXPORT_BUFSIZE) { + logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE); + totalProcessed=EXPORT_BUFSIZE; + } + for (int j=0; j<(int)totalProcessed; j++) { + total++; + if (isFadingOut) { + double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples)); + for (int i=0; iisStereo()) { + sysBuf[i][j]=(double)disCont[i].bbOut[0][j]*mul; + } else { + sysBuf[i][j<<1]=(double)disCont[i].bbOut[0][j]*mul; + sysBuf[i][1+(j<<1)]=(double)disCont[i].bbOut[1][j]*mul; + } + } + if (++curFadeOutSample>=fadeOutSamples) { + playing=false; + break; + } + } else { + for (int i=0; iisStereo()) { + sysBuf[i][j]=disCont[i].bbOut[0][j]; + } else { + sysBuf[i][j<<1]=disCont[i].bbOut[0][j]; + sysBuf[i][1+(j<<1)]=disCont[i].bbOut[1][j]; + } + } + if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) { + logD("start fading out..."); + isFadingOut=true; } } - if (totalProcessed>EXPORT_BUFSIZE) { - logE("error: total processed is bigger than export bufsize! (%d) %d>%d",i,totalProcessed,EXPORT_BUFSIZE); - } - if (sf_writef_short(sf[i],sysBuf,totalProcessed)!=(int)totalProcessed) { + } + for (int i=0; iEXPORT_BUFSIZE) { logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE); + totalProcessed=EXPORT_BUFSIZE; } - if (sf_writef_float(sf,outBuf[2],totalProcessed)!=(int)totalProcessed) { + for (int j=0; j<(int)totalProcessed; j++) { + total++; + if (isFadingOut) { + double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples)); + outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]))*mul; + outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]))*mul; + if (++curFadeOutSample>=fadeOutSamples) { + playing=false; + break; + } + } else { + outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j])); + outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j])); + if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) { + logD("start fading out..."); + isFadingOut=true; + } + } + } + if (sf_writef_float(sf,outBuf[2],total)!=(int)total) { logE("error: failed to write entire buffer!"); break; } @@ -448,13 +521,14 @@ void DivEngine::runExportThread() { } #endif -bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) { +bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime) { #ifndef HAVE_SNDFILE logE("Furnace was not compiled with libsndfile. cannot export!"); return false; #else exportPath=path; exportMode=mode; + exportFadeOut=fadeOutTime; if (exportMode!=DIV_EXPORT_MODE_ONE) { // remove extension String lowerCase=exportPath; @@ -471,7 +545,12 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) stop(); repeatPattern=false; setOrder(0); - remainingLoops=loops; + if (exportFadeOut<=0.01) { + remainingLoops=loops; + } else { + remainingLoops=-1; + } + exportLoopCount=loops; exportThread=new std::thread(_runExportThread,this); return true; #endif @@ -780,6 +859,8 @@ void DivEngine::changeSong(size_t songIndex) { curSubSongIndex=songIndex; curOrder=0; curRow=0; + prevOrder=0; + prevRow=0; } void DivEngine::swapChannelsP(int src, int dest) { @@ -827,12 +908,51 @@ bool DivEngine::removeSubSong(int index) { return true; } +void DivEngine::moveSubSongUp(size_t index) { + if (index<1 || index>=song.subsong.size()) return; + BUSY_BEGIN; + saveLock.lock(); + + if (index==curSubSongIndex) { + curSubSongIndex--; + } else if (index-1==curSubSongIndex) { + curSubSongIndex++; + } + + DivSubSong* prev=song.subsong[index-1]; + song.subsong[index-1]=song.subsong[index]; + song.subsong[index]=prev; + + saveLock.unlock(); + BUSY_END; +} + +void DivEngine::moveSubSongDown(size_t index) { + if (index>=song.subsong.size()-1) return; + BUSY_BEGIN; + saveLock.lock(); + + if (index==curSubSongIndex) { + curSubSongIndex++; + } else if (index+1==curSubSongIndex) { + curSubSongIndex--; + } + + DivSubSong* prev=song.subsong[index+1]; + song.subsong[index+1]=song.subsong[index]; + song.subsong[index]=prev; + + saveLock.unlock(); + BUSY_END; +} + void DivEngine::clearSubSongs() { BUSY_BEGIN; saveLock.lock(); song.clearSongData(); changeSong(0); curOrder=0; + prevOrder=0; saveLock.unlock(); BUSY_END; } @@ -1075,6 +1195,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { int goal=curOrder; curOrder=0; curRow=0; + prevOrder=0; + prevRow=0; stepPlay=0; int prevDrift; prevDrift=clockDrift; @@ -1088,6 +1210,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { totalTicks=0; totalSeconds=0; totalTicksR=0; + totalLoops=0; + lastLoopPos=-1; } speedAB=false; playing=true; @@ -1124,6 +1248,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) { if (!preserveDrift) { ticks=1; subticks=1; + prevOrder=curOrder; + prevRow=curRow; } skipping=false; cmdStream.clear(); @@ -1250,6 +1376,7 @@ unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int void DivEngine::play() { BUSY_BEGIN_SOFT; + curOrder=prevOrder; sPreview.sample=-1; sPreview.wave=-1; sPreview.pos=0; @@ -1545,11 +1672,11 @@ int DivEngine::getMaxVolumeChan(int ch) { } unsigned char DivEngine::getOrder() { - return curOrder; + return prevOrder; } int DivEngine::getRow() { - return curRow; + return prevRow; } size_t DivEngine::getCurrentSubSong() { @@ -2462,7 +2589,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) { // 2. find a free channel do { - if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel)) { + if ((!midiPoly) || (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel))) { chan[finalChan].midiNote=note; chan[finalChan].midiAge=midiAgeCounter++; pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true)); @@ -2518,10 +2645,15 @@ void DivEngine::autoNoteOffAll() { } } +void DivEngine::setAutoNotePoly(bool poly) { + midiPoly=poly; +} + void DivEngine::setOrder(unsigned char order) { BUSY_BEGIN_SOFT; curOrder=order; if (order>=curSubSong->ordersLen) curOrder=0; + prevOrder=curOrder; if (playing && !freelance) { playSub(false); } @@ -2715,6 +2847,8 @@ void DivEngine::quitDispatch() { tempoAccum=0; curRow=0; curOrder=0; + prevRow=0; + prevOrder=0; nextSpeed=3; changeOrd=-1; changePos=0; diff --git a/src/engine/engine.h b/src/engine/engine.h index 1ad8eb2e1..dc16584de 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -303,7 +303,7 @@ class DivEngine { bool systemsRegistered; bool hasLoadedSomething; int softLockCount; - int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed; + int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed; size_t curSubSongIndex; double divider; int cycles; @@ -318,6 +318,7 @@ class DivEngine { DivChannelState chan[DIV_MAX_CHANS]; DivAudioEngines audioEngine; DivAudioExportModes exportMode; + double exportFadeOut; std::map conf; std::queue pendingNotes; bool isMuted[DIV_MAX_CHANS]; @@ -352,6 +353,7 @@ class DivEngine { int reversePitchTable[4096]; int pitchTable[4096]; int midiBaseChan; + bool midiPoly; size_t midiAgeCounter; blip_buffer_t* samp_bb; @@ -462,7 +464,7 @@ class DivEngine { // dump to VGM. SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171); // export to an audio file - bool saveAudio(const char* path, int loops, DivAudioExportModes mode); + bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0); // wait for audio export to finish void waitAudioFile(); // stop audio file export @@ -725,6 +727,9 @@ class DivEngine { void autoNoteOn(int chan, int ins, int note, int vol=-1); void autoNoteOff(int chan, int note, int vol=-1); void autoNoteOffAll(); + + // set whether autoNoteIn is mono or poly + void setAutoNotePoly(bool poly); // go to order void setOrder(unsigned char order); @@ -825,6 +830,10 @@ class DivEngine { // remove subsong bool removeSubSong(int index); + // move subsong + void moveSubSongUp(size_t index); + void moveSubSongDown(size_t index); + // clear all subsong data void clearSubSongs(); @@ -927,7 +936,12 @@ class DivEngine { ticks(0), curRow(0), curOrder(0), + prevRow(0), + prevOrder(0), remainingLoops(-1), + totalLoops(0), + lastLoopPos(0), + exportLoopCount(0), nextSpeed(3), curSubSongIndex(0), divider(60), @@ -951,7 +965,9 @@ class DivEngine { haltOn(DIV_HALT_NONE), audioEngine(DIV_AUDIO_NULL), exportMode(DIV_EXPORT_MODE_ONE), + exportFadeOut(0.0), midiBaseChan(0), + midiPoly(true), midiAgeCounter(0), samp_bb(NULL), samp_bbInLen(0), diff --git a/src/engine/macroInt.cpp b/src/engine/macroInt.cpp index 6f853bffa..06f04682e 100644 --- a/src/engine/macroInt.cpp +++ b/src/engine/macroInt.cpp @@ -101,6 +101,7 @@ void DivMacroInt::init(DivInstrument* which) { macroListLen=0; subTick=1; + hasRelease=false; released=false; if (ins==NULL) return; @@ -236,7 +237,12 @@ void DivMacroInt::init(DivInstrument* which) { } for (size_t i=0; iprepare(*macroSource[i],e); + if (macroSource[i]!=NULL) { + macroList[i]->prepare(*macroSource[i],e); + hasRelease=(macroSource[i]->rel>=0 && macroSource[i]->rellen); + } else { + hasRelease=false; + } } } diff --git a/src/engine/macroInt.h b/src/engine/macroInt.h index d82cb2b21..839625558 100644 --- a/src/engine/macroInt.h +++ b/src/engine/macroInt.h @@ -96,6 +96,9 @@ class DivMacroInt { ksr() {} } op[4]; + // state + bool hasRelease; + /** * trigger macro release. */ @@ -149,7 +152,8 @@ class DivMacroInt { ex5(), ex6(), ex7(), - ex8() { + ex8(), + hasRelease(false) { memset(macroList,0,128*sizeof(void*)); memset(macroSource,0,128*sizeof(void*)); } diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index 03961b0a3..8037feafe 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -427,6 +427,10 @@ bool DivPlatformAmiga::keyOffAffectsArp(int ch) { return true; } +DivMacroInt* DivPlatformAmiga::getChanMacroInt(int ch) { + return &chan[ch].std; +} + void DivPlatformAmiga::notifyInsChange(int ins) { for (int i=0; i<4; i++) { if (chan[i].ins==ins) { diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index 539f7830c..e7372e63b 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -99,6 +99,7 @@ class DivPlatformAmiga: public DivDispatch { void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); + DivMacroInt* getChanMacroInt(int ch); void setFlags(unsigned int flags); void notifyInsChange(int ins); void notifyWaveChange(int wave); diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index f6fa7d70a..1be61fa23 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -252,12 +252,12 @@ void DivPlatformArcade::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -346,7 +346,7 @@ void DivPlatformArcade::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -400,7 +400,7 @@ void DivPlatformArcade::tick(bool sysTick) { if (m.tl.had) { op.tl=127-m.tl.val; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -496,7 +496,7 @@ int DivPlatformArcade::dispatch(DivCommand c) { DivInstrumentFM::Operator op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -553,7 +553,7 @@ int DivPlatformArcade::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -644,7 +644,7 @@ int DivPlatformArcade::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -844,7 +844,7 @@ void DivPlatformArcade::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -881,6 +881,10 @@ void* DivPlatformArcade::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformArcade::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformArcade::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/arcade.h b/src/engine/platform/arcade.h index 9d0e39685..189ce1e9b 100644 --- a/src/engine/platform/arcade.h +++ b/src/engine/platform/arcade.h @@ -113,6 +113,7 @@ class DivPlatformArcade: public DivDispatch, public DivPlatformOPMBase { void forceIns(); void tick(bool sysTick=true); void muteChannel(int ch, bool mute); + DivMacroInt* getChanMacroInt(int ch); void notifyInsChange(int ins); void setFlags(unsigned int flags); bool isStereo(); diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 2f2b20c57..83de1fa13 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -509,6 +509,10 @@ void* DivPlatformAY8910::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformAY8910::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 8ff5f39c5..a1f35d1b6 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -105,6 +105,7 @@ class DivPlatformAY8910: public DivDispatch { void setFlags(unsigned int flags); bool isStereo(); bool keyOffAffectsArp(int ch); + DivMacroInt* getChanMacroInt(int ch); bool getDCOffRequired(); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index fb498b2c7..a92f185ca 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -539,6 +539,10 @@ void* DivPlatformAY8930::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformAY8930::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 67420f48c..10ac7736b 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -98,6 +98,7 @@ class DivPlatformAY8930: public DivDispatch { void setFlags(unsigned int flags); bool isStereo(); bool keyOffAffectsArp(int ch); + DivMacroInt* getChanMacroInt(int ch); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); diff --git a/src/engine/platform/bubsyswsg.cpp b/src/engine/platform/bubsyswsg.cpp index 5793277dd..85d50e668 100644 --- a/src/engine/platform/bubsyswsg.cpp +++ b/src/engine/platform/bubsyswsg.cpp @@ -281,6 +281,10 @@ void* DivPlatformBubSysWSG::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformBubSysWSG::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformBubSysWSG::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/bubsyswsg.h b/src/engine/platform/bubsyswsg.h index cb30d85c0..e288493c1 100644 --- a/src/engine/platform/bubsyswsg.h +++ b/src/engine/platform/bubsyswsg.h @@ -78,6 +78,7 @@ class DivPlatformBubSysWSG: public DivDispatch { void muteChannel(int ch, bool mute); bool isStereo(); bool keyOffAffectsArp(int ch); + DivMacroInt* getChanMacroInt(int ch); void setFlags(unsigned int flags); void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 726928871..3372c8203 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -491,6 +491,10 @@ void* DivPlatformC64::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformC64::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformC64::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 495da461d..d9dc08040 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -97,6 +97,7 @@ class DivPlatformC64: public DivDispatch { void setFlags(unsigned int flags); void notifyInsChange(int ins); bool getDCOffRequired(); + DivMacroInt* getChanMacroInt(int ch); void notifyInsDeletion(void* ins); void poke(unsigned int addr, unsigned short val); void poke(std::vector& wlist); diff --git a/src/engine/platform/fds.cpp b/src/engine/platform/fds.cpp index eba371834..ea4b83e9d 100644 --- a/src/engine/platform/fds.cpp +++ b/src/engine/platform/fds.cpp @@ -139,7 +139,7 @@ void DivPlatformFDS::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { // ok, why are the volumes like that? - chan[i].outVol=MIN(32,chan[i].std.vol.val)-(32-MIN(32,chan[i].vol)); + chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol,chan[i].std.vol.val,32); if (chan[i].outVol<0) chan[i].outVol=0; rWrite(0x4080,0x80|chan[i].outVol); } @@ -436,6 +436,10 @@ void* DivPlatformFDS::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformFDS::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformFDS::getOscBuffer(int ch) { return oscBuf; } diff --git a/src/engine/platform/fds.h b/src/engine/platform/fds.h index c563a47f8..f655a7777 100644 --- a/src/engine/platform/fds.h +++ b/src/engine/platform/fds.h @@ -88,6 +88,7 @@ class DivPlatformFDS: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/gb.cpp b/src/engine/platform/gb.cpp index 40bf6a835..999d31b91 100644 --- a/src/engine/platform/gb.cpp +++ b/src/engine/platform/gb.cpp @@ -433,6 +433,10 @@ void* DivPlatformGB::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformGB::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformGB::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/gb.h b/src/engine/platform/gb.h index cce1ffb6c..fe2f6e51b 100644 --- a/src/engine/platform/gb.h +++ b/src/engine/platform/gb.h @@ -72,6 +72,7 @@ class DivPlatformGB: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index a832bab4a..512f55f81 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -26,6 +26,7 @@ static unsigned char konOffs[6]={ 0, 1, 2, 4, 5, 6 }; +#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6]))) const char* DivPlatformGenesis::getEffectName(unsigned char effect) { switch (effect) { @@ -141,10 +142,13 @@ void DivPlatformGenesis::processDAC() { DivSample* s=parent->getSample(chan[i].dacSample); if (!isMuted[i] && s->samples>0) { if (parent->song.noOPN2Vol) { - sample+=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]; + chan[i].dacOutput=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]; } else { - sample+=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7; + chan[i].dacOutput=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7; } + sample+=chan[i].dacOutput; + } else { + chan[i].dacOutput=0; } chan[i].dacPeriod+=chan[i].dacRate; if (chan[i].dacPeriod>=(chipClock/576)) { @@ -246,7 +250,20 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1]; //OPN2_Write(&fm,0,0); - oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7; + if (i==5) { + if (fm.dacen) { + if (softPCM) { + oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7; + oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7; + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7; + } + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7; + } + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7; + } } os[0]=(os[0]<<5); @@ -290,7 +307,20 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si //OPN2_Write(&fm,0,0); for (int i=0; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + if (i==5) { + if (fm_ymfm->debug_dac_enable()) { + if (softPCM) { + oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7; + oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7; + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7; + } + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + } + } else { + oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6; + } } if (os[0]<-32768) os[0]=-32768; @@ -318,7 +348,7 @@ void DivPlatformGenesis::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; @@ -326,7 +356,7 @@ void DivPlatformGenesis::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -352,7 +382,7 @@ void DivPlatformGenesis::tick(bool sysTick) { if (chan[i].std.panL.had) { chan[i].pan=chan[i].std.panL.val&3; - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } if (chan[i].std.pitch.had) { @@ -381,7 +411,7 @@ void DivPlatformGenesis::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -394,11 +424,11 @@ void DivPlatformGenesis::tick(bool sysTick) { } if (chan[i].std.fms.had) { chan[i].state.fms=chan[i].std.fms.val; - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } if (chan[i].std.ams.had) { chan[i].state.ams=chan[i].std.ams.val; - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); } for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; @@ -434,7 +464,7 @@ void DivPlatformGenesis::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -492,8 +522,7 @@ void DivPlatformGenesis::tick(bool sysTick) { } } - - for (int i=0; i<8; i++) { + for (int i=0; i<7; i++) { if (i==2 && extMode) continue; if (chan[i].freqChanged) { if (parent->song.linearPitch==2) { @@ -542,27 +571,45 @@ void DivPlatformGenesis::tick(bool sysTick) { void DivPlatformGenesis::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (ch>5) return; - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[ch]|opOffs[j]; - DivInstrumentFM::Operator& op=chan[ch].state.op[j]; - if (isMuted[ch]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (isOutput[chan[ch].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); + if (ch>6) return; + if (ch<6) { + for (int j=0; j<4; j++) { + unsigned short baseAddr=chanOffs[ch]|opOffs[j]; + DivInstrumentFM::Operator& op=chan[ch].state.op[j]; + if (isMuted[ch]) { + rWrite(baseAddr+ADDR_TL,127); } else { - rWrite(baseAddr+ADDR_TL,op.tl); + if (isOutput[chan[ch].state.alg][j]) { + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127)); + } else { + rWrite(baseAddr+ADDR_TL,op.tl); + } } } + } else { + ch--; } - rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); + rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4)); } int DivPlatformGenesis::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM); + if (c.chan==7 && extMode && softPCM) { // CSM + chan[c.chan].macroInit(ins); + chan[c.chan].insChanged=false; + + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].portaPause=false; + chan[c.chan].note=c.value; + chan[c.chan].freqChanged=true; + } + chan[c.chan].keyOn=true; + chan[c.chan].active=true; + break; + } if (c.chan>=5) { if (ins->type==DIV_INS_AMIGA) { chan[c.chan].dacMode=1; @@ -631,7 +678,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } else { if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -650,7 +697,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } if (chan[c.chan].insChanged) { rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3)); - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); + rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); } chan[c.chan].insChanged=false; @@ -665,7 +712,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_OFF: - if (c.chan>=5) { + if (c.chan>=5 && c.chan<7) { chan[c.chan].dacSample=-1; if (dumpWrites) addWrite(0xffff0002,0); if (parent->song.brokenDACMode) { @@ -703,7 +750,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -728,7 +775,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } else { chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); } - rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); + rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4)); break; } case DIV_CMD_PITCH: { @@ -760,6 +807,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; } + if (c.chan==7) { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { int destFreq=parent->calcBaseFreq(1,1,c.value2,false); bool return2=false; @@ -806,7 +876,9 @@ int DivPlatformGenesis::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: { - if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { + if (c.chan==7) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + } else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) { chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); } else { chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11); @@ -844,7 +916,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1035,7 +1107,7 @@ void DivPlatformGenesis::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1048,7 +1120,7 @@ void DivPlatformGenesis::forceIns() { rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3)); - rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); + rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4)); if (chan[i].active) { chan[i].keyOn=true; chan[i].freqChanged=true; @@ -1068,6 +1140,10 @@ void* DivPlatformGenesis::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformGenesis::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index 71493fa5e..70b3dc8a3 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -55,6 +55,7 @@ class DivPlatformGenesis: public DivDispatch, public DivPlatformOPNBase { bool dacReady; bool dacDirection; unsigned char sampleBank; + signed char dacOutput; void macroInit(DivInstrument* which) { std.init(which); pitch2=0; @@ -89,7 +90,8 @@ class DivPlatformGenesis: public DivDispatch, public DivPlatformOPNBase { dacDelay(0), dacReady(true), dacDirection(false), - sampleBank(0) {} + sampleBank(0), + dacOutput(0) {} }; Channel chan[10]; DivDispatchOscBuffer* oscBuf[10]; @@ -130,6 +132,7 @@ class DivPlatformGenesis: public DivDispatch, public DivPlatformOPNBase { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index 76837f649..057049eaa 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -50,7 +50,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -88,7 +88,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } break; } @@ -194,7 +194,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else if (isOutput[chan[2].state.alg][c.value]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -377,8 +377,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) { rWrite(baseAddr+0x40,127); immWrite(baseAddr+0x40,127); } else if (isOutput[chan[2].state.alg][ordch]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127)); - immWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); + immWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); immWrite(baseAddr+0x40,op.tl); @@ -442,9 +442,43 @@ void DivPlatformGenesisExt::tick(bool sysTick) { opChan[i].keyOn=false; } } + + if (extMode && softPCM) { + if (chan[7].freqChanged) { + chan[7].freq=parent->calcFreq(chan[7].baseFreq,chan[7].pitch,true,0,chan[7].pitch2,chipClock,CHIP_DIVIDER); + if (chan[7].freq<1) chan[7].freq=1; + if (chan[7].freq>1024) chan[7].freq=1024; + int wf=0x400-chan[7].freq; + immWrite(0x24,wf>>2); + immWrite(0x25,wf&3); + chan[7].freqChanged=false; + } + + if (chan[7].keyOff || chan[7].keyOn) { + writeNoteOn=true; + for (int i=0; i<4; i++) { + writeMask|=opChan[i].active<<(4+i); + } + } + } + if (writeNoteOn) { + if (chan[7].active) { // CSM + writeMask^=0xf0; + } immWrite(0x28,writeMask); } + + if (extMode && softPCM) { + if (chan[7].keyOn) { + immWrite(0x27,0x81); + chan[7].keyOn=false; + } + if (chan[7].keyOff) { + immWrite(0x27,0x40); + chan[7].keyOff=false; + } + } } void DivPlatformGenesisExt::forceIns() { @@ -456,7 +490,7 @@ void DivPlatformGenesisExt::forceIns() { if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); } else if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -465,7 +499,7 @@ void DivPlatformGenesisExt::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -504,6 +538,12 @@ void* DivPlatformGenesisExt::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) { + if (ch>=6) return &chan[ch-3].std; + if (ch>=2) return NULL; // currently not implemented + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) { if (ch>=6) return oscBuf[ch-3]; if (ch<3) return oscBuf[ch]; diff --git a/src/engine/platform/genesisext.h b/src/engine/platform/genesisext.h index f6c1e3dc9..07e0d5cc3 100644 --- a/src/engine/platform/genesisext.h +++ b/src/engine/platform/genesisext.h @@ -55,6 +55,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/lynx.cpp b/src/engine/platform/lynx.cpp index dce2fb044..4fd30db98 100644 --- a/src/engine/platform/lynx.cpp +++ b/src/engine/platform/lynx.cpp @@ -424,6 +424,10 @@ void* DivPlatformLynx::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformLynx::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformLynx::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/lynx.h b/src/engine/platform/lynx.h index 4f221d037..849c3182b 100644 --- a/src/engine/platform/lynx.h +++ b/src/engine/platform/lynx.h @@ -88,6 +88,7 @@ class DivPlatformLynx: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/mmc5.cpp b/src/engine/platform/mmc5.cpp index 920b9a061..2b97f46f6 100644 --- a/src/engine/platform/mmc5.cpp +++ b/src/engine/platform/mmc5.cpp @@ -107,7 +107,7 @@ void DivPlatformMMC5::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { // ok, why are the volumes like that? - chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); + chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); if (chan[i].outVol<0) chan[i].outVol=0; rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6)); } @@ -353,6 +353,10 @@ void* DivPlatformMMC5::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformMMC5::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformMMC5::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/mmc5.h b/src/engine/platform/mmc5.h index 2213b995f..f09092d55 100644 --- a/src/engine/platform/mmc5.h +++ b/src/engine/platform/mmc5.h @@ -74,6 +74,7 @@ class DivPlatformMMC5: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/msm6258.cpp b/src/engine/platform/msm6258.cpp index 826d221b2..db1848cfa 100644 --- a/src/engine/platform/msm6258.cpp +++ b/src/engine/platform/msm6258.cpp @@ -31,50 +31,73 @@ const char** DivPlatformMSM6258::getRegisterSheet() { } const char* DivPlatformMSM6258::getEffectName(unsigned char effect) { + switch (effect) { + case 0x20: + return "20xx: Set frequency divider (0-2)"; + break; + case 0x21: + return "21xx: Select clock rate (0: full; 1: half)"; + break; + } return NULL; } void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) { + short* outs[2]={ + &msmOut, + NULL + }; for (size_t h=start; hctrl_w(w.val); - break; - } - writes.pop(); - } - - if (sample>=0 && samplesong.sampleLen) { - DivSample* s=parent->getSample(sample); - unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4); - if (msm->data_w(nextData)) { - samplePos++; - if (samplePos>=(int)s->lengthVOX) { - sample=-1; - samplePos=0; - msm->ctrl_w(1); + if (--msmClockCount<0) { + if (--msmDividerCount<=0) { + if (!writes.empty()) { + QueuedWrite& w=writes.front(); + switch (w.addr) { + case 0: + msm->ctrl_w(w.val); + break; + case 2: + msmPan=w.val; + break; + case 8: + msmClock=w.val; + break; + case 12: + msmDivider=4-(w.val&3); + if (msmDivider<2) msmDivider=2; + break; + } + writes.pop(); } + + if (sample>=0 && samplesong.sampleLen) { + DivSample* s=parent->getSample(sample); + unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4); + if (msm->data_w(nextData)) { + samplePos++; + if (samplePos>=(int)s->lengthVOX) { + sample=-1; + samplePos=0; + msm->ctrl_w(1); + } + } + } + + msm->sound_stream_update(outs,1); + msmDividerCount=msmDivider; } + msmClockCount=msmClock; } - msm->sound_stream_update(outs,1); if (isMuted[0]) { bufL[h]=0; + bufR[h]=0; + oscBuf[0]->data[oscBuf[0]->needle++]=0; + } else { + bufL[h]=(msmPan&2)?msmOut:0; + bufR[h]=(msmPan&1)?msmOut:0; + oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?msmOut:0; } - - /*if (++updateOsc>=22) { - updateOsc=0; - // TODO: per-channel osc - for (int i=0; i<1; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6); - } - }*/ } } @@ -176,6 +199,23 @@ int DivPlatformMSM6258::dispatch(DivCommand c) { sampleBank=parent->song.sample.size()/12; } break; + case DIV_CMD_SAMPLE_FREQ: + rateSel=c.value&3; + rWrite(12,rateSel); + break; + case DIV_CMD_SAMPLE_MODE: + clockSel=c.value&1; + rWrite(8,clockSel); + break; + case DIV_CMD_PANNING: { + if (c.value==0 && c.value2==0) { + chan[c.chan].pan=3; + } else { + chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1); + } + rWrite(2,chan[c.chan].pan); + break; + } case DIV_CMD_LEGATO: { break; } @@ -205,12 +245,19 @@ void DivPlatformMSM6258::forceIns() { for (int i=0; i<1; i++) { chan[i].insChanged=true; } + rWrite(12,rateSel); + rWrite(8,clockSel); + rWrite(2,chan[0].pan); } void* DivPlatformMSM6258::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformMSM6258::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) { return oscBuf[ch]; } @@ -234,6 +281,14 @@ void DivPlatformMSM6258::poke(std::vector& wlist) { void DivPlatformMSM6258::reset() { while (!writes.empty()) writes.pop(); msm->device_reset(); + msmClock=chipClock; + msmDivider=2; + msmDividerCount=0; + msmClock=0; + msmClockCount=0; + msmPan=3; + rateSel=2; + clockSel=0; if (dumpWrites) { addWrite(0xffffffff,0); } @@ -253,6 +308,10 @@ void DivPlatformMSM6258::reset() { delay=0; } +bool DivPlatformMSM6258::isStereo() { + return true; +} + bool DivPlatformMSM6258::keyOffAffectsArp(int ch) { return false; } @@ -307,15 +366,23 @@ void DivPlatformMSM6258::renderSamples() { } void DivPlatformMSM6258::setFlags(unsigned int flags) { - if (flags&1) { - chipClock=4096000; - } else { - chipClock=4000000; + switch (flags) { + case 3: + chipClock=8192000; + break; + case 2: + chipClock=8000000; + break; + case 1: + chipClock=4096000; + break; + default: + chipClock=4000000; + break; } - rate=chipClock/256; + rate=chipClock/128; for (int i=0; i<1; i++) { - isMuted[i]=false; - oscBuf[i]->rate=rate/256; + oscBuf[i]->rate=rate; } } diff --git a/src/engine/platform/msm6258.h b/src/engine/platform/msm6258.h index 3a05bcf82..cd975c8f8 100644 --- a/src/engine/platform/msm6258.h +++ b/src/engine/platform/msm6258.h @@ -81,7 +81,9 @@ class DivPlatformMSM6258: public DivDispatch { unsigned char* adpcmMem; size_t adpcmMemLen; - unsigned char sampleBank; + unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel; + signed char msmDividerCount, msmClockCount; + short msmOut; int delay, updateOsc, sample, samplePos; @@ -91,6 +93,7 @@ class DivPlatformMSM6258: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); @@ -98,6 +101,7 @@ class DivPlatformMSM6258: public DivDispatch { void forceIns(); void tick(bool sysTick=true); void muteChannel(int ch, bool mute); + bool isStereo(); bool keyOffAffectsArp(int ch); void notifyInsChange(int ins); void notifyInsDeletion(void* ins); diff --git a/src/engine/platform/msm6295.cpp b/src/engine/platform/msm6295.cpp index 0d88d35dd..fc5f9ea30 100644 --- a/src/engine/platform/msm6295.cpp +++ b/src/engine/platform/msm6295.cpp @@ -230,6 +230,10 @@ void* DivPlatformMSM6295::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformMSM6295::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformMSM6295::getOscBuffer(int ch) { return oscBuf[ch]; } @@ -394,7 +398,6 @@ void DivPlatformMSM6295::setFlags(unsigned int flags) { } rate=chipClock/3; for (int i=0; i<4; i++) { - isMuted[i]=false; oscBuf[i]->rate=rate/22; } if (rateSel!=rateSelInit) { diff --git a/src/engine/platform/msm6295.h b/src/engine/platform/msm6295.h index 463384f14..f01c1b233 100644 --- a/src/engine/platform/msm6295.h +++ b/src/engine/platform/msm6295.h @@ -81,6 +81,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { virtual void acquire(short* bufL, short* bufR, size_t start, size_t len) override; virtual int dispatch(DivCommand c) override; virtual void* getChanState(int chan) override; + virtual DivMacroInt* getChanMacroInt(int ch) override; virtual DivDispatchOscBuffer* getOscBuffer(int chan) override; virtual unsigned char* getRegisterPool() override; virtual int getRegisterPoolSize() override; @@ -101,7 +102,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf { virtual size_t getSampleMemCapacity(int index) override; virtual size_t getSampleMemUsage(int index) override; virtual void renderSamples() override; - + virtual int init(DivEngine* parent, int channels, int sugRate, unsigned int flags) override; virtual void quit() override; DivPlatformMSM6295(): diff --git a/src/engine/platform/n163.cpp b/src/engine/platform/n163.cpp index 58a416936..50f9d8363 100644 --- a/src/engine/platform/n163.cpp +++ b/src/engine/platform/n163.cpp @@ -625,6 +625,10 @@ void* DivPlatformN163::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformN163::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformN163::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/n163.h b/src/engine/platform/n163.h index 6ecb1e0c5..5e44d3dd8 100644 --- a/src/engine/platform/n163.h +++ b/src/engine/platform/n163.h @@ -95,6 +95,7 @@ class DivPlatformN163: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/namcowsg.cpp b/src/engine/platform/namcowsg.cpp index 88cba3606..1e8ff46d1 100644 --- a/src/engine/platform/namcowsg.cpp +++ b/src/engine/platform/namcowsg.cpp @@ -467,6 +467,10 @@ void* DivPlatformNamcoWSG::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformNamcoWSG::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformNamcoWSG::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/namcowsg.h b/src/engine/platform/namcowsg.h index 652181e01..4ab81bdce 100644 --- a/src/engine/platform/namcowsg.h +++ b/src/engine/platform/namcowsg.h @@ -79,6 +79,7 @@ class DivPlatformNamcoWSG: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index dfc9108bc..50e548ea3 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -230,7 +230,7 @@ void DivPlatformNES::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { // ok, why are the volumes like that? - chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); + chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); if (chan[i].outVol<0) chan[i].outVol=0; if (i==2) { // triangle rWrite(0x4000+i*4,(chan[i].outVol==0)?0:255); @@ -610,6 +610,10 @@ void* DivPlatformNES::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformNES::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformNES::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index a03efc7a3..35c51df74 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -89,6 +89,7 @@ class DivPlatformNES: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/opl.cpp b/src/engine/platform/opl.cpp index a40f41143..43ce2f5f3 100644 --- a/src/engine/platform/opl.cpp +++ b/src/engine/platform/opl.cpp @@ -347,7 +347,7 @@ void DivPlatformOPL::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(63,chan[i].std.vol.val))/63; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(63,chan[i].std.vol.val),63); for (int j=0; jmelodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -485,7 +485,7 @@ void DivPlatformOPL::tick(bool sysTick) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -694,7 +694,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -825,7 +825,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { if (isMuted[ch]) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6)); } rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult); @@ -860,7 +860,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -976,7 +976,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -1125,7 +1125,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -1355,7 +1355,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -1372,7 +1372,7 @@ int DivPlatformOPL::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -1446,7 +1446,7 @@ void DivPlatformOPL::forceIns() { rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6)); } else { if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) { - rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6)); + rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6)); } else { rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6)); } @@ -1488,6 +1488,10 @@ void* DivPlatformOPL::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) { if (ch>=18) return NULL; return oscBuf[ch]; diff --git a/src/engine/platform/opl.h b/src/engine/platform/opl.h index 9d2d17742..5e8294c37 100644 --- a/src/engine/platform/opl.h +++ b/src/engine/platform/opl.h @@ -125,6 +125,7 @@ class DivPlatformOPL: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/opll.cpp b/src/engine/platform/opll.cpp index c232e68e3..6d77ca1f7 100644 --- a/src/engine/platform/opll.cpp +++ b/src/engine/platform/opll.cpp @@ -149,9 +149,9 @@ void DivPlatformOPLL::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(15,chan[i].std.vol.val))/15; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(15,chan[i].std.vol.val),15); if (i<9) { - rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4)); } } @@ -174,7 +174,7 @@ void DivPlatformOPLL::tick(bool sysTick) { if (chan[i].std.wave.had && chan[i].state.opllPreset!=16) { chan[i].state.opllPreset=chan[i].std.wave.val; if (i<9) { - rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4)); } } @@ -244,7 +244,7 @@ void DivPlatformOPLL::tick(bool sysTick) { op.tl=((j==1)?15:63)-m.tl.val; if (j==1) { if (i<9) { - rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4)); } } else { rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(op.tl&63)); @@ -384,21 +384,6 @@ int DivPlatformOPLL::toFreq(int freq) { void DivPlatformOPLL::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - /* - for (int j=0; j<4; j++) { - unsigned short baseAddr=chanOffs[ch]|opOffs[j]; - DivInstrumentFM::Operator& op=chan[ch].state.op[j]; - if (isMuted[ch]) { - rWrite(baseAddr+ADDR_TL,127); - } else { - if (isOutput[chan[ch].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); - } else { - rWrite(baseAddr+ADDR_TL,op.tl); - } - } - } - rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));*/ } int DivPlatformOPLL::dispatch(DivCommand c) { @@ -483,7 +468,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { } } if (c.chan<9) { - rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); + rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4)); } } } @@ -553,7 +538,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { break; } else if (c.chan<6 || !drums) { if (c.chan<9) { - rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); + rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4)); } } break; @@ -647,7 +632,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) { DivInstrumentFM::Operator& car=chan[c.chan].state.op[1]; car.tl=c.value2&15; if (c.chan<9) { - rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4)); + rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4)); } } break; @@ -862,7 +847,7 @@ void DivPlatformOPLL::forceIns() { rWrite(0x07,(car.sl<<4)|(car.rr)); } if (i<9) { - rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4)); + rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4)); } if (!(i>=6 && properDrums)) { if (chan[i].active) { @@ -911,6 +896,10 @@ void* DivPlatformOPLL::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformOPLL::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) { if (ch>=9) return NULL; return oscBuf[ch]; diff --git a/src/engine/platform/opll.h b/src/engine/platform/opll.h index d2d208269..dad660dea 100644 --- a/src/engine/platform/opll.h +++ b/src/engine/platform/opll.h @@ -102,6 +102,7 @@ class DivPlatformOPLL: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index be104fb91..b8c580e51 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -154,7 +154,7 @@ void DivPlatformPCE::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=((chan[i].vol&31)*MIN(31,chan[i].std.vol.val))>>5; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31); if (chan[i].furnaceDac && chan[i].pcm) { // ignore for now } else { @@ -477,6 +477,10 @@ void* DivPlatformPCE::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformPCE::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index 58fa6600a..870b5218a 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -89,6 +89,7 @@ class DivPlatformPCE: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/pcspkr.cpp b/src/engine/platform/pcspkr.cpp index e26ef64f1..d977690f5 100644 --- a/src/engine/platform/pcspkr.cpp +++ b/src/engine/platform/pcspkr.cpp @@ -19,6 +19,7 @@ #include "pcspkr.h" #include "../engine.h" +#include "../../ta-log.h" #include #ifdef __linux__ @@ -28,6 +29,8 @@ #include #include #include +#include +#include #endif #define PCSPKR_DIVIDER 4 @@ -38,6 +41,137 @@ const char* regCheatSheetPCSpeaker[]={ NULL }; +void _pcSpeakerThread(void* inst) { + ((DivPlatformPCSpeaker*)inst)->pcSpeakerThread(); +} + +void DivPlatformPCSpeaker::pcSpeakerThread() { + std::unique_lock unique(realOutSelfLock); + RealQueueVal r(0,0,0); + logD("starting PC speaker out thread"); + while (!realOutQuit) { + realQueueLock.lock(); + if (realQueue.empty()) { + realQueueLock.unlock(); + realOutCond.wait(unique); + continue; + } else { + r=realQueue.front(); + realQueue.pop(); + } + realQueueLock.unlock(); +#ifdef __linux__ + static struct timespec ts, tSleep, rSleep; + if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) { + logW("could not get time!"); + tSleep.tv_sec=0; + tSleep.tv_nsec=0; + } else { + tSleep.tv_sec=r.tv_sec-ts.tv_sec; + tSleep.tv_nsec=r.tv_nsec-ts.tv_nsec; + if (tSleep.tv_nsec<0) { + tSleep.tv_sec--; + tSleep.tv_nsec+=1000000000; + } + } + + if (tSleep.tv_nsec>0 || tSleep.tv_sec>0) { + nanosleep(&tSleep,&rSleep); + } + if (beepFD>=0) { + switch (realOutMethod) { + case 0: { // evdev + static struct input_event ie; + ie.time.tv_sec=r.tv_sec; + ie.time.tv_usec=r.tv_nsec/1000; + ie.type=EV_SND; + ie.code=SND_TONE; + if (r.val>0) { + ie.value=chipClock/r.val; + } else { + ie.value=0; + } + if (write(beepFD,&ie,sizeof(struct input_event))<0) { + logW("error while writing frequency! %s",strerror(errno)); + } else { + //logV("writing freq: %d",r.val); + } + break; + } + case 1: // KIOCSOUND (on tty) + if (ioctl(beepFD,KIOCSOUND,r.val)<0) { + logW("ioctl error! %s",strerror(errno)); + } + break; + case 2: { // /dev/port + unsigned char bOut; + bOut=0; + if (r.val==0) { + lseek(beepFD,0x61,SEEK_SET); + if (read(beepFD,&bOut,1)<1) { + logW("read from 0x61: %s",strerror(errno)); + } + bOut&=(~3); + lseek(beepFD,0x61,SEEK_SET); + if (write(beepFD,&bOut,1)<1) { + logW("write to 0x61: %s",strerror(errno)); + } + } else { + lseek(beepFD,0x43,SEEK_SET); + bOut=0xb6; + if (write(beepFD,&bOut,1)<1) { + logW("write to 0x43: %s",strerror(errno)); + } + lseek(beepFD,0x42,SEEK_SET); + bOut=r.val&0xff; + if (write(beepFD,&bOut,1)<1) { + logW("write to 0x42: %s",strerror(errno)); + } + lseek(beepFD,0x42,SEEK_SET); + bOut=r.val>>8; + if (write(beepFD,&bOut,1)<1) { + logW("write to 0x42: %s",strerror(errno)); + } + lseek(beepFD,0x61,SEEK_SET); + if (read(beepFD,&bOut,1)<1) { + logW("read from 0x61: %s",strerror(errno)); + } + bOut|=3; + lseek(beepFD,0x61,SEEK_SET); + if (write(beepFD,&bOut,1)<1) { + logW("write to 0x61: %s",strerror(errno)); + } + } + break; + } + case 3: // KIOCSOUND (on stdout) + if (ioctl(beepFD,KIOCSOUND,r.val)<0) { + logW("ioctl error! %s",strerror(errno)); + } + break; + case 4: // outb() + if (r.val==0) { + outb(inb(0x61)&(~3),0x61); + realOutEnabled=false; + } else { + outb(0xb6,0x43); + outb(r.val&0xff,0x42); + outb(r.val>>8,0x42); + if (!realOutEnabled) { + outb(inb(0x61)|3,0x61); + realOutEnabled=true; + } + } + break; + } + } else { + //logV("not writing because fd is less than 0"); + } +#endif + } + logD("stopping PC speaker out thread"); +} + const char** DivPlatformPCSpeaker::getRegisterSheet() { return regCheatSheetPCSpeaker; } @@ -126,25 +260,28 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start, } } -void DivPlatformPCSpeaker::beepFreq(int freq) { +void DivPlatformPCSpeaker::beepFreq(int freq, int delay) { + realQueueLock.lock(); #ifdef __linux__ - static struct input_event ie; - if (beepFD>=0) { - gettimeofday(&ie.time,NULL); - ie.type=EV_SND; - ie.code=SND_TONE; - if (freq>0) { - ie.value=chipClock/freq; - } else { - ie.value=0; - } - if (write(beepFD,&ie,sizeof(struct input_event))<0) { - perror("error while writing frequency!"); - } else { - //printf("writing freq: %d\n",freq); + struct timespec ts; + double addition=1000000000.0*(double)delay/(double)rate; + addition+=1500000000.0*((double)parent->getAudioDescGot().bufsize/parent->getAudioDescGot().rate); + if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) { + ts.tv_sec=0; + ts.tv_nsec=0; + } else { + ts.tv_nsec+=addition; + while (ts.tv_nsec>=1000000000) { + ts.tv_sec++; + ts.tv_nsec-=1000000000; } } + realQueue.push(RealQueueVal(ts.tv_sec,ts.tv_nsec,freq)); +#else + realQueue.push(RealQueueVal(0,0,freq)); #endif + realQueueLock.unlock(); + realOutCond.notify_one(); } void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start, size_t len) { @@ -152,7 +289,7 @@ void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start, if (lastOn!=on || lastFreq!=freq) { lastOn=on; lastFreq=freq; - beepFreq((on && !isMuted[0])?freq:0); + beepFreq((on && !isMuted[0])?freq:0,start); } for (size_t i=start; igetConfInt("pcSpeakerOutMethod",0); + realOutEnabled=false; for (int i=0; i<1; i++) { isMuted[i]=false; } @@ -450,8 +628,14 @@ void DivPlatformPCSpeaker::quit() { if (speakerType==3) { beepFreq(0); } + if (realOutThread!=NULL) { + realOutQuit=true; + realOutCond.notify_one(); + realOutThread->join(); + delete realOutThread; + } #ifdef __linux__ - if (beepFD>=0) close(beepFD); + if (beepFD>=0 && realOutMethod<3) close(beepFD); #endif delete oscBuf; } diff --git a/src/engine/platform/pcspkr.h b/src/engine/platform/pcspkr.h index 155416bb8..cb6f070fe 100644 --- a/src/engine/platform/pcspkr.h +++ b/src/engine/platform/pcspkr.h @@ -22,6 +22,10 @@ #include "../dispatch.h" #include "../macroInt.h" +#include +#include +#include +#include class DivPlatformPCSpeaker: public DivDispatch { struct Channel { @@ -57,9 +61,23 @@ class DivPlatformPCSpeaker: public DivDispatch { }; Channel chan[1]; DivDispatchOscBuffer* oscBuf; + std::thread* realOutThread; + std::mutex realOutSelfLock; + std::condition_variable realOutCond; + bool realOutQuit; + struct RealQueueVal { + int tv_sec, tv_nsec; + unsigned short val; + RealQueueVal(int sec, int nsec, unsigned short v): + tv_sec(sec), + tv_nsec(nsec), + val(v) {} + }; + std::queue realQueue; + std::mutex realQueueLock; bool isMuted[1]; - bool on, flip, lastOn; - int pos, speakerType, beepFD; + bool on, flip, lastOn, realOutEnabled; + int pos, speakerType, beepFD, realOutMethod; float low, band; float low2, high2, band2; float low3, band3; @@ -68,7 +86,7 @@ class DivPlatformPCSpeaker: public DivDispatch { friend void putDispatchChan(void*,int,int); - void beepFreq(int freq); + void beepFreq(int freq, int delay=0); void acquire_unfilt(short* bufL, short* bufR, size_t start, size_t len); void acquire_cone(short* bufL, short* bufR, size_t start, size_t len); @@ -76,9 +94,11 @@ class DivPlatformPCSpeaker: public DivDispatch { void acquire_real(short* bufL, short* bufR, size_t start, size_t len); public: + void pcSpeakerThread(); void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/pet.cpp b/src/engine/platform/pet.cpp index 233027e21..0decd909d 100644 --- a/src/engine/platform/pet.cpp +++ b/src/engine/platform/pet.cpp @@ -249,6 +249,10 @@ void* DivPlatformPET::getChanState(int ch) { return &chan; } +DivMacroInt* DivPlatformPET::getChanMacroInt(int ch) { + return &chan.std; +} + DivDispatchOscBuffer* DivPlatformPET::getOscBuffer(int ch) { return oscBuf; } diff --git a/src/engine/platform/pet.h b/src/engine/platform/pet.h index 1e5e49ce5..a50370453 100644 --- a/src/engine/platform/pet.h +++ b/src/engine/platform/pet.h @@ -66,6 +66,7 @@ class DivPlatformPET: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/qsound.cpp b/src/engine/platform/qsound.cpp index 4bd9c3220..8af565df4 100644 --- a/src/engine/platform/qsound.cpp +++ b/src/engine/platform/qsound.cpp @@ -530,6 +530,10 @@ void* DivPlatformQSound::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformQSound::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/qsound.h b/src/engine/platform/qsound.h index d12e952ef..285760138 100644 --- a/src/engine/platform/qsound.h +++ b/src/engine/platform/qsound.h @@ -78,6 +78,7 @@ class DivPlatformQSound: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/rf5c68.cpp b/src/engine/platform/rf5c68.cpp index ae5138c15..2106a5a72 100644 --- a/src/engine/platform/rf5c68.cpp +++ b/src/engine/platform/rf5c68.cpp @@ -300,6 +300,10 @@ void* DivPlatformRF5C68::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformRF5C68::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformRF5C68::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/rf5c68.h b/src/engine/platform/rf5c68.h index 79d7d58b0..6946b4900 100644 --- a/src/engine/platform/rf5c68.h +++ b/src/engine/platform/rf5c68.h @@ -75,6 +75,7 @@ class DivPlatformRF5C68: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/saa.cpp b/src/engine/platform/saa.cpp index f6be85090..dc9a13e81 100644 --- a/src/engine/platform/saa.cpp +++ b/src/engine/platform/saa.cpp @@ -136,7 +136,7 @@ void DivPlatformSAA1099::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); + chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); if (chan[i].outVol<0) chan[i].outVol=0; if (isMuted[i]) { rWrite(i,0); @@ -401,6 +401,10 @@ void* DivPlatformSAA1099::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformSAA1099::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformSAA1099::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/saa.h b/src/engine/platform/saa.h index 70edaa249..fafb36d7c 100644 --- a/src/engine/platform/saa.h +++ b/src/engine/platform/saa.h @@ -91,6 +91,7 @@ class DivPlatformSAA1099: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/scc.cpp b/src/engine/platform/scc.cpp index 1b70d956c..05f32d78b 100644 --- a/src/engine/platform/scc.cpp +++ b/src/engine/platform/scc.cpp @@ -310,6 +310,10 @@ void* DivPlatformSCC::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformSCC::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformSCC::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/scc.h b/src/engine/platform/scc.h index d83e02934..43a8be5ae 100644 --- a/src/engine/platform/scc.h +++ b/src/engine/platform/scc.h @@ -70,6 +70,7 @@ class DivPlatformSCC: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/segapcm.cpp b/src/engine/platform/segapcm.cpp index 22ccbff07..19b8eb82e 100644 --- a/src/engine/platform/segapcm.cpp +++ b/src/engine/platform/segapcm.cpp @@ -85,10 +85,17 @@ void DivPlatformSegaPCM::tick(bool sysTick) { for (int i=0; i<16; i++) { chan[i].std.next(); - // TODO: fix - /*if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; - }*/ + if (parent->song.newSegaPCM) { + if (chan[i].std.vol.had) { + chan[i].outVol=(chan[i].vol*MIN(64,chan[i].std.vol.val))>>6; + chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127; + chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127; + if (dumpWrites) { + addWrite(0x10002+(i<<3),chan[i].chVolL); + addWrite(0x10003+(i<<3),chan[i].chVolR); + } + } + } if (chan[i].std.arp.had) { if (!chan[i].inPorta) { @@ -107,14 +114,24 @@ void DivPlatformSegaPCM::tick(bool sysTick) { } if (chan[i].std.panL.had) { - chan[i].chVolL=chan[i].std.panL.val&127; + if (parent->song.newSegaPCM) { + chan[i].chPanL=chan[i].std.panL.val&127; + chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127; + } else { + chan[i].chVolL=chan[i].std.panL.val&127; + } if (dumpWrites) { addWrite(0x10002+(i<<3),chan[i].chVolL); } } if (chan[i].std.panR.had) { - chan[i].chVolR=chan[i].std.panR.val&127; + if (parent->song.newSegaPCM) { + chan[i].chPanR=chan[i].std.panR.val&127; + chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127; + } else { + chan[i].chVolR=chan[i].std.panR.val&127; + } if (dumpWrites) { addWrite(0x10003+(i<<3),chan[i].chVolR); } @@ -261,8 +278,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; } - chan[c.chan].chVolL=c.value; - chan[c.chan].chVolR=c.value; + if (parent->song.newSegaPCM) { + chan[c.chan].chVolL=(c.value*chan[c.chan].chPanL)/127; + chan[c.chan].chVolR=(c.value*chan[c.chan].chPanR)/127; + } else { + chan[c.chan].chVolL=c.value; + chan[c.chan].chVolR=c.value; + } if (dumpWrites) { addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR); @@ -280,8 +302,15 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) { chan[c.chan].ins=c.value; break; case DIV_CMD_PANNING: { - chan[c.chan].chVolL=c.value>>1; - chan[c.chan].chVolR=c.value2>>1; + if (parent->song.newSegaPCM) { + chan[c.chan].chPanL=c.value>>1; + chan[c.chan].chPanR=c.value2>>1; + chan[c.chan].chVolL=(chan[c.chan].outVol*chan[c.chan].chPanL)/127; + chan[c.chan].chVolR=(chan[c.chan].outVol*chan[c.chan].chPanR)/127; + } else { + chan[c.chan].chVolL=c.value>>1; + chan[c.chan].chVolR=c.value2>>1; + } if (dumpWrites) { addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL); addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR); @@ -371,6 +400,10 @@ void* DivPlatformSegaPCM::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformSegaPCM::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/segapcm.h b/src/engine/platform/segapcm.h index 32cd22c29..6edc85302 100644 --- a/src/engine/platform/segapcm.h +++ b/src/engine/platform/segapcm.h @@ -34,6 +34,7 @@ class DivPlatformSegaPCM: public DivDispatch { bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM; int vol, outVol; unsigned char chVolL, chVolR; + unsigned char chPanL, chPanR; struct PCMChannel { int sample; @@ -46,7 +47,29 @@ class DivPlatformSegaPCM: public DivDispatch { std.init(which); pitch2=0; } - Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {} + Channel(): + freqH(0), + freqL(0), + freq(0), + baseFreq(0), + pitch(0), + pitch2(0), + note(0), + ins(-1), + active(false), + insChanged(true), + freqChanged(false), + keyOn(false), + keyOff(false), + inPorta(false), + portaPause(false), + furnacePCM(false), + vol(0), + outVol(0), + chVolL(127), + chVolR(127), + chPanL(127), + chPanR(127) {} }; Channel chan[16]; DivDispatchOscBuffer* oscBuf[16]; @@ -78,6 +101,7 @@ class DivPlatformSegaPCM: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index c83108a62..f6624b97f 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -75,14 +75,13 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_ if (o<-32768) o=-32768; if (o>32767) o=32767; bufL[h]=o; - /* for (int i=0; i<4; i++) { if (isMuted[i]) { oscBuf[i]->data[oscBuf[i]->needle++]=0; } else { - oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i); + oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]]; } - }*/ + } } } @@ -359,6 +358,8 @@ int DivPlatformSMS::dispatch(DivCommand c) { if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD)); } chan[c.chan].inPorta=c.value; + // TODO: pre porta cancel arp compat flag + //if (chan[c.chan].inPorta) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); break; case DIV_CMD_GET_VOLMAX: return 15; @@ -391,6 +392,10 @@ void* DivPlatformSMS::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformSMS::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index 56f6beda4..c4e4179fe 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -86,6 +86,7 @@ class DivPlatformSMS: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/sound/ymfm/ymfm_opn.h b/src/engine/platform/sound/ymfm/ymfm_opn.h index 74d2b01dd..34dc065d7 100644 --- a/src/engine/platform/sound/ymfm/ymfm_opn.h +++ b/src/engine/platform/sound/ymfm/ymfm_opn.h @@ -778,6 +778,10 @@ public: // get the engine fm_engine* debug_engine() { return &m_fm; } + // get DAC state + uint16_t debug_dac_data() { return m_dac_data; } + uint8_t debug_dac_enable() { return m_dac_enable; } + protected: // simulate the DAC discontinuity constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); } diff --git a/src/engine/platform/su.cpp b/src/engine/platform/su.cpp index 1f85cd335..692fbbee3 100644 --- a/src/engine/platform/su.cpp +++ b/src/engine/platform/su.cpp @@ -484,6 +484,10 @@ void* DivPlatformSoundUnit::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformSoundUnit::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformSoundUnit::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/su.h b/src/engine/platform/su.h index 857ae7251..1d39854f2 100644 --- a/src/engine/platform/su.h +++ b/src/engine/platform/su.h @@ -112,6 +112,7 @@ class DivPlatformSoundUnit: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/swan.cpp b/src/engine/platform/swan.cpp index c941a8fff..73dc79281 100644 --- a/src/engine/platform/swan.cpp +++ b/src/engine/platform/swan.cpp @@ -464,6 +464,10 @@ void* DivPlatformSwan::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformSwan::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/swan.h b/src/engine/platform/swan.h index 32f400e5b..aafb17ace 100644 --- a/src/engine/platform/swan.h +++ b/src/engine/platform/swan.h @@ -79,6 +79,7 @@ class DivPlatformSwan: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/tia.cpp b/src/engine/platform/tia.cpp index 7a4407ebd..da3472446 100644 --- a/src/engine/platform/tia.cpp +++ b/src/engine/platform/tia.cpp @@ -88,7 +88,7 @@ void DivPlatformTIA::tick(bool sysTick) { for (int i=0; i<2; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15)); + chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15); if (chan[i].outVol<0) chan[i].outVol=0; if (isMuted[i]) { rWrite(0x19+i,0); @@ -293,6 +293,10 @@ void* DivPlatformTIA::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformTIA::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformTIA::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/tia.h b/src/engine/platform/tia.h index 76064d069..cabe91533 100644 --- a/src/engine/platform/tia.h +++ b/src/engine/platform/tia.h @@ -52,6 +52,7 @@ class DivPlatformTIA: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index d530d9ee3..123d1193c 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -238,7 +238,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; @@ -246,7 +246,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -322,7 +322,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -375,7 +375,7 @@ void DivPlatformTX81Z::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -467,7 +467,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[ch].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -497,7 +497,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { } else { if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -562,7 +562,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -662,7 +662,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -953,7 +953,7 @@ void DivPlatformTX81Z::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -995,6 +995,10 @@ void* DivPlatformTX81Z::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformTX81Z::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformTX81Z::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/tx81z.h b/src/engine/platform/tx81z.h index 93f220f1c..b185de5f6 100644 --- a/src/engine/platform/tx81z.h +++ b/src/engine/platform/tx81z.h @@ -100,6 +100,7 @@ class DivPlatformTX81Z: public DivDispatch, public DivPlatformOPMBase { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/vera.cpp b/src/engine/platform/vera.cpp index 353a9f603..62e1ea682 100644 --- a/src/engine/platform/vera.cpp +++ b/src/engine/platform/vera.cpp @@ -397,6 +397,10 @@ void* DivPlatformVERA::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformVERA::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformVERA::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/vera.h b/src/engine/platform/vera.h index 734db020b..612b4354b 100644 --- a/src/engine/platform/vera.h +++ b/src/engine/platform/vera.h @@ -66,6 +66,7 @@ class DivPlatformVERA: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/vic20.cpp b/src/engine/platform/vic20.cpp index 708db4abf..a766ba443 100644 --- a/src/engine/platform/vic20.cpp +++ b/src/engine/platform/vic20.cpp @@ -278,6 +278,10 @@ void* DivPlatformVIC20::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformVIC20::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformVIC20::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/vic20.h b/src/engine/platform/vic20.h index f05ad8f2b..d23f27be8 100644 --- a/src/engine/platform/vic20.h +++ b/src/engine/platform/vic20.h @@ -68,6 +68,7 @@ class DivPlatformVIC20: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/vrc6.cpp b/src/engine/platform/vrc6.cpp index 16ba73b0b..08c6d0538 100644 --- a/src/engine/platform/vrc6.cpp +++ b/src/engine/platform/vrc6.cpp @@ -438,6 +438,10 @@ void* DivPlatformVRC6::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformVRC6::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformVRC6::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/vrc6.h b/src/engine/platform/vrc6.h index dd6863d5f..450e09b98 100644 --- a/src/engine/platform/vrc6.h +++ b/src/engine/platform/vrc6.h @@ -85,6 +85,7 @@ class DivPlatformVRC6: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/x1_010.cpp b/src/engine/platform/x1_010.cpp index 1cbfd68e7..dc5c7ed22 100644 --- a/src/engine/platform/x1_010.cpp +++ b/src/engine/platform/x1_010.cpp @@ -842,6 +842,10 @@ void* DivPlatformX1_010::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformX1_010::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/x1_010.h b/src/engine/platform/x1_010.h index 939280ef9..7a85b6336 100644 --- a/src/engine/platform/x1_010.h +++ b/src/engine/platform/x1_010.h @@ -129,6 +129,7 @@ class DivPlatformX1_010: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2203.cpp b/src/engine/platform/ym2203.cpp index de54d8a61..3176a1a4b 100644 --- a/src/engine/platform/ym2203.cpp +++ b/src/engine/platform/ym2203.cpp @@ -329,7 +329,7 @@ void DivPlatformYM2203::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; @@ -337,7 +337,7 @@ void DivPlatformYM2203::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -387,7 +387,7 @@ void DivPlatformYM2203::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -432,7 +432,7 @@ void DivPlatformYM2203::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -544,7 +544,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { } else { if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -603,7 +603,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -681,7 +681,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -873,7 +873,7 @@ void DivPlatformYM2203::muteChannel(int ch, bool mute) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[ch].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -890,7 +890,7 @@ void DivPlatformYM2203::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -924,6 +924,11 @@ void* DivPlatformYM2203::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2203::getChanMacroInt(int ch) { + if (ch>=3) return ay->getChanMacroInt(ch-3); + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2203::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ym2203.h b/src/engine/platform/ym2203.h index c072647c4..70be354d6 100644 --- a/src/engine/platform/ym2203.h +++ b/src/engine/platform/ym2203.h @@ -102,6 +102,7 @@ class DivPlatformYM2203: public DivDispatch, public DivPlatformOPNBase { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2203ext.cpp b/src/engine/platform/ym2203ext.cpp index 350a58704..fbef7b043 100644 --- a/src/engine/platform/ym2203ext.cpp +++ b/src/engine/platform/ym2203ext.cpp @@ -42,7 +42,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -81,7 +81,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } break; } @@ -413,7 +413,7 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) { if (isOpMuted[ch-2]) { rWrite(baseAddr+0x40,127); } else if (isOutput[ins->fm.alg][ordch]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -428,7 +428,7 @@ void DivPlatformYM2203Ext::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -468,6 +468,12 @@ void* DivPlatformYM2203Ext::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2203Ext::getChanMacroInt(int ch) { + if (ch>=6) return ay->getChanMacroInt(ch-6); + if (ch>=2) return NULL; // currently not implemented + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) { if (ch>=6) return oscBuf[ch-3]; if (ch<3) return oscBuf[ch]; diff --git a/src/engine/platform/ym2203ext.h b/src/engine/platform/ym2203ext.h index 5025881cf..1a398d1a6 100644 --- a/src/engine/platform/ym2203ext.h +++ b/src/engine/platform/ym2203ext.h @@ -40,6 +40,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ym2608.cpp b/src/engine/platform/ym2608.cpp index 9756a44af..d4871486b 100644 --- a/src/engine/platform/ym2608.cpp +++ b/src/engine/platform/ym2608.cpp @@ -491,12 +491,12 @@ void DivPlatformYM2608::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -550,7 +550,7 @@ void DivPlatformYM2608::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -600,7 +600,7 @@ void DivPlatformYM2608::tick(bool sysTick) { if (m.tl.had) { op.tl=127-m.tl.val; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -827,7 +827,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -907,7 +907,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1011,7 +1011,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1214,7 +1214,7 @@ void DivPlatformYM2608::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1248,6 +1248,11 @@ void* DivPlatformYM2608::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2608::getChanMacroInt(int ch) { + if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6); + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2608::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ym2608.h b/src/engine/platform/ym2608.h index ffa7a160c..a6d4399a4 100644 --- a/src/engine/platform/ym2608.h +++ b/src/engine/platform/ym2608.h @@ -117,6 +117,7 @@ class DivPlatformYM2608: public DivDispatch, public DivPlatformOPNBase { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2608ext.cpp b/src/engine/platform/ym2608ext.cpp index 38d2b4b28..d33bf9edb 100644 --- a/src/engine/platform/ym2608ext.cpp +++ b/src/engine/platform/ym2608ext.cpp @@ -42,7 +42,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -81,7 +81,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } break; } @@ -413,7 +413,7 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) { if (isOpMuted[ch-2]) { rWrite(baseAddr+0x40,127); } else if (isOutput[ins->fm.alg][ordch]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -428,7 +428,7 @@ void DivPlatformYM2608Ext::forceIns() { if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); } else if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -437,7 +437,7 @@ void DivPlatformYM2608Ext::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -481,6 +481,13 @@ void* DivPlatformYM2608Ext::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2608Ext::getChanMacroInt(int ch) { + if (ch>=9 && ch<12) return ay->getChanMacroInt(ch-9); + if (ch>=6) return &chan[ch-3].std; + if (ch>=2) return NULL; // currently not implemented + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) { if (ch>=6) return oscBuf[ch-3]; if (ch<3) return oscBuf[ch]; diff --git a/src/engine/platform/ym2608ext.h b/src/engine/platform/ym2608ext.h index a0966dfe1..bc3d4f991 100644 --- a/src/engine/platform/ym2608ext.h +++ b/src/engine/platform/ym2608ext.h @@ -40,6 +40,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index d8d8e8572..df340804a 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -531,12 +531,12 @@ void DivPlatformYM2610::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -590,7 +590,7 @@ void DivPlatformYM2610::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -640,7 +640,7 @@ void DivPlatformYM2610::tick(bool sysTick) { if (m.tl.had) { op.tl=127-m.tl.val; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -870,7 +870,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -950,7 +950,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1054,7 +1054,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1257,7 +1257,7 @@ void DivPlatformYM2610::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1291,6 +1291,11 @@ void* DivPlatformYM2610::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2610::getChanMacroInt(int ch) { + if (ch>=4 && ch<7) return ay->getChanMacroInt(ch-4); + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2610::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ym2610.h b/src/engine/platform/ym2610.h index 37c0fa839..39bc82f39 100644 --- a/src/engine/platform/ym2610.h +++ b/src/engine/platform/ym2610.h @@ -135,6 +135,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index 90ddeb403..16f198f7e 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -513,12 +513,12 @@ void DivPlatformYM2610B::tick(bool sysTick) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127; + chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127); for (int j=0; j<4; j++) { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -572,7 +572,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -622,7 +622,7 @@ void DivPlatformYM2610B::tick(bool sysTick) { if (m.tl.had) { op.tl=127-m.tl.val; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -851,7 +851,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { if (!chan[c.chan].active || chan[c.chan].insChanged) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } } else { if (chan[c.chan].insChanged) { @@ -931,7 +931,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; if (isOutput[chan[c.chan].state.alg][i]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1035,7 +1035,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.tl=c.value2; if (isOutput[chan[c.chan].state.alg][c.value]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1238,7 +1238,7 @@ void DivPlatformYM2610B::forceIns() { unsigned short baseAddr=chanOffs[i]|opOffs[j]; DivInstrumentFM::Operator& op=chan[i].state.op[j]; if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -1272,6 +1272,11 @@ void* DivPlatformYM2610B::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2610B::getChanMacroInt(int ch) { + if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6); + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2610B::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ym2610b.h b/src/engine/platform/ym2610b.h index 1262ba028..737251e4f 100644 --- a/src/engine/platform/ym2610b.h +++ b/src/engine/platform/ym2610b.h @@ -103,6 +103,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index b4c154faa..61a997895 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -42,7 +42,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -81,7 +81,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } break; } @@ -413,7 +413,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) { if (isOpMuted[ch-2]) { rWrite(baseAddr+0x40,127); } else if (isOutput[ins->fm.alg][ordch]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -428,7 +428,7 @@ void DivPlatformYM2610BExt::forceIns() { if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); } else if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -437,7 +437,7 @@ void DivPlatformYM2610BExt::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -481,6 +481,13 @@ void* DivPlatformYM2610BExt::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYM2610BExt::getChanMacroInt(int ch) { + if (ch>=9 && ch<12) return ay->getChanMacroInt(ch-9); + if (ch>=6) return &chan[ch-3].std; + if (ch>=2) return NULL; // currently not implemented + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) { if (ch>=6) return oscBuf[ch-3]; if (ch<3) return oscBuf[ch]; diff --git a/src/engine/platform/ym2610bext.h b/src/engine/platform/ym2610bext.h index c17fd8d8a..732678fe5 100644 --- a/src/engine/platform/ym2610bext.h +++ b/src/engine/platform/ym2610bext.h @@ -40,6 +40,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 0ac118a1a..f2006c1cc 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -42,7 +42,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { rWrite(baseAddr+0x40,127); } else { if (opChan[ch].insChanged) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } } if (opChan[ch].insChanged) { @@ -81,7 +81,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } break; } @@ -413,7 +413,7 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) { if (isOpMuted[ch]) { rWrite(baseAddr+0x40,127); } else if (isOutput[ins->fm.alg][ordch]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -428,7 +428,7 @@ void DivPlatformYM2610Ext::forceIns() { if (isOpMuted[j]) { rWrite(baseAddr+0x40,127); } else if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127)); + rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127)); } else { rWrite(baseAddr+0x40,op.tl); } @@ -437,7 +437,7 @@ void DivPlatformYM2610Ext::forceIns() { rWrite(baseAddr+ADDR_TL,127); } else { if (isOutput[chan[i].state.alg][j]) { - rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127)); + rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127)); } else { rWrite(baseAddr+ADDR_TL,op.tl); } @@ -475,13 +475,19 @@ void DivPlatformYM2610Ext::forceIns() { } } - void* DivPlatformYM2610Ext::getChanState(int ch) { if (ch>=5) return &chan[ch-3]; if (ch>=1) return &opChan[ch-1]; return &chan[ch]; } +DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) { + if (ch>=7 && ch<10) return ay->getChanMacroInt(ch-7); + if (ch>=5) return &chan[ch-3].std; + if (ch>=1) return NULL; // currently not implemented + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) { if (ch>=5) return oscBuf[ch-3]; if (ch<2) return oscBuf[ch]; diff --git a/src/engine/platform/ym2610ext.h b/src/engine/platform/ym2610ext.h index 492eb5de4..119d63569 100644 --- a/src/engine/platform/ym2610ext.h +++ b/src/engine/platform/ym2610ext.h @@ -40,6 +40,7 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 { public: int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); void reset(); void forceIns(); diff --git a/src/engine/platform/ymz280b.cpp b/src/engine/platform/ymz280b.cpp index 87497238e..45631a940 100644 --- a/src/engine/platform/ymz280b.cpp +++ b/src/engine/platform/ymz280b.cpp @@ -333,6 +333,10 @@ void* DivPlatformYMZ280B::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformYMZ280B::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformYMZ280B::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/ymz280b.h b/src/engine/platform/ymz280b.h index 97c64d21e..0d254c088 100644 --- a/src/engine/platform/ymz280b.h +++ b/src/engine/platform/ymz280b.h @@ -74,6 +74,7 @@ class DivPlatformYMZ280B: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/platform/zxbeeper.cpp b/src/engine/platform/zxbeeper.cpp index ff14d161c..34265f8a0 100644 --- a/src/engine/platform/zxbeeper.cpp +++ b/src/engine/platform/zxbeeper.cpp @@ -251,6 +251,10 @@ void* DivPlatformZXBeeper::getChanState(int ch) { return &chan[ch]; } +DivMacroInt* DivPlatformZXBeeper::getChanMacroInt(int ch) { + return &chan[ch].std; +} + DivDispatchOscBuffer* DivPlatformZXBeeper::getOscBuffer(int ch) { return oscBuf[ch]; } diff --git a/src/engine/platform/zxbeeper.h b/src/engine/platform/zxbeeper.h index 9520ea71b..a9b400cb6 100644 --- a/src/engine/platform/zxbeeper.h +++ b/src/engine/platform/zxbeeper.h @@ -77,6 +77,7 @@ class DivPlatformZXBeeper: public DivDispatch { void acquire(short* bufL, short* bufR, size_t start, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); DivDispatchOscBuffer* getOscBuffer(int chan); unsigned char* getRegisterPool(); int getRegisterPoolSize(); diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 73e122383..ed70da94b 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "macroInt.h" #include #define _USE_MATH_DEFINES #include "dispatch.h" @@ -797,6 +798,9 @@ void DivEngine::nextRow() { printf("| %.2x:%s | \x1b[1;33m%3d%s\x1b[m\n",curOrder,pb1,curRow,pb3); } + prevOrder=curOrder; + prevRow=curRow; + for (int i=0; i=chans) { + pendingNotes.pop(); + continue; + } if (note.on) { dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,note.channel,note.ins,1)); dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note)); keyHit[note.channel]=true; chan[note.channel].noteOnInhibit=true; } else { - dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel)); + DivMacroInt* macroInt=disCont[dispatchOfChan[note.channel]].dispatch->getChanMacroInt(dispatchChanOfChan[note.channel]); + if (macroInt!=NULL) { + if (macroInt->hasRelease) { + dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,note.channel)); + } else { + dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel)); + } + } else { + dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,note.channel)); + } } pendingNotes.pop(); } @@ -1079,6 +1096,8 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) { } void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size) { + lastLoopPos=-1; + if (out!=NULL) { memset(out[0],0,size*sizeof(float)); memset(out[1],0,size*sizeof(float)); @@ -1270,6 +1289,9 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi } } if (nextTick()) { + lastLoopPos=size-(runLeftG>>MASTER_CLOCK_PREC); + logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG); + totalLoops++; if (remainingLoops>0) { remainingLoops--; if (!remainingLoops) { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index be2c72363..e5a4888b9 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1975,7 +1975,21 @@ void DivEngine::registerSystems() { {"Sample"}, {"PCM"}, {DIV_CH_PCM}, - {DIV_INS_AMIGA} + {DIV_INS_AMIGA}, + {}, + [this](int ch, unsigned char effect, unsigned char effectVal) -> bool { + switch (effect) { + case 0x20: // select rate + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal)); + break; + case 0x21: // select clock + dispatchCmd(DivCommand(DIV_CMD_SAMPLE_MODE,ch,effectVal)); + break; + default: + return false; + } + return true; + } ); sysDefs[DIV_SYSTEM_YMZ280B]=new DivSysDef( diff --git a/src/gui/chanOsc.cpp b/src/gui/chanOsc.cpp index 168f469db..fd9e2e107 100644 --- a/src/gui/chanOsc.cpp +++ b/src/gui/chanOsc.cpp @@ -22,8 +22,9 @@ #include "imgui.h" #include "imgui_internal.h" -#define FURNACE_FFT_SIZE 8192 +#define FURNACE_FFT_SIZE 4096 #define FURNACE_FFT_RATE 80.0 +#define FURNACE_FFT_CUTOFF 0.1 void FurnaceGUI::drawChanOsc() { if (nextWindow==GUI_WINDOW_CHAN_OSC) { @@ -34,6 +35,7 @@ void FurnaceGUI::drawChanOsc() { if (!chanOscOpen) return; ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); if (ImGui::Begin("Oscilloscope (per-channel)",&chanOscOpen,globalWinFlags)) { + bool centerSettingReset=false; if (ImGui::BeginTable("ChanOscSettings",3)) { ImGui::TableNextRow(); ImGui::TableNextColumn(); @@ -55,7 +57,9 @@ void FurnaceGUI::drawChanOsc() { } ImGui::TableNextColumn(); - ImGui::Checkbox("Center waveform",&chanOscWaveCorr); + if (ImGui::Checkbox("Center waveform",&chanOscWaveCorr)) { + centerSettingReset=true; + } ImGui::EndTable(); } @@ -98,6 +102,10 @@ void FurnaceGUI::drawChanOsc() { ImVec2 size=ImGui::GetContentRegionAvail(); size.y=availY/rows; + if (centerSettingReset) { + buf->readNeedle=buf->needle; + } + // check FFT status existence if (fft->plan==NULL) { logD("creating FFT plan for channel %d",ch); @@ -129,11 +137,14 @@ void FurnaceGUI::drawChanOsc() { } else { unsigned short needlePos=buf->needle; if (chanOscWaveCorr) { + /* double fftDataRate=(FURNACE_FFT_SIZE*FURNACE_FFT_RATE)/((double)buf->rate); while (buf->readNeedle!=needlePos) { fft->inBufPosFrac+=fftDataRate; while (fft->inBufPosFrac>=1.0) { - fft->inBuf[fft->inBufPos]=(double)buf->data[buf->readNeedle]/32768.0; + chanOscLP0[ch]+=FURNACE_FFT_CUTOFF*((float)buf->data[buf->readNeedle]-chanOscLP0[ch]); + chanOscLP1[ch]+=FURNACE_FFT_CUTOFF*(chanOscLP0[ch]-chanOscLP1[ch]); + fft->inBuf[fft->inBufPos]=(double)chanOscLP1[ch]/32768.0; if (++fft->inBufPos>=FURNACE_FFT_SIZE) { fftw_execute(fft->plan); fft->inBufPos=0; @@ -142,7 +153,12 @@ void FurnaceGUI::drawChanOsc() { fft->inBufPosFrac-=1.0; } buf->readNeedle++; + }*/ + + for (int i=0; iinBuf[i]=(double)buf->data[(unsigned short)(needlePos-displaySize*2+((i*displaySize*2)/FURNACE_FFT_SIZE))]/32768.0; } + fftw_execute(fft->plan); // find origin frequency int point=1; @@ -159,14 +175,24 @@ void FurnaceGUI::drawChanOsc() { // PHASE fftw_complex& candPoint=fft->outBuf[point]; - double phase=((double)buf->rate/(FURNACE_FFT_RATE*point))*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2))); + double phase=((double)(displaySize*2)/(double)point)*(0.5+(atan2(candPoint[1],candPoint[0])/(M_PI*2))); - //printf("%d cphase: %f\n",ch,phase*((double)buf->rate/FURNACE_FFT_RATE)); - String cPhase=fmt::sprintf("%d cphase: %f\n",point,phase); + //needlePos=fft->needle; + needlePos-=phase; + + /* + int alignment=0; + for (unsigned short i=0; idata[(unsigned short)(needlePos-i)])>fabs(buf->data[(unsigned short)(needlePos-alignment)])) { + alignment=i; + } + } + needlePos-=alignment; + */ + + String cPhase=fmt::sprintf("%d cphase: %f",point,phase); dl->AddText(inRect.Min,0xffffffff,cPhase.c_str()); - needlePos=fft->needle; - needlePos-=phase; needlePos-=displaySize; for (unsigned short i=0; i<512; i++) { float x=(float)i/512.0f; diff --git a/src/gui/doAction.cpp b/src/gui/doAction.cpp index a83c646da..60c9d5c3a 100644 --- a/src/gui/doAction.cpp +++ b/src/gui/doAction.cpp @@ -87,6 +87,12 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_STOP: stop(); break; + case GUI_ACTION_PLAY_START: + e->setOrder(0); + if (!e->isPlaying()) { + play(); + } + break; case GUI_ACTION_PLAY_REPEAT: play(); e->setRepeatPattern(true); @@ -244,6 +250,9 @@ void FurnaceGUI::doAction(int what) { case GUI_ACTION_WINDOW_CHAN_OSC: nextWindow=GUI_WINDOW_CHAN_OSC; break; + case GUI_ACTION_WINDOW_FIND: + nextWindow=GUI_WINDOW_FIND; + break; case GUI_ACTION_COLLAPSE_WINDOW: collapseWindow=true; @@ -325,6 +334,9 @@ void FurnaceGUI::doAction(int what) { case GUI_WINDOW_CHAN_OSC: chanOscOpen=false; break; + case GUI_WINDOW_FIND: + findOpen=false; + break; default: break; } @@ -536,6 +548,17 @@ void FurnaceGUI::doAction(int what) { if (curIns==-1) { showError("too many instruments!"); } else { + if (settings.blankIns) { + e->song.ins[curIns]->fm.fb=0; + for (int i=0; i<4; i++) { + e->song.ins[curIns]->fm.op[i]=DivInstrumentFM::Operator(); + e->song.ins[curIns]->fm.op[i].ar=31; + e->song.ins[curIns]->fm.op[i].dr=31; + e->song.ins[curIns]->fm.op[i].rr=15; + e->song.ins[curIns]->fm.op[i].tl=127; + e->song.ins[curIns]->fm.op[i].dt=3; + } + } wantScrollList=true; MARK_MODIFIED; wavePreviewInit=true; diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 671ca63b4..912bd1c09 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -150,6 +150,14 @@ void FurnaceGUI::drawEditControls() { e->stepOne(cursor.y); pendingStepUpdate=true; } + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(noteInputPoly)); + if (ImGui::Button(noteInputPoly?("Poly##PolyInput"):("Mono##PolyInput"))) { + noteInputPoly=!noteInputPoly; + e->setAutoNotePoly(noteInputPoly); + } + ImGui::PopStyleColor(); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; ImGui::End(); @@ -227,6 +235,14 @@ void FurnaceGUI::drawEditControls() { unimportant(ImGui::Checkbox("Orders",&followOrders)); ImGui::SameLine(); unimportant(ImGui::Checkbox("Pattern",&followPattern)); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(noteInputPoly)); + if (ImGui::Button(noteInputPoly?("Poly##PolyInput"):("Mono##PolyInput"))) { + noteInputPoly=!noteInputPoly; + e->setAutoNotePoly(noteInputPoly); + } + ImGui::PopStyleColor(); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; ImGui::End(); @@ -302,6 +318,13 @@ void FurnaceGUI::drawEditControls() { followPattern=!followPattern; } ImGui::PopStyleColor(); + + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(noteInputPoly)); + if (ImGui::Button(noteInputPoly?("Poly##PolyInput"):("Mono##PolyInput"))) { + noteInputPoly=!noteInputPoly; + e->setAutoNotePoly(noteInputPoly); + } + ImGui::PopStyleColor(); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; ImGui::End(); @@ -357,6 +380,14 @@ void FurnaceGUI::drawEditControls() { e->setRepeatPattern(!repeatPattern); } ImGui::PopStyleColor(); + + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Button,TOGGLE_COLOR(noteInputPoly)); + if (ImGui::Button(noteInputPoly?("Poly##PolyInput"):("Mono##PolyInput"))) { + noteInputPoly=!noteInputPoly; + e->setAutoNotePoly(noteInputPoly); + } + ImGui::PopStyleColor(); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_EDIT_CONTROLS; ImGui::End(); diff --git a/src/gui/findReplace.cpp b/src/gui/findReplace.cpp new file mode 100644 index 000000000..3a5ec75f2 --- /dev/null +++ b/src/gui/findReplace.cpp @@ -0,0 +1,19 @@ +#include "gui.h" +#include "imgui.h" +#include "IconsFontAwesome4.h" +#include "misc/cpp/imgui_stdlib.h" + +void FurnaceGUI::drawFindReplace() { + if (nextWindow==GUI_WINDOW_FIND) { + findOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!findOpen) return; + ImGui::SetNextWindowSizeConstraints(ImVec2(64.0f*dpiScale,32.0f*dpiScale),ImVec2(scrW*dpiScale,scrH*dpiScale)); + if (ImGui::Begin("Find/Replace",&findOpen,globalWinFlags)) { + ImGui::Text("What am I gonna do with you?"); + } + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_FIND; + ImGui::End(); +} diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 5c083a5c0..20d3436b7 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -1657,6 +1657,8 @@ int FurnaceGUI::load(String path) { curNibble=false; orderNibble=false; orderCursor=-1; + curOrder=0; + oldRow=0; samplePos=0; updateSampleTex=true; selStart=SelectionPoint(); @@ -1666,6 +1668,7 @@ int FurnaceGUI::load(String path) { undoHist.clear(); redoHist.clear(); updateWindowTitle(); + updateScroll(0); if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } @@ -1673,7 +1676,7 @@ int FurnaceGUI::load(String path) { } void FurnaceGUI::exportAudio(String path, DivAudioExportModes mode) { - e->saveAudio(path.c_str(),exportLoops+1,mode); + e->saveAudio(path.c_str(),exportLoops+1,mode,exportFadeOut); displayExporting=true; } @@ -1896,6 +1899,12 @@ void FurnaceGUI::processDrags(int dragX, int dragY) { void FurnaceGUI::editOptions(bool topMenu) { char id[4096]; editOptsVisible=true; + + if (topMenu) { + ImGui::Text("..."); + ImGui::Separator(); + } + if (ImGui::MenuItem("cut",BIND_FOR(GUI_ACTION_PAT_CUT))) doCopy(true); if (ImGui::MenuItem("copy",BIND_FOR(GUI_ACTION_PAT_COPY))) doCopy(false); if (ImGui::MenuItem("paste",BIND_FOR(GUI_ACTION_PAT_PASTE))) doPaste(); @@ -2198,6 +2207,17 @@ void FurnaceGUI::editOptions(bool topMenu) { if (ImGui::MenuItem("expand",BIND_FOR(GUI_ACTION_PAT_EXPAND_ROWS))) doExpand(2); if (topMenu) { + ImGui::Separator(); + if (ImGui::MenuItem("find/replace",BIND_FOR(GUI_ACTION_WINDOW_FIND),findOpen)) { + if (findOpen) { + findOpen=false; + } else { + nextWindow=GUI_WINDOW_FIND; + } + } + } + + /*if (topMenu) { ImGui::Separator(); ImGui::MenuItem("collapse pattern",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_PAT)); ImGui::MenuItem("expand pattern",BIND_FOR(GUI_ACTION_PAT_EXPAND_PAT)); @@ -2205,7 +2225,7 @@ void FurnaceGUI::editOptions(bool topMenu) { ImGui::Separator(); ImGui::MenuItem("collapse song",BIND_FOR(GUI_ACTION_PAT_COLLAPSE_SONG)); ImGui::MenuItem("expand song",BIND_FOR(GUI_ACTION_PAT_EXPAND_SONG)); - } + }*/ } void FurnaceGUI::toggleMobileUI(bool enable, bool force) { @@ -2824,6 +2844,9 @@ bool FurnaceGUI::loop() { if (ImGui::InputInt("Loops",&exportLoops,1,2)) { if (exportLoops<0) exportLoops=0; } + if (ImGui::InputDouble("Fade out (seconds)",&exportFadeOut,1.0,2.0,"%.1f")) { + if (exportFadeOut<0.0) exportFadeOut=0.0; + } ImGui::EndMenu(); } if (ImGui::BeginMenu("export VGM...")) { @@ -3078,6 +3101,7 @@ bool FurnaceGUI::loop() { ImGui::DockSpaceOverViewport(NULL,lockLayout?(ImGuiDockNodeFlags_NoWindowMenuButton|ImGuiDockNodeFlags_NoMove|ImGuiDockNodeFlags_NoResize|ImGuiDockNodeFlags_NoCloseButton|ImGuiDockNodeFlags_NoDocking|ImGuiDockNodeFlags_NoDockingSplitMe|ImGuiDockNodeFlags_NoDockingSplitOther):0); drawSubSongs(); + drawFindReplace(); drawPattern(); drawEditControls(); drawSongInfo(); @@ -3330,8 +3354,16 @@ bool FurnaceGUI::loop() { if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } - for (DivInstrument* i: instruments) { - e->addInstrumentPtr(i); + if (instruments.size()>1) { // ask which instruments to load + for (DivInstrument* i: instruments) { + pendingIns.push_back(std::make_pair(i,false)); + } + displayPendingIns=true; + pendingInsSingle=false; + } else { // load the only instrument + for (DivInstrument* i: instruments) { + e->addInstrumentPtr(i); + } } } else { showError("cannot load instrument! ("+e->getLastError()+")"); @@ -3344,13 +3376,21 @@ bool FurnaceGUI::loop() { if (!e->getWarnings().empty()) { showWarning(e->getWarnings(),GUI_WARN_GENERIC); } - if (curIns>=0 && curIns<(int)e->song.ins.size()) { - *e->song.ins[curIns]=*instruments[0]; - } else { - showError("...but you haven't selected an instrument!"); - } - for (DivInstrument* i: instruments) { - delete i; + if (instruments.size()>1) { // ask which instrument + for (DivInstrument* i: instruments) { + pendingIns.push_back(std::make_pair(i,false)); + } + displayPendingIns=true; + pendingInsSingle=true; + } else { // replace with the only instrument + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + *e->song.ins[curIns]=*instruments[0]; + } else { + showError("...but you haven't selected an instrument!"); + } + for (DivInstrument* i: instruments) { + delete i; + } } } else { showError("cannot load instrument! ("+e->getLastError()+")"); @@ -3442,6 +3482,11 @@ bool FurnaceGUI::loop() { ImGui::OpenPopup("Error"); } + if (displayPendingIns) { + displayPendingIns=false; + ImGui::OpenPopup("Select Instrument"); + } + if (displayExporting) { displayExporting=false; ImGui::OpenPopup("Rendering..."); @@ -3792,6 +3837,86 @@ bool FurnaceGUI::loop() { ImGui::EndPopup(); } + // TODO: + // - multiple selection + // - replace instrument + if (ImGui::BeginPopupModal("Select Instrument",NULL,ImGuiWindowFlags_AlwaysAutoResize)) { + bool quitPlease=false; + if (pendingInsSingle) { + ImGui::Text("this is an instrument bank! select which one to use:"); + } else { + ImGui::Text("this is an instrument bank! select which ones to load:"); + ImGui::SameLine(); + if (ImGui::Button("All")) { + for (std::pair& i: pendingIns) { + i.second=true; + } + } + ImGui::SameLine(); + if (ImGui::Button("None")) { + for (std::pair& i: pendingIns) { + i.second=false; + } + } + } + bool anySelected=false; + float sizeY=ImGui::GetFrameHeightWithSpacing()*pendingIns.size(); + if (sizeY>(scrH-180.0)*dpiScale) { + sizeY=(scrH-180.0)*dpiScale; + if (sizeY<60.0*dpiScale) sizeY=60.0*dpiScale; + } + if (ImGui::BeginTable("PendingInsList",1,ImGuiTableFlags_ScrollY,ImVec2(0.0f,sizeY))) { + for (size_t i=0; iname); + if (pendingInsSingle) { + if (ImGui::Selectable(id.c_str())) { + pendingIns[i].second=true; + quitPlease=true; + } + } else { + ImGui::Checkbox(id.c_str(),&pendingIns[i].second); + } + if (pendingIns[i].second) anySelected=true; + } + ImGui::EndTable(); + } + if (!pendingInsSingle) { + ImGui::BeginDisabled(!anySelected); + if (ImGui::Button("OK")) { + quitPlease=true; + } + ImGui::EndDisabled(); + ImGui::SameLine(); + } + if (ImGui::Button("Cancel")) { + for (std::pair& i: pendingIns) { + i.second=false; + } + quitPlease=true; + } + if (quitPlease) { + ImGui::CloseCurrentPopup(); + for (std::pair& i: pendingIns) { + if (!i.second || pendingInsSingle) { + if (i.second) { + if (curIns>=0 && curIns<(int)e->song.ins.size()) { + *e->song.ins[curIns]=*i.first; + } else { + showError("...but you haven't selected an instrument!"); + } + } + delete i.first; + } else { + e->addInstrumentPtr(i.first); + } + } + pendingIns.clear(); + } + ImGui::EndPopup(); + } + layoutTimeEnd=SDL_GetPerformanceCounter(); // backup trigger @@ -3923,6 +4048,7 @@ bool FurnaceGUI::init() { edit=e->getConfBool("edit",false); followOrders=e->getConfBool("followOrders",true); followPattern=e->getConfBool("followPattern",true); + noteInputPoly=e->getConfBool("noteInputPoly",true); orderEditMode=e->getConfInt("orderEditMode",0); if (orderEditMode<0) orderEditMode=0; if (orderEditMode>3) orderEditMode=3; @@ -3949,6 +4075,8 @@ bool FurnaceGUI::init() { initSystemPresets(); + e->setAutoNotePoly(noteInputPoly); + #if !(defined(__APPLE__) || defined(_WIN32)) unsigned char* furIcon=getFurnaceIcon(); SDL_Surface* icon=SDL_CreateRGBSurfaceFrom(furIcon,256,256,32,256*4,0xff,0xff00,0xff0000,0xff000000); @@ -4142,6 +4270,7 @@ bool FurnaceGUI::finish() { e->setConf("followOrders",followOrders); e->setConf("followPattern",followPattern); e->setConf("orderEditMode",orderEditMode); + e->setConf("noteInputPoly",noteInputPoly); // commit oscilloscope state e->setConf("oscZoom",oscZoom); @@ -4193,6 +4322,9 @@ FurnaceGUI::FurnaceGUI(): fullScreen(false), preserveChanPos(false), wantScrollList(false), + noteInputPoly(true), + displayPendingIns(false), + pendingInsSingle(false), vgmExportVersion(0x171), drawHalt(10), macroPointSize(16), @@ -4240,6 +4372,7 @@ FurnaceGUI::FurnaceGUI(): latchTarget(0), wheelX(0), wheelY(0), + exportFadeOut(5.0), editControlsOpen(true), ordersOpen(true), insListOpen(true), @@ -4267,35 +4400,7 @@ FurnaceGUI::FurnaceGUI(): effectListOpen(false), chanOscOpen(false), subSongsOpen(true), - /* - editControlsDocked(false), - ordersDocked(false), - insListDocked(false), - songInfoDocked(false), - patternDocked(false), - insEditDocked(false), - waveListDocked(false), - waveEditDocked(false), - sampleListDocked(false), - sampleEditDocked(false), - aboutDocked(false), - settingsDocked(false), - mixerDocked(false), - debugDocked(false), - inspectorDocked(false), - oscDocked(false), - volMeterDocked(false), - statsDocked(false), - compatFlagsDocked(false), - pianoDocked(false), - notesDocked(false), - channelsDocked(false), - regViewDocked(false), - logDocked(false), - effectListDocked(false), - chanOscDocked(false), - subSongsDocked(false), - */ + findOpen(false), selecting(false), selectingFull(false), curNibble(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index e4a98337b..a1ff2b3d9 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "fileDialog.h" @@ -244,7 +245,8 @@ enum FurnaceGUIWindows { GUI_WINDOW_LOG, GUI_WINDOW_EFFECT_LIST, GUI_WINDOW_CHAN_OSC, - GUI_WINDOW_SUBSONGS + GUI_WINDOW_SUBSONGS, + GUI_WINDOW_FIND }; enum FurnaceGUIFileDialogs { @@ -308,6 +310,7 @@ enum FurnaceGUIActions { GUI_ACTION_PLAY_TOGGLE, GUI_ACTION_PLAY, GUI_ACTION_STOP, + GUI_ACTION_PLAY_START, GUI_ACTION_PLAY_REPEAT, GUI_ACTION_PLAY_CURSOR, GUI_ACTION_STEP_ONE, @@ -352,6 +355,7 @@ enum FurnaceGUIActions { GUI_ACTION_WINDOW_EFFECT_LIST, GUI_ACTION_WINDOW_CHAN_OSC, GUI_ACTION_WINDOW_SUBSONGS, + GUI_ACTION_WINDOW_FIND, GUI_ACTION_COLLAPSE_WINDOW, GUI_ACTION_CLOSE_WINDOW, @@ -815,7 +819,8 @@ class FurnaceGUI { String mmlStringW; bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu; - bool displayNew, fullScreen, preserveChanPos, wantScrollList; + bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly; + bool displayPendingIns, pendingInsSingle; bool willExport[32]; int vgmExportVersion; int drawHalt; @@ -871,6 +876,7 @@ class FurnaceGUI { int saaCore; int nesCore; int fdsCore; + int pcSpeakerOutMethod; String yrw801Path; String tg100Path; String mu5Path; @@ -905,10 +911,8 @@ class FurnaceGUI { int avoidRaisingPattern; int insFocusesPattern; int stepOnInsert; - // TODO flags int unifiedDataView; int sysFileDialog; - // end int roundedWindows; int roundedButtons; int roundedMenus; @@ -952,6 +956,7 @@ class FurnaceGUI { int effectCellSpacing; int effectValCellSpacing; int doubleClickColumn; + int blankIns; unsigned int maxUndoSteps; String mainFontPath; String patFontPath; @@ -972,6 +977,7 @@ class FurnaceGUI { saaCore(1), nesCore(0), fdsCore(0), + pcSpeakerOutMethod(0), yrw801Path(""), tg100Path(""), mu5Path(""), @@ -1051,6 +1057,7 @@ class FurnaceGUI { effectCellSpacing(0), effectValCellSpacing(0), doubleClickColumn(1), + blankIns(0), maxUndoSteps(100), mainFontPath(""), patFontPath(""), @@ -1067,19 +1074,13 @@ class FurnaceGUI { int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, patNameTarget, newSongCategory, latchTarget; int wheelX, wheelY; + double exportFadeOut; + bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen; bool mixerOpen, debugOpen, inspectorOpen, oscOpen, volMeterOpen, statsOpen, compatFlagsOpen; bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen; - bool subSongsOpen; - - /* there ought to be a better way... - bool editControlsDocked, ordersDocked, insListDocked, songInfoDocked, patternDocked, insEditDocked; - bool waveListDocked, waveEditDocked, sampleListDocked, sampleEditDocked, aboutDocked, settingsDocked; - bool mixerDocked, debugDocked, inspectorDocked, oscDocked, volMeterDocked, statsDocked, compatFlagsDocked; - bool pianoDocked, notesDocked, channelsDocked, regViewDocked, logDocked, effectListDocked, chanOscDocked; - bool subSongsDocked; - */ + bool subSongsOpen, findOpen; SelectionPoint selStart, selEnd, cursor; bool selecting, selectingFull, curNibble, orderNibble, followOrders, followPattern, changeAllOrders, mobileUI; @@ -1128,7 +1129,7 @@ class FurnaceGUI { std::vector activeNotes; std::vector cmdStream; std::vector particles; - std::vector pendingIns; + std::vector> pendingIns; std::vector sysCategories; @@ -1348,6 +1349,7 @@ class FurnaceGUI { void drawLog(); void drawEffectList(); void drawSubSongs(); + void drawFindReplace(); void parseKeybinds(); void promptKey(int which); diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 722c56520..80f9b5d85 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -447,6 +447,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("PLAY_TOGGLE", "Play/Stop (toggle)", SDLK_RETURN), D("PLAY", "Play", 0), D("STOP", "Stop", 0), + D("PLAY_START", "Play (from beginning)", SDLK_F5), D("PLAY_REPEAT", "Play (repeat pattern)", 0), D("PLAY_CURSOR", "Play from cursor", FURKMOD_SHIFT|SDLK_RETURN), D("STEP_ONE", "Step row", FURKMOD_CMD|SDLK_RETURN), @@ -491,6 +492,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={ D("WINDOW_SUBSONGS", "Subsongs", 0), D("EFFECT_LIST", "Effect List", 0), D("WINDOW_CHAN_OSC", "Oscilloscope (per-channel)", 0), + D("WINDOW_FIND", "Find/Replace", FURKMOD_CMD|SDLK_f), D("COLLAPSE_WINDOW", "Collapse/expand current window", 0), D("CLOSE_WINDOW", "Close current window", FURKMOD_SHIFT|SDLK_ESCAPE), diff --git a/src/gui/pattern.cpp b/src/gui/pattern.cpp index ee06a1b82..fa00243c3 100644 --- a/src/gui/pattern.cpp +++ b/src/gui/pattern.cpp @@ -320,7 +320,13 @@ void FurnaceGUI::drawPattern() { bool inhibitMenu=false; float scrollX=0; - if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) cursor.y=oldRow+((pendingStepUpdate)?1:0); + if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) { + cursor.y=oldRow+((pendingStepUpdate)?1:0); + if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && !selecting) { + selStart=cursor; + selEnd=cursor; + } + } demandX=0; sel1=selStart; sel2=selEnd; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 8176c6be6..eef892ab8 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -94,6 +94,14 @@ const char* nesCores[]={ "NSFplay" }; +const char* pcspkrOutMethods[]={ + "evdev SND_TONE", + "KIOCSOUND on /dev/tty1", + "/dev/port", + "KIOCSOUND on standard output", + "outb()" +}; + const char* valueInputStyles[]={ "Disabled/custom", "Two octaves (0 is C-4, F is D#5)", @@ -457,6 +465,11 @@ void FurnaceGUI::drawSettings() { ImGui::SetTooltip("saves power by lowering the frame rate to 2fps when idle.\nmay cause issues under Mesa drivers!"); } + bool blankInsB=settings.blankIns; + if (ImGui::Checkbox("New instruments are blank",&blankInsB)) { + settings.blankIns=blankInsB; + } + ImGui::Text("Note preview behavior:"); if (ImGui::RadioButton("Never##npb0",settings.notePreviewBehavior==0)) { settings.notePreviewBehavior=0; @@ -898,6 +911,12 @@ void FurnaceGUI::drawSettings() { ImGui::SameLine(); ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2); + ImGui::Separator(); + + ImGui::Text("PC Speaker strategy"); + ImGui::SameLine(); + ImGui::Combo("##PCSOutMethod",&settings.pcSpeakerOutMethod,pcspkrOutMethods,5); + ImGui::Separator(); ImGui::Text("Sample ROMs:"); @@ -1526,6 +1545,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_TOGGLE); UI_KEYBIND_CONFIG(GUI_ACTION_PLAY); UI_KEYBIND_CONFIG(GUI_ACTION_STOP); + UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_START); UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_REPEAT); UI_KEYBIND_CONFIG(GUI_ACTION_PLAY_CURSOR); UI_KEYBIND_CONFIG(GUI_ACTION_STEP_ONE); @@ -1937,6 +1957,7 @@ void FurnaceGUI::syncSettings() { settings.saaCore=e->getConfInt("saaCore",1); settings.nesCore=e->getConfInt("nesCore",0); settings.fdsCore=e->getConfInt("fdsCore",0); + settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0); settings.yrw801Path=e->getConfString("yrw801Path",""); settings.tg100Path=e->getConfString("tg100Path",""); settings.mu5Path=e->getConfString("mu5Path",""); @@ -2014,7 +2035,8 @@ void FurnaceGUI::syncSettings() { settings.insCellSpacing=e->getConfInt("insCellSpacing",0); settings.volCellSpacing=e->getConfInt("volCellSpacing",0); settings.effectCellSpacing=e->getConfInt("effectCellSpacing",0); - settings.doubleClickColumn=e->getConfInt("doubleClickColumn",0); + settings.doubleClickColumn=e->getConfInt("doubleClickColumn",1); + settings.blankIns=e->getConfInt("blankIns",0); clampSetting(settings.mainFontSize,2,96); clampSetting(settings.patFontSize,2,96); @@ -2029,6 +2051,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.saaCore,0,1); clampSetting(settings.nesCore,0,1); clampSetting(settings.fdsCore,0,1); + clampSetting(settings.pcSpeakerOutMethod,0,4); clampSetting(settings.mainFont,0,6); clampSetting(settings.patFont,0,6); clampSetting(settings.patRowsBase,0,1); @@ -2098,6 +2121,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.effectCellSpacing,0,32); clampSetting(settings.effectValCellSpacing,0,32); clampSetting(settings.doubleClickColumn,0,1); + clampSetting(settings.blankIns,0,1); settings.initialSys=e->decodeSysDesc(e->getConfString("initialSys","")); if (settings.initialSys.size()<4) { @@ -2150,6 +2174,7 @@ void FurnaceGUI::commitSettings() { e->setConf("saaCore",settings.saaCore); e->setConf("nesCore",settings.nesCore); e->setConf("fdsCore",settings.fdsCore); + e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod); e->setConf("yrw801Path",settings.yrw801Path); e->setConf("tg100Path",settings.tg100Path); e->setConf("mu5Path",settings.mu5Path); @@ -2230,6 +2255,7 @@ void FurnaceGUI::commitSettings() { e->setConf("effectCellSpacing",settings.effectCellSpacing); e->setConf("effectValCellSpacing",settings.effectValCellSpacing); e->setConf("doubleClickColumn",settings.doubleClickColumn); + e->setConf("blankIns",settings.blankIns); // colors for (int i=0; igetCurrentSubSong()+1,e->curSubSong->name.c_str()); } if (ImGui::BeginCombo("##SubSong",id)) { - for (size_t i=0; isong.subsong.size(); i++) { - if (e->song.subsong[i]->name.empty()) { - snprintf(id,1023,"%d. ",(int)i+1); - } else { - snprintf(id,1023,"%d. %s",(int)i+1,e->song.subsong[i]->name.c_str()); - } - if (ImGui::Selectable(id,i==e->getCurrentSubSong())) { - e->changeSongP(i); - updateScroll(0); - oldOrder=0; - oldOrder1=0; - oldRow=0; - cursor.xCoarse=0; - cursor.xFine=0; - cursor.y=0; - selStart=cursor; - selEnd=cursor; - curOrder=0; + if (ImGui::BeginTable("SubSongSelection",2)) { + ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthStretch); + ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed); + for (size_t i=0; isong.subsong.size(); i++) { + if (e->song.subsong[i]->name.empty()) { + snprintf(id,1023,"%d. ",(int)i+1); + } else { + snprintf(id,1023,"%d. %s",(int)i+1,e->song.subsong[i]->name.c_str()); + } + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Selectable(id,i==e->getCurrentSubSong())) { + e->changeSongP(i); + updateScroll(0); + oldOrder=0; + oldOrder1=0; + oldRow=0; + cursor.xCoarse=0; + cursor.xFine=0; + cursor.y=0; + selStart=cursor; + selEnd=cursor; + curOrder=0; + } + ImGui::TableNextColumn(); + ImGui::PushID(i); + if (ImGui::SmallButton(ICON_FA_ARROW_UP "##SubUp")) { + e->moveSubSongUp(i); + } + ImGui::SameLine(); + if (ImGui::SmallButton(ICON_FA_ARROW_DOWN "##SubDown")) { + e->moveSubSongDown(i); + } + ImGui::PopID(); } + ImGui::EndTable(); } ImGui::EndCombo(); } diff --git a/src/gui/sysConf.cpp b/src/gui/sysConf.cpp index 8878ca8f4..fcd01e0dd 100644 --- a/src/gui/sysConf.cpp +++ b/src/gui/sysConf.cpp @@ -445,6 +445,22 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool } break; } + case DIV_SYSTEM_MSM6258: { + ImGui::Text("Clock rate:"); + if (ImGui::RadioButton("4MHz",flags==0)) { + copyOfFlags=0; + } + if (ImGui::RadioButton("4.096MHz",flags==1)) { + copyOfFlags=1; + } + if (ImGui::RadioButton("8MHz (X68000)",flags==2)) { + copyOfFlags=2; + } + if (ImGui::RadioButton("8.192MHz",flags==3)) { + copyOfFlags=3; + } + break; + } case DIV_SYSTEM_MSM6295: { ImGui::Text("Clock rate:"); if (ImGui::RadioButton("1MHz",(flags&127)==0)) { diff --git a/src/ta-utils.h b/src/ta-utils.h index 3a619248b..feddbc1e9 100644 --- a/src/ta-utils.h +++ b/src/ta-utils.h @@ -40,6 +40,7 @@ typedef std::string String; #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) +#define CLAMP(x,xMin,xMax) (MIN(MAX((x),(xMin)),(xMax))) typedef std::wstring WString;