Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace:
  YM2610: optimize oscilloscope fetch CPU usage
  GUI: add audio load meter to statistics
  GUI: put "OK" and "Cancel" buttons in the bottom
  better FM chip names
  sysDef refactor, part 3 - PLEASE READ
  NO
  Reduce unnecessary line
  Debug improvements
  AY8930: Fix VGM output.

# Conflicts:
#	.gitignore
#	src/engine/playback.cpp
#	src/gui/debugWindow.cpp
This commit is contained in:
cam900 2022-05-04 03:32:26 +09:00
commit 4021abe495
18 changed files with 2519 additions and 1925 deletions

View file

@ -26,8 +26,6 @@
- unified data view
- volume commands should work on Game Boy
- macro editor menu
- refactor sysDef.cpp
- effect/postEffect refactor
- add another FM editor layout
- try to find out why does VSlider not accept keyboard input
- finish lock layout
@ -36,7 +34,5 @@
- store edit/followOrders/followPattern state in config
- add ability to select a column by double clicking
- add ability to move selection by dragging
- settings: OK/Cancel buttons should be always visible
- Apply button in settings
- better FM chip names (number and codename)
- find and replace

View file

@ -71,9 +71,9 @@ afterwards everyone moved to Windows and software mixed PCM streaming...
- `56xx`: set DR of all operators.
- `57xx`: set DR of operator 1.
- `58xx`: set DR of operator 2.
- `58xx`: set DR of operator 3.
- `59xx`: set DR of operator 3.
- only in 4-op mode (OPL3).
- `58xx`: set DR of operator 4.
- `5Axx`: set DR of operator 4.
- only in 4-op mode (OPL3).
- `5Bxy`: set KSR of operator.
- `x` is the operator (1-4; last 2 operators only in 4-op mode). a value of 0 means "all operators".

View file

@ -233,15 +233,19 @@ struct DivRegWrite {
};
struct DivDispatchOscBuffer {
bool follow;
unsigned int rate;
unsigned short needle;
unsigned short readNeedle;
unsigned short followNeedle;
short data[65536];
DivDispatchOscBuffer():
follow(true),
rate(65536),
needle(0),
readNeedle(0) {
readNeedle(0),
followNeedle(0) {
memset(data,0,65536*sizeof(short));
}
};

View file

@ -26,6 +26,7 @@
#include "safeWriter.h"
#include "../audio/taAudio.h"
#include "blip_buf.h"
#include <atomic>
#include <functional>
#include <initializer_list>
#include <thread>
@ -208,8 +209,8 @@ struct DivSysDef {
std::initializer_list<int> chTypes,
std::initializer_list<DivInstrumentType> chInsType1,
std::initializer_list<DivInstrumentType> chInsType2={},
EffectProcess fxHandler=NULL,
EffectProcess postFxHandler=NULL):
EffectProcess fxHandler=[](int,unsigned char,unsigned char) -> bool {return false;},
EffectProcess postFxHandler=[](int,unsigned char,unsigned char) -> bool {return false;}):
name(sysName),
nameJ(sysNameJ),
id(fileID),
@ -418,6 +419,7 @@ class DivEngine {
float oscSize;
int oscReadPos, oscWritePos;
int tickMult;
std::atomic<size_t> processTime;
void runExportThread();
void nextBuf(float** in, float** out, int inChans, int outChans, unsigned int size);
@ -925,7 +927,8 @@ class DivEngine {
oscSize(1),
oscReadPos(0),
oscWritePos(0),
tickMult(1) {
tickMult(1),
processTime(0) {
memset(isMuted,0,DIV_MAX_CHANS*sizeof(bool));
memset(keyHit,0,DIV_MAX_CHANS*sizeof(bool));
memset(dispatchChanOfChan,0,DIV_MAX_CHANS*sizeof(int));

View file

@ -25,7 +25,7 @@
#include <math.h>
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define immWrite2(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
#define CHIP_DIVIDER 8
@ -61,6 +61,18 @@ const char* regCheatSheetAY8930[]={
NULL
};
void DivPlatformAY8930::immWrite(unsigned char a, unsigned char v) {
if ((int)bank!=(a>>4)) {
bank=a>>4;
immWrite2(0x0d, 0xa0|(bank<<4)|ayEnvMode[0]);
}
if (a==0x0d) {
immWrite2(0x0d,0xa0|(bank<<4)|(v&15));
} else {
immWrite2(a&15,v);
}
}
const char** DivPlatformAY8930::getRegisterSheet() {
return regCheatSheetAY8930;
}
@ -123,18 +135,13 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l
}
while (!writes.empty()) {
QueuedWrite w=writes.front();
if ((int)bank!=(w.addr>>4)) {
bank=w.addr>>4;
ay->address_w(0x0d);
ay->data_w(0xa0|(bank<<4)|ayEnvMode[0]);
}
ay->address_w(w.addr&15);
if (w.addr==0x0d) {
ay->data_w(0xa0|(bank<<4)|(w.val&15));
} else {
ay->address_w(w.addr);
ay->data_w(w.val);
if (w.addr!=0x0d && (regPool[0x0d]&0xf0)==0xb0) {
regPool[(w.addr&0x0f)|0x10]=w.val;
} else {
regPool[w.addr&0x0f]=w.val;
}
regPool[w.addr&0x1f]=w.val;
writes.pop();
}
ay->sound_stream_update(ayBuf,len);

View file

@ -73,6 +73,7 @@ class DivPlatformAY8930: public DivDispatch {
size_t ayBufLen;
void updateOutSel(bool immediate=false);
void immWrite(unsigned char a, unsigned char v);
friend void putDispatchChan(void*,int,int);

View file

@ -269,7 +269,7 @@ void DivPlatformES5506::tick(bool sysTick) {
signed int k1=chan[i].k1Prev,k2=chan[i].k2Prev;
// volume/panning macros
if (chan[i].std.vol.had) {
const unsigned int nextVol=((chan[i].vol&0xff)*MIN(0xffff,chan[i].std.vol.val))/0xff;
const unsigned int nextVol=((chan[i].vol&0xff)*MIN(0xffff,(0xffff*chan[i].std.vol.val)/chan[i].volMacroMax))/0xff;
if (chan[i].outVol!=nextVol) {
chan[i].outVol=nextVol;
if (!isMuted[i]) {
@ -278,7 +278,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
if (chan[i].std.panL.had) {
const unsigned int nextLVol=(((ins->es5506.lVol*(chan[i].lVol&0xff))/0xff)*MIN(0xffff,chan[i].std.panL.val))/0xffff;
const unsigned int nextLVol=(((ins->es5506.lVol*(chan[i].lVol&0xff))/0xff)*MIN(0xffff,(0xffff*chan[i].std.panL.val)/chan[i].panMacroMax))/0xffff;
if (chan[i].outLVol!=nextLVol) {
chan[i].outLVol=nextLVol;
if (!isMuted[i]) {
@ -287,7 +287,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
if (chan[i].std.panR.had) {
const unsigned int nextRVol=(((ins->es5506.rVol*(chan[i].rVol&0xff))/0xff)*MIN(0xffff,chan[i].std.panR.val))/0xffff;
const unsigned int nextRVol=(((ins->es5506.rVol*(chan[i].rVol&0xff))/0xff)*MIN(0xffff,(0xffff*chan[i].std.panR.val)/chan[i].panMacroMax))/0xffff;
if (chan[i].outRVol!=nextRVol) {
chan[i].outRVol=nextRVol;
if (!isMuted[i]) {
@ -644,11 +644,22 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].pcm.length=length;
chan[c.chan].pcm.loopStart=(start+(s->loopStart<<11))&0xfffff800;
chan[c.chan].pcm.loopEnd=(start+((s->loopEnd-1)<<11))&0xffffff80;
if (ins->type==DIV_INS_ES5506) { // Native format
chan[c.chan].volMacroMax=0xffff;
chan[c.chan].panMacroMax=0xffff;
chan[c.chan].filter=ins->es5506.filter;
chan[c.chan].envelope=ins->es5506.envelope;
} else { // Amiga format
chan[c.chan].volMacroMax=64;
chan[c.chan].panMacroMax=127;
chan[c.chan].filter=DivInstrumentES5506::Filter();
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
}
} else {
chan[c.chan].sample=-1;
chan[c.chan].pcm.index=-1;
chan[c.chan].volMacroMax=0xffff;
chan[c.chan].panMacroMax=0xffff;
chan[c.chan].filter=DivInstrumentES5506::Filter();
chan[c.chan].envelope=DivInstrumentES5506::Envelope();
}

View file

@ -56,6 +56,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
} pcm;
int freq, baseFreq, pitch, pitch2, note, ins, sample, wave;
unsigned int volMacroMax, panMacroMax;
bool active, insChanged, freqChanged, pcmChanged, keyOn, keyOff, inPorta, useWave, isReverseLoop;
struct VolChanged { // Volume changed flags
@ -135,6 +136,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
ins(-1),
sample(-1),
wave(-1),
volMacroMax(0xffff),
panMacroMax(0xffff),
active(false),
insChanged(true),
freqChanged(false),

View file

@ -220,7 +220,7 @@ bool adpcm_a_channel::clock()
//-------------------------------------------------
template<int NumOutputs>
void adpcm_a_channel::output(ymfm_output<NumOutputs> &output) const
void adpcm_a_channel::output(ymfm_output<NumOutputs> &output)
{
// volume combines instrument and total levels
int vol = (m_regs.ch_instrument_level(m_choffs) ^ 0x1f) + (m_regs.total_level() ^ 0x3f);
@ -239,14 +239,18 @@ void adpcm_a_channel::output(ymfm_output<NumOutputs> &output) const
int16_t value = ((int16_t(m_accumulator << 4) * mul) >> shift) & ~3;
// apply to left/right as appropriate
if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs))
if (NumOutputs == 1 || m_regs.ch_pan_left(m_choffs)) {
output.data[0] += value;
if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs))
m_lastOut[0] = value;
}
if (NumOutputs > 1 && m_regs.ch_pan_right(m_choffs)) {
output.data[1] += value;
m_lastOut[1] = value;
}
}
template void adpcm_a_channel::output<1>(ymfm_output<1> &output) const;
template void adpcm_a_channel::output<2>(ymfm_output<2> &output) const;
template void adpcm_a_channel::output<1>(ymfm_output<1> &output);
template void adpcm_a_channel::output<2>(ymfm_output<2> &output);
//*********************************************************
@ -528,7 +532,7 @@ void adpcm_b_channel::clock()
//-------------------------------------------------
template<int NumOutputs>
void adpcm_b_channel::output(ymfm_output<NumOutputs> &output, uint32_t rshift) const
void adpcm_b_channel::output(ymfm_output<NumOutputs> &output, uint32_t rshift)
{
// mask out some channels for debug purposes
if ((debug::GLOBAL_ADPCM_B_CHANNEL_MASK & 1) == 0)
@ -541,10 +545,14 @@ void adpcm_b_channel::output(ymfm_output<NumOutputs> &output, uint32_t rshift) c
result = (result * int32_t(m_regs.level())) >> (8 + rshift);
// apply to left/right
if (NumOutputs == 1 || m_regs.pan_left())
if (NumOutputs == 1 || m_regs.pan_left()) {
m_lastOut[0] = result;
output.data[0] += result;
if (NumOutputs > 1 && m_regs.pan_right())
}
if (NumOutputs > 1 && m_regs.pan_right()) {
m_lastOut[1] = result;
output.data[1] += result;
}
}

View file

@ -146,7 +146,10 @@ public:
// return the computed output value, with panning applied
template<int NumOutputs>
void output(ymfm_output<NumOutputs> &output) const;
void output(ymfm_output<NumOutputs> &output);
// return the last output
int32_t get_last_out(int ch) { return m_lastOut[ch]; }
private:
// internal state
@ -158,6 +161,7 @@ private:
uint32_t m_curaddress; // current address
int32_t m_accumulator; // accumulator
int32_t m_step_index; // index in the stepping table
int32_t m_lastOut[2]; // last output
adpcm_a_registers &m_regs; // reference to registers
adpcm_a_engine &m_owner; // reference to our owner
};
@ -326,7 +330,7 @@ public:
// return the computed output value, with panning applied
template<int NumOutputs>
void output(ymfm_output<NumOutputs> &output, uint32_t rshift) const;
void output(ymfm_output<NumOutputs> &output, uint32_t rshift);
// return the status register
uint8_t status() const { return m_status; }
@ -337,6 +341,9 @@ public:
// handle special register writes
void write(uint32_t regnum, uint8_t value);
// return the last output
int32_t get_last_out(int ch) { return m_lastOut[ch]; }
private:
// helper - return the current address shift
uint32_t address_shift() const;
@ -361,6 +368,7 @@ private:
int32_t m_accumulator; // accumulator
int32_t m_prev_accum; // previous accumulator (for linear interp)
int32_t m_adpcm_step; // next forecast
int32_t m_lastOut[2]; // last output
adpcm_b_registers &m_regs; // reference to registers
adpcm_b_engine &m_owner; // reference to our owner
};
@ -387,6 +395,9 @@ public:
template<int NumOutputs>
void output(ymfm_output<NumOutputs> &output, uint32_t rshift);
// get last output
int32_t get_last_out(int ch) { return m_channel->get_last_out(ch); }
// read from the ADPCM-B registers
uint32_t read(uint32_t regnum) { return m_channel->read(regnum); }

View file

@ -428,7 +428,15 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l
ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine();
ymfm::ssg_engine::output_data ssgOut;
ymfm::ymfm_output<2> adpcmOut;
ymfm::fm_channel<ymfm::opn_registers_base<true>>* fmChan[6];
ymfm::adpcm_a_channel* adpcmAChan[6];
for (int i=0; i<4; i++) {
fmChan[i]=fme->debug_channel(bchOffs[i]);
}
for (int i=0; i<6; i++) {
adpcmAChan[i]=aae->debug_channel(i);
}
for (size_t h=start; h<start+len; h++) {
os[0]=0; os[1]=0;
@ -457,8 +465,7 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l
bufR[h]=os[1];
for (int i=0; i<4; i++) {
int ch=bchOffs[i];
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(ch)->debug_output(0)+fme->debug_channel(ch)->debug_output(1));
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
}
ssge->get_last_out(ssgOut);
@ -467,14 +474,10 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l
}
for (int i=7; i<13; i++) {
adpcmOut.clear();
aae->debug_channel(i-7)->output<2>(adpcmOut);
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1];
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-7]->get_last_out(0)+adpcmAChan[i-7]->get_last_out(1);
}
adpcmOut.clear();
abe->output(adpcmOut,1);
oscBuf[13]->data[oscBuf[13]->needle++]=adpcmOut.data[0]+adpcmOut.data[1];
oscBuf[13]->data[oscBuf[13]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
}
}

View file

@ -408,7 +408,13 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t
ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine();
ymfm::ssg_engine::output_data ssgOut;
ymfm::ymfm_output<2> adpcmOut;
ymfm::fm_channel<ymfm::opn_registers_base<true>>* fmChan[6];
ymfm::adpcm_a_channel* adpcmAChan[6];
for (int i=0; i<6; i++) {
fmChan[i]=fme->debug_channel(i);
adpcmAChan[i]=aae->debug_channel(i);
}
for (size_t h=start; h<start+len; h++) {
os[0]=0; os[1]=0;
@ -436,8 +442,9 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t
bufL[h]=os[0];
bufR[h]=os[1];
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));
oscBuf[i]->data[oscBuf[i]->needle++]=(fmChan[i]->debug_output(0)+fmChan[i]->debug_output(1));
}
ssge->get_last_out(ssgOut);
@ -446,14 +453,10 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t
}
for (int i=9; i<15; i++) {
adpcmOut.clear();
aae->debug_channel(i-9)->output<2>(adpcmOut);
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmOut.data[0]+adpcmOut.data[1];
oscBuf[i]->data[oscBuf[i]->needle++]=adpcmAChan[i-9]->get_last_out(0)+adpcmAChan[i-9]->get_last_out(1);
}
adpcmOut.clear();
abe->output(adpcmOut,1);
oscBuf[15]->data[oscBuf[15]->needle++]=adpcmOut.data[0]+adpcmOut.data[1];
oscBuf[15]->data[oscBuf[15]->needle++]=abe->get_last_out(0)+abe->get_last_out(1);
}
}

View file

@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <chrono>
#define _USE_MATH_DEFINES
#include "dispatch.h"
#include "engine.h"
@ -262,6 +263,7 @@ int DivEngine::dispatchCmd(DivCommand c) {
}
bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effectVal) {
<<<<<<< HEAD
switch (sysOfChan[ch]) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
@ -599,391 +601,15 @@ bool DivEngine::perSystemEffect(int ch, unsigned char effect, unsigned char effe
return false;
}
return true;
=======
if (sysDefs[sysOfChan[ch]]==NULL) return false;
return sysDefs[sysOfChan[ch]]->effectFunc(ch,effect,effectVal);
>>>>>>> 4c9b172b50a240efc37aec8bdd4a59972fbf10c4
}
#define IS_YM2610 (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT)
#define IS_OPM_LIKE (sysOfChan[ch]==DIV_SYSTEM_YM2151 || sysOfChan[ch]==DIV_SYSTEM_OPZ)
bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char effectVal) {
switch (sysOfChan[ch]) {
case DIV_SYSTEM_YM2612:
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2151:
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_EXT:
case DIV_SYSTEM_OPZ:
switch (effect) {
case 0x10: // LFO or noise mode
if (IS_OPM_LIKE) {
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
} else {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
}
break;
case 0x11: // FB
dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7));
break;
case 0x12: // TL op1
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x7f));
break;
case 0x13: // TL op2
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x7f));
break;
case 0x14: // TL op3
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,2,effectVal&0x7f));
break;
case 0x15: // TL op4
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,3,effectVal&0x7f));
break;
case 0x16: // MULT
if ((effectVal>>4)>0 && (effectVal>>4)<5) {
dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15));
}
break;
case 0x17: // arcade LFO
if (IS_OPM_LIKE) {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
}
break;
case 0x18: // EXT or LFO waveform
if (IS_OPM_LIKE) {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal));
} else {
dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal));
}
break;
case 0x19: // AR global
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31));
break;
case 0x1a: // AR op1
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&31));
break;
case 0x1b: // AR op2
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31));
break;
case 0x1c: // AR op3
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,2,effectVal&31));
break;
case 0x1d: // AR op4
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&31));
break;
case 0x1e: // UNOFFICIAL: Arcade AM depth
dispatchCmd(DivCommand(DIV_CMD_FM_AM_DEPTH,ch,effectVal&127));
break;
case 0x1f: // UNOFFICIAL: Arcade PM depth
dispatchCmd(DivCommand(DIV_CMD_FM_PM_DEPTH,ch,effectVal&127));
break;
case 0x20: // Neo Geo PSG mode
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
}
break;
case 0x21: // Neo Geo PSG noise freq
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
}
break;
case 0x22: // UNOFFICIAL: Neo Geo PSG envelope enable
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal));
}
break;
case 0x23: // UNOFFICIAL: Neo Geo PSG envelope period low
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal));
}
break;
case 0x24: // UNOFFICIAL: Neo Geo PSG envelope period high
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal));
}
break;
case 0x25: // UNOFFICIAL: Neo Geo PSG envelope slide up
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal));
}
break;
case 0x26: // UNOFFICIAL: Neo Geo PSG envelope slide down
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal));
}
break;
case 0x29: // auto-envelope
if (IS_YM2610) {
dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal));
}
break;
default:
return false;
}
break;
case DIV_SYSTEM_OPLL:
case DIV_SYSTEM_OPLL_DRUMS:
case DIV_SYSTEM_VRC7:
switch (effect) {
case 0x11: // FB
dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7));
break;
case 0x12: // TL op1
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x3f));
break;
case 0x13: // TL op2
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x0f));
break;
case 0x16: // MULT
if ((effectVal>>4)>0 && (effectVal>>4)<3) {
dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15));
}
break;
case 0x19: // AR global
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31));
break;
case 0x1a: // AR op1
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&31));
break;
case 0x1b: // AR op2
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&31));
break;
default:
return false;
}
break;
case DIV_SYSTEM_OPL:
case DIV_SYSTEM_OPL2:
case DIV_SYSTEM_OPL3:
case DIV_SYSTEM_OPL_DRUMS:
case DIV_SYSTEM_OPL2_DRUMS:
case DIV_SYSTEM_OPL3_DRUMS:
switch (effect) {
case 0x10: // DAM
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal&1));
break;
case 0x11: // FB
dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7));
break;
case 0x12: // TL op1
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,0,effectVal&0x3f));
break;
case 0x13: // TL op2
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,1,effectVal&0x3f));
break;
case 0x14: // TL op3
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,2,effectVal&0x3f));
break;
case 0x15: // TL op4
dispatchCmd(DivCommand(DIV_CMD_FM_TL,ch,3,effectVal&0x3f));
break;
case 0x16: // MULT
if ((effectVal>>4)>0 && (effectVal>>4)<5) {
dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15));
}
break;
case 0x17: // DVB
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,2+(effectVal&1)));
break;
case 0x19: // AR global
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&15));
break;
case 0x1a: // AR op1
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,0,effectVal&15));
break;
case 0x1b: // AR op2
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,1,effectVal&15));
break;
case 0x1c: // AR op3
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,2,effectVal&15));
break;
case 0x1d: // AR op4
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&15));
break;
default:
return false;
}
break;
case DIV_SYSTEM_C64_6581: case DIV_SYSTEM_C64_8580:
switch (effect) {
case 0x10: // select waveform
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
break;
case 0x11: // cutoff
dispatchCmd(DivCommand(DIV_CMD_C64_CUTOFF,ch,effectVal));
break;
case 0x12: // duty
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
break;
case 0x13: // resonance
dispatchCmd(DivCommand(DIV_CMD_C64_RESONANCE,ch,effectVal));
break;
case 0x14: // filter mode
dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_MODE,ch,effectVal));
break;
case 0x15: // reset time
dispatchCmd(DivCommand(DIV_CMD_C64_RESET_TIME,ch,effectVal));
break;
case 0x1a: // reset mask
dispatchCmd(DivCommand(DIV_CMD_C64_RESET_MASK,ch,effectVal));
break;
case 0x1b: // cutoff reset
dispatchCmd(DivCommand(DIV_CMD_C64_FILTER_RESET,ch,effectVal));
break;
case 0x1c: // duty reset
dispatchCmd(DivCommand(DIV_CMD_C64_DUTY_RESET,ch,effectVal));
break;
case 0x1e: // extended
dispatchCmd(DivCommand(DIV_CMD_C64_EXTENDED,ch,effectVal));
break;
case 0x30: case 0x31: case 0x32: case 0x33:
case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b:
case 0x3c: case 0x3d: case 0x3e: case 0x3f: // fine duty
dispatchCmd(DivCommand(DIV_CMD_C64_FINE_DUTY,ch,((effect&0x0f)<<8)|effectVal));
break;
case 0x40: case 0x41: case 0x42: case 0x43:
case 0x44: case 0x45: case 0x46: case 0x47: // fine cutoff
dispatchCmd(DivCommand(DIV_CMD_C64_FINE_CUTOFF,ch,((effect&0x07)<<8)|effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_AY8910:
case DIV_SYSTEM_AY8930:
switch (effect) {
case 0x12: // duty on 8930
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,0x10+(effectVal&15)));
break;
case 0x20: // mode
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal&15));
break;
case 0x21: // noise freq
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
break;
case 0x22: // envelope enable
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal));
break;
case 0x23: // envelope period low
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal));
break;
case 0x24: // envelope period high
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal));
break;
case 0x25: // envelope slide up
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal));
break;
case 0x26: // envelope slide down
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal));
break;
case 0x27: // noise and mask
dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_AND,ch,effectVal));
break;
case 0x28: // noise or mask
dispatchCmd(DivCommand(DIV_CMD_AY_NOISE_MASK_OR,ch,effectVal));
break;
case 0x29: // auto-envelope
dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal));
break;
case 0x2d: // TEST
dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,255,effectVal));
break;
case 0x2e: // I/O port A
dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,0,effectVal));
break;
case 0x2f: // I/O port B
dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,1,effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_SAA1099:
switch (effect) {
case 0x10: // select channel mode
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
break;
case 0x11: // set noise freq
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
break;
case 0x12: // setup envelope
dispatchCmd(DivCommand(DIV_CMD_SAA_ENVELOPE,ch,effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_TIA:
switch (effect) {
case 0x10: // select waveform
dispatchCmd(DivCommand(DIV_CMD_WAVE,ch,effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_AMIGA:
switch (effect) {
case 0x10: // toggle filter
dispatchCmd(DivCommand(DIV_CMD_AMIGA_FILTER,ch,effectVal));
break;
case 0x11: // toggle AM
dispatchCmd(DivCommand(DIV_CMD_AMIGA_AM,ch,effectVal));
break;
case 0x12: // toggle PM
dispatchCmd(DivCommand(DIV_CMD_AMIGA_PM,ch,effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_SEGAPCM:
case DIV_SYSTEM_SEGAPCM_COMPAT:
switch (effect) {
case 0x20: // PCM frequency
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
break;
default:
return false;
}
break;
case DIV_SYSTEM_LYNX:
if (effect>=0x30 && effect<0x40) {
int value = ((int)(effect&0x0f)<<8)|effectVal;
dispatchCmd(DivCommand(DIV_CMD_LYNX_LFSR_LOAD,ch,value));
break;
}
return false;
break;
case DIV_SYSTEM_X1_010:
switch (effect) {
case 0x20: // PCM frequency
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
break;
case 0x22: // envelope mode
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_MODE,ch,effectVal));
break;
case 0x23: // envelope period
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_PERIOD,ch,effectVal));
break;
case 0x25: // envelope slide up
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,effectVal));
break;
case 0x26: // envelope slide down
dispatchCmd(DivCommand(DIV_CMD_X1_010_ENVELOPE_SLIDE,ch,-effectVal));
break;
case 0x29: // auto-envelope
dispatchCmd(DivCommand(DIV_CMD_X1_010_AUTO_ENVELOPE,ch,effectVal));
break;
default:
return false;
}
break;
default:
return false;
}
return true;
if (sysDefs[sysOfChan[ch]]==NULL) return false;
return sysDefs[sysOfChan[ch]]->postEffectFunc(ch,effect,effectVal);
}
void DivEngine::processRow(int i, bool afterDelay) {
@ -1787,6 +1413,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
got.bufsize=size;
std::chrono::steady_clock::time_point ts_processBegin=std::chrono::steady_clock::now();
// process MIDI events (TODO: everything)
if (output) if (output->midiIn) while (!output->midiIn->queue.empty()) {
TAMidiMessage& msg=output->midiIn->queue.front();
@ -2150,4 +1778,8 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
}
}
isBusy.unlock();
std::chrono::steady_clock::time_point ts_processEnd=std::chrono::steady_clock::now();
processTime=std::chrono::duration_cast<std::chrono::nanoseconds>(ts_processEnd-ts_processBegin).count();
}

File diff suppressed because it is too large Load diff

View file

@ -345,6 +345,8 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::Text("- ins: %d",ch->ins);
ImGui::Text("- sample: %d",ch->sample);
ImGui::Text("- wave: %d",ch->wave);
ImGui::Text("- VolMacroMax: %d",ch->volMacroMax);
ImGui::Text("- PanMacroMax: %d",ch->panMacroMax);
ImGui::Text("* PCM:");
ImGui::Text(" - index: %d",ch->pcm.index);
ImGui::Text(" - freqOffs: %.6f",ch->pcm.freqOffs);

View file

@ -149,8 +149,7 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("%d: <NULL!>",i);
continue;
}
ImGui::Text("%d: %s",i,sample->name.c_str());
ImGui::Indent();
if (ImGui::TreeNode(fmt::sprintf("%d: %s",i,sample->name).c_str())) {
ImGui::Text("rate: %d",sample->rate);
ImGui::Text("centerRate: %d",sample->centerRate);
ImGui::Text("loopStart: %d",sample->loopStart);
@ -184,7 +183,61 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("offSU: %x",sample->offSU);
ImGui::Text("samples: %d",sample->samples);
ImGui::Unindent();
ImGui::TreePop();
}
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Oscilloscope Debug")) {
int c=0;
for (int i=0; i<e->song.systemLen; i++) {
DivSystem system=e->song.system[i];
if (e->getChannelCount(system)>0) {
if (ImGui::TreeNode(fmt::sprintf("%d: %s",i,e->getSystemName(system)).c_str())) {
if (ImGui::BeginTable("OscilloscopeTable",4,ImGuiTableFlags_Borders|ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Channel");
ImGui::TableNextColumn();
ImGui::Text("Follow");
ImGui::TableNextColumn();
ImGui::Text("Address");
ImGui::TableNextColumn();
ImGui::Text("Data");
for (int j=0; j<e->getChannelCount(system); j++, c++) {
ImGui::TableNextRow();
// channel
ImGui::TableNextColumn();
ImGui::Text("%d",j);
// follow
ImGui::TableNextColumn();
ImGui::Checkbox(fmt::sprintf("##%d_OSCFollow_%d",i,c).c_str(),&e->getOscBuffer(c)->follow);
// address
ImGui::TableNextColumn();
int needle=e->getOscBuffer(c)->follow?e->getOscBuffer(c)->needle:e->getOscBuffer(c)->followNeedle;
ImGui::BeginDisabled(e->getOscBuffer(c)->follow);
if (ImGui::InputInt(fmt::sprintf("##%d_OSCFollowNeedle_%d",i,c).c_str(),&needle,1,100)) {
e->getOscBuffer(c)->followNeedle=MIN(MAX(needle,0),65535);
}
ImGui::EndDisabled();
// data
ImGui::TableNextColumn();
ImGui::Text("%d",e->getOscBuffer(c)->data[needle]);
}
ImGui::EndTable();
}
ImGui::TreePop();
}
} else {
ImGui::Text("%d: <NULL!>",i);
continue;
}
}
ImGui::TreePop();
}

View file

@ -225,6 +225,9 @@ void FurnaceGUI::drawSettings() {
}
if (ImGui::BeginTabBar("settingsTab")) {
if (ImGui::BeginTabItem("General")) {
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
ImGui::Text("Workspace layout:");
ImGui::SameLine();
if (ImGui::Button("Import")) {
@ -498,10 +501,14 @@ void FurnaceGUI::drawSettings() {
if (ImGui::RadioButton("Move to effect value/next effect and wrap around##eicb2",settings.effectCursorDir==2)) {
settings.effectCursorDir=2;
}
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Audio/MIDI")) {
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
ImGui::Text("Backend");
ImGui::SameLine();
ImGui::Combo("##Backend",&settings.audioEngine,audioBackends,2);
@ -848,10 +855,14 @@ void FurnaceGUI::drawSettings() {
ImGui::TreePop();
}
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Emulation")) {
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
ImGui::Text("Arcade/YM2151 core");
ImGui::SameLine();
ImGui::Combo("##ArcadeCore",&settings.arcadeCore,arcadeCores,2);
@ -871,10 +882,14 @@ void FurnaceGUI::drawSettings() {
ImGui::Text("FDS core");
ImGui::SameLine();
ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2);
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Appearance")) {
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
bool dpiScaleAuto=(settings.dpiScale<0.5f);
if (ImGui::Checkbox("Automatic UI scaling factor",&dpiScaleAuto)) {
if (dpiScaleAuto) {
@ -1360,10 +1375,14 @@ void FurnaceGUI::drawSettings() {
}
ImGui::TreePop();
}
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Keyboard")) {
ImVec2 settingsViewSize=ImGui::GetContentRegionAvail();
settingsViewSize.y-=ImGui::GetFrameHeight()+ImGui::GetStyle().WindowPadding.y;
if (ImGui::BeginChild("SettingsView",settingsViewSize)) {
if (ImGui::Button("Import")) {
openFileDialog(GUI_FILE_IMPORT_KEYBINDS);
}
@ -1703,6 +1722,8 @@ void FurnaceGUI::drawSettings() {
KEYBIND_CONFIG_END;
ImGui::TreePop();
}
}
ImGui::EndChild();
ImGui::EndTabItem();
}
ImGui::EndTabBar();

View file

@ -19,6 +19,7 @@
#include "gui.h"
#include <fmt/printf.h>
#include <imgui.h>
void FurnaceGUI::drawStats() {
if (nextWindow==GUI_WINDOW_STATS) {
@ -28,6 +29,13 @@ void FurnaceGUI::drawStats() {
}
if (!statsOpen) return;
if (ImGui::Begin("Statistics",&statsOpen)) {
size_t lastProcTime=e->processTime;
double maxGot=1000000000.0*(double)e->getAudioDescGot().bufsize/(double)e->getAudioDescGot().rate;
String procStr=fmt::sprintf("%.1f%%",100.0*((double)lastProcTime/(double)maxGot));
ImGui::Text("Audio load");
ImGui::SameLine();
ImGui::ProgressBar((double)lastProcTime/maxGot,ImVec2(-FLT_MIN,0),procStr.c_str());
ImGui::Separator();
for (int i=0; i<e->song.systemLen; i++) {
DivDispatch* dispatch=e->getDispatch(i);
for (int j=0; dispatch!=NULL && dispatch->getSampleMemCapacity(j)>0; j++) {