Add support of N163 demultiplexed output
so, there's to way for reduce N163 noises: reduce channel limit and demultiplex * channel limit is runtime changeable and it makes some usable effects with disable demultiplex * demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered) Furnace support both after this, You can choose output behavior via configuration flag.
This commit is contained in:
parent
e23dcd6e1b
commit
e6d74766ca
|
@ -156,7 +156,7 @@ const char* DivPlatformN163::getEffectName(unsigned char effect) {
|
||||||
void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
void DivPlatformN163::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
for (size_t i=start; i<start+len; i++) {
|
for (size_t i=start; i<start+len; i++) {
|
||||||
n163.tick();
|
n163.tick();
|
||||||
int out=(n163.out()<<6)*2; // scale to 16 bit
|
int out=((n163.out()<<6)*2); // scale to 16 bit
|
||||||
if (out>32767) out=32767;
|
if (out>32767) out=32767;
|
||||||
if (out<-32768) out=-32768;
|
if (out<-32768) out=-32768;
|
||||||
bufL[i]=bufR[i]=out;
|
bufL[i]=bufR[i]=out;
|
||||||
|
@ -607,6 +607,7 @@ void DivPlatformN163::reset() {
|
||||||
memset(regPool,0,128);
|
memset(regPool,0,128);
|
||||||
|
|
||||||
n163.set_disable(false);
|
n163.set_disable(false);
|
||||||
|
n163.set_multiplex(multiplex);
|
||||||
chanMax=initChanMax;
|
chanMax=initChanMax;
|
||||||
loadWave=-1;
|
loadWave=-1;
|
||||||
loadPos=0;
|
loadPos=0;
|
||||||
|
@ -636,8 +637,10 @@ void DivPlatformN163::setFlags(unsigned int flags) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
initChanMax=chanMax=(flags>>4)&7;
|
initChanMax=chanMax=(flags>>4)&7;
|
||||||
|
multiplex=((flags>>7)&1)?false:true; // not accurate in real hardware
|
||||||
chipClock=rate;
|
chipClock=rate;
|
||||||
rate/=15;
|
rate/=15;
|
||||||
|
n163.set_multiplex(multiplex);
|
||||||
rWrite(0x7f,initChanMax<<4);
|
rWrite(0x7f,initChanMax<<4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,7 @@ class DivPlatformN163: public DivDispatch {
|
||||||
unsigned char chanMax;
|
unsigned char chanMax;
|
||||||
short loadWave, loadPos, loadLen;
|
short loadWave, loadPos, loadLen;
|
||||||
unsigned char loadMode;
|
unsigned char loadMode;
|
||||||
|
bool multiplex;
|
||||||
|
|
||||||
n163_core n163;
|
n163_core n163;
|
||||||
unsigned char regPool[128];
|
unsigned char regPool[128];
|
||||||
|
|
|
@ -58,16 +58,27 @@
|
||||||
|
|
||||||
Frequency formula:
|
Frequency formula:
|
||||||
Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536)
|
Frequency: Pitch input * ((Input clock * 15 * Number of activated voices) / 65536)
|
||||||
|
|
||||||
|
There's to way for reduce N163 noises: reduce channel limit and demultiplex
|
||||||
|
- Channel limit is runtime changeable and it makes some usable effects.
|
||||||
|
- Demultiplex is used for "non-ear destroyable" emulators, but less hardware accurate. (when LPF and RF filter is not considered)
|
||||||
|
This core is support both, You can choose output behavior
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "n163.hpp"
|
#include "n163.hpp"
|
||||||
|
|
||||||
void n163_core::tick()
|
void n163_core::tick()
|
||||||
{
|
{
|
||||||
|
if (m_multiplex)
|
||||||
m_out = 0;
|
m_out = 0;
|
||||||
// 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.)
|
// 0xe000-0xe7ff Disable sound bits (bit 6, bit 0 to 5 are CPU ROM Bank 0x8000-0x9fff select.)
|
||||||
if (m_disable)
|
if (m_disable)
|
||||||
|
{
|
||||||
|
if (!m_multiplex)
|
||||||
|
m_out = 0;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// tick per each clock
|
// tick per each clock
|
||||||
const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield<u32>(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency
|
const u32 freq = m_ram[m_voice_cycle + 0] | (u32(m_ram[m_voice_cycle + 2]) << 8) | (bitfield<u32>(m_ram[m_voice_cycle + 4], 0, 2) << 16); // 18 bit frequency
|
||||||
|
@ -88,22 +99,34 @@ void n163_core::tick()
|
||||||
m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8);
|
m_ram[m_voice_cycle + 5] = bitfield(accum, 16, 8);
|
||||||
|
|
||||||
// update voice cycle
|
// update voice cycle
|
||||||
|
bool flush = m_multiplex ? true : false;
|
||||||
m_voice_cycle -= 0x8;
|
m_voice_cycle -= 0x8;
|
||||||
if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3)))
|
if (m_voice_cycle < (0x78 - (bitfield(m_ram[0x7f], 4, 3) << 3)))
|
||||||
|
{
|
||||||
|
if (!m_multiplex)
|
||||||
|
flush = true;
|
||||||
m_voice_cycle = 0x78;
|
m_voice_cycle = 0x78;
|
||||||
|
}
|
||||||
|
|
||||||
// output 4 bit waveform and volume, multiplexed
|
// output 4 bit waveform and volume, multiplexed
|
||||||
m_out = wave * volume;
|
m_acc += wave * volume;
|
||||||
|
if (flush)
|
||||||
|
{
|
||||||
|
m_out = m_acc / (m_multiplex ? 1 : (bitfield(m_ram[0x7f], 4, 3) + 1));
|
||||||
|
m_acc = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void n163_core::reset()
|
void n163_core::reset()
|
||||||
{
|
{
|
||||||
// reset this chip
|
// reset this chip
|
||||||
m_disable = false;
|
m_disable = false;
|
||||||
|
m_multiplex = true;
|
||||||
std::fill(std::begin(m_ram), std::end(m_ram), 0);
|
std::fill(std::begin(m_ram), std::end(m_ram), 0);
|
||||||
m_voice_cycle = 0x78;
|
m_voice_cycle = 0x78;
|
||||||
m_addr_latch.reset();
|
m_addr_latch.reset();
|
||||||
m_out = 0;
|
m_out = 0;
|
||||||
|
m_acc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// accessor
|
// accessor
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
|
|
||||||
// register pool
|
// register pool
|
||||||
u8 reg(u8 addr) { return m_ram[addr & 0x7f]; }
|
u8 reg(u8 addr) { return m_ram[addr & 0x7f]; }
|
||||||
|
void set_multiplex(bool multiplex = true) { m_multiplex = multiplex; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Address latch
|
// Address latch
|
||||||
|
@ -73,6 +74,9 @@ private:
|
||||||
u8 m_voice_cycle = 0x78; // Voice cycle for processing
|
u8 m_voice_cycle = 0x78; // Voice cycle for processing
|
||||||
addr_latch_t m_addr_latch; // address latch
|
addr_latch_t m_addr_latch; // address latch
|
||||||
s16 m_out = 0; // output
|
s16 m_out = 0; // output
|
||||||
|
// demultiplex related
|
||||||
|
bool m_multiplex = true; // multiplex flag, but less noisy = inaccurate!
|
||||||
|
s16 m_acc = 0; // accumulated output
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -372,6 +372,11 @@ void FurnaceGUI::drawSysConf(int i) {
|
||||||
e->setSysFlags(i,(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4),restart);
|
e->setSysFlags(i,(flags & ~(7 << 4)) | (((initialChannelLimit-1) & 7) << 4),restart);
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
} rightClickable
|
} rightClickable
|
||||||
|
bool n163Multiplex=flags&128;
|
||||||
|
if (ImGui::Checkbox("Disable Multiplexed Output",&n163Multiplex)) {
|
||||||
|
e->setSysFlags(i,(flags&(~128))|(n163Multiplex<<7),restart);
|
||||||
|
updateWindowTitle();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DIV_SYSTEM_GB:
|
case DIV_SYSTEM_GB:
|
||||||
|
|
Loading…
Reference in New Issue