#include "headers.h"
#include "misc.h"
#include "opna.h"
#include "fmgeninl.h"
#define BUILD_OPN
#define BUILD_OPNA
#define BUILD_OPNB
#define BUILD_OPN2
#ifdef BUILD_OPNA
#include "file.h"
#endif
namespace FM
{
#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) || defined (BUILD_OPN2)
uint32 OPNBase::lfotable[8];
OPNBase::OPNBase()
{
prescale = 0;
}
void OPNBase::SetParameter(Channel4* ch, uint addr, uint data)
{
const static uint slottable[4] = { 0, 2, 1, 3 };
const static uint8 sltable[16] =
{
0, 4, 8, 12, 16, 20, 24, 28,
32, 36, 40, 44, 48, 52, 56, 124,
};
if ((addr & 3) < 3)
{
uint slot = slottable[(addr >> 2) & 3];
Operator* op = &ch->op[slot];
switch ((addr >> 4) & 15)
{
case 3: op->SetDT((data >> 4) & 0x07);
op->SetMULTI(data & 0x0f);
break;
case 4: op->SetTL(data & 0x7f, (regtc & 0x80) && (csmch == ch));
break;
case 5: op->SetKS((data >> 6) & 3);
op->SetAR((data & 0x1f) * 2);
break;
case 6: op->SetDR((data & 0x1f) * 2);
op->SetAMON((data & 0x80) != 0);
break;
case 7: op->SetSR((data & 0x1f) * 2);
break;
case 8: op->SetSL(sltable[(data >> 4) & 15]);
op->SetRR((data & 0x0f) * 4 + 2);
break;
case 9: op->SetSSGEC(data & 0x0f);
break;
}
}
}
void OPNBase::Reset()
{
status = 0;
SetPrescaler(0);
Timer::Reset();
psg.Reset();
}
void OPNBase::SetPrescaler(uint p)
{
static const char table[3][2] = { { 6, 4 }, { 3, 2 }, { 2, 1 } };
static const uint8 table2[8] = { 108, 77, 71, 67, 62, 44, 8, 5 };
if (prescale != p)
{
prescale = p;
assert(0 <= prescale && prescale < 3);
uint fmclock = clock / table[p][0] / 12;
rate = psgrate;
assert(fmclock < (0x80000000 >> FM_RATIOBITS));
uint ratio = ((fmclock << FM_RATIOBITS) + rate/2) / rate;
SetTimerBase(fmclock);
chip.SetRatio(ratio);
psg.SetClock(clock / table[p][1], psgrate);
for (int i=0; i<8; i++)
{
lfotable[i] = (ratio << (2+FM_LFOCBITS-FM_RATIOBITS)) / table2[i];
}
}
}
bool OPNBase::Init(uint c, uint r)
{
clock = c;
psgrate = r;
return true;
}
void OPNBase::SetVolumeFM(int db)
{
db = Min(db, 20);
if (db > -192)
fmvolume = int(16384.0 * pow(10.0, db / 40.0));
else
fmvolume = 0;
}
void OPNBase::TimerA()
{
if (regtc & 0x80)
{
csmch->KeyControl(0x00);
csmch->KeyControl(0x0f);
}
}
#endif
#ifdef BUILD_OPN
OPN::OPN()
{
SetVolumeFM(0);
SetVolumePSG(0);
csmch = &ch[2];
for (int i=0; i<3; i++)
{
ch[i].SetChip(&chip);
ch[i].SetType(typeN);
}
}
bool OPN::Init(uint c, uint r, bool ip, const char*)
{
if (!SetRate(c, r, ip))
return false;
Reset();
SetVolumeFM(0);
SetVolumePSG(0);
SetChannelMask(0);
SetPan(0xff);
return true;
}
bool OPN::SetRate(uint c, uint r, bool)
{
OPNBase::Init(c, r);
RebuildTimeTable();
return true;
}
void OPN::Reset()
{
int i;
for (i=0x20; i<0x28; i++) SetReg(i, 0);
for (i=0x30; i<0xc0; i++) SetReg(i, 0);
OPNBase::Reset();
ch[0].Reset();
ch[1].Reset();
ch[2].Reset();
}
uint OPN::GetReg(uint addr)
{
if (addr < 0x10)
return psg.GetReg(addr);
else
return 0;
}
void OPN::SetReg(uint addr, uint data)
{
if (addr >= 0x100)
return;
int c = addr & 3;
switch (addr)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
psg.SetReg(addr, data);
break;
case 0x24: case 0x25:
SetTimerA(addr, data);
break;
case 0x26:
SetTimerB(data);
break;
case 0x27:
SetTimerControl(data);
break;
case 0x28: if ((data & 3) < 3)
ch[data & 3].KeyControl(data >> 4);
break;
case 0x2d: case 0x2e: case 0x2f:
SetPrescaler(addr-0x2d);
break;
case 0xa0: case 0xa1: case 0xa2:
fnum[c] = data + fnum2[c] * 0x100;
break;
case 0xa4: case 0xa5: case 0xa6:
fnum2[c] = uint8(data);
break;
case 0xa8: case 0xa9: case 0xaa:
fnum3[c] = data + fnum2[c+3] * 0x100;
break;
case 0xac: case 0xad: case 0xae:
fnum2[c+3] = uint8(data);
break;
case 0xb0: case 0xb1: case 0xb2:
ch[c].SetFB((data >> 3) & 7);
ch[c].SetAlgorithm(data & 7);
break;
default:
if (c < 3)
{
if ((addr & 0xf0) == 0x60)
data &= 0x1f;
OPNBase::SetParameter(&ch[c], addr, data);
}
break;
}
}
void OPN::SetStatus(uint bits)
{
if (!(status & bits))
{
status |= bits;
Intr(true);
}
}
void OPN::ResetStatus(uint bit)
{
status &= ~bit;
if (!status)
Intr(false);
}
void OPN::SetChannelMask(uint mask)
{
for (int i=0; i<3; i++)
ch[i].Mute(!!(mask & (1 << i)));
psg.SetChannelMask(mask >> 6);
}
void OPN::SetPan(uint panning)
{
pan = (panning >> 6) & 3;
psg.SetPan(panning & 0x3f);
}
void OPN::Mix(Sample* buffer, int nsamples)
{
#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14)
psg.Mix(buffer, nsamples);
ch[0].SetFNum(fnum[0]);
ch[1].SetFNum(fnum[1]);
if (!(regtc & 0xc0))
ch[2].SetFNum(fnum[2]);
else
{ ch[2].op[0].SetFNum(fnum3[1]);
ch[2].op[1].SetFNum(fnum3[2]);
ch[2].op[2].SetFNum(fnum3[0]);
ch[2].op[3].SetFNum(fnum[2]);
}
int actch = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare();
if (actch & 0x15)
{
Sample* limit = buffer + nsamples * 2;
for (Sample* dest = buffer; dest < limit; dest+=2)
{
ISample s = 0;
if (actch & 0x01) s = ch[0].Calc();
if (actch & 0x04) s += ch[1].Calc();
if (actch & 0x10) s += ch[2].Calc();
s = IStoSample(s);
StoreSample(dest[0], (pan&1)?s:0);
StoreSample(dest[1], (pan&2)?s:0);
}
}
#undef IStoSample
}
#endif
#if defined(BUILD_OPNA) || defined(BUILD_OPNB) || defined (BUILD_OPN2)
int OPNABase::amtable[FM_LFOENTS] = { -1, };
int OPNABase::pmtable[FM_LFOENTS];
int32 OPNABase::tltable[FM_TLENTS+FM_TLPOS];
bool OPNABase::tablehasmade = false;
OPNABase::OPNABase()
{
ch6dac_enable = 0;
ch6dac_disable = 0x80;
adpcmbuf = 0;
memaddr = 0;
startaddr = 0;
deltan = 256;
adpcmvol = 0;
control2 = 0;
MakeTable2();
BuildLFOTable();
for (int i=0; i<6; i++)
{
ch[i].SetChip(&chip);
ch[i].SetType(typeN);
}
}
OPNABase::~OPNABase()
{
}
bool OPNABase::Init(uint c, uint r, bool)
{
RebuildTimeTable();
Reset();
SetVolumeFM(0);
SetVolumePSG(0);
SetChannelMask(0);
return true;
}
void OPNABase::MakeTable2()
{
if (!tablehasmade)
{
for (int i=-FM_TLPOS; i<FM_TLENTS; i++)
{
tltable[i+FM_TLPOS] = uint(65536. * pow(2.0, i * -16. / FM_TLENTS))-1;
}
tablehasmade = true;
}
}
void OPNABase::Reset()
{
int i;
OPNBase::Reset();
for (i=0x20; i<0x28; i++) SetReg(i, 0);
for (i=0x30; i<0xc0; i++) SetReg(i, 0);
for (i=0x130; i<0x1c0; i++) SetReg(i, 0);
for (i=0x100; i<0x110; i++) SetReg(i, 0);
for (i=0x10; i<0x20; i++) SetReg(i, 0);
for (i=0; i<6; i++)
{
pan[i] = 3;
ch[i].Reset();
}
stmask = ~0x1c;
statusnext = 0;
memaddr = 0;
adpcmd = 127;
adpcmx = 0;
lfocount = 0;
adpcmplay = false;
adplc = 0;
adpld = 0x100;
status = 0;
UpdateStatus();
}
bool OPNABase::SetRate(uint c, uint r, bool)
{
c /= 2;
OPNBase::Init(c, r);
adplbase = int(8192. * (clock/72.) / r);
adpld = deltan * adplbase >> 16;
RebuildTimeTable();
lfodcount = reg22 & 0x08 ? lfotable[reg22 & 7] : 0;
return true;
}
void OPNABase::SetChannelMask(uint mask)
{
for (int i=0; i<6; i++)
ch[i].Mute(!!(mask & (1 << i)));
psg.SetChannelMask(mask >> 6);
adpcmmask_ = (mask & (1 << 9)) != 0;
rhythmmask_ = (mask >> 10) & ((1 << 6) - 1);
}
void OPNABase::SetReg(uint addr, uint data)
{
int c = addr & 3;
switch (addr)
{
uint modified;
case 0x24: case 0x25:
SetTimerA(addr, data);
break;
case 0x26:
SetTimerB(data);
break;
case 0x27:
SetTimerControl(data);
break;
case 0x28: if ((data & 3) < 3)
{
c = (data & 3) + (data & 4 ? 3 : 0);
ch[c].KeyControl(data >> 4);
}
break;
case 0x29:
reg29 = data;
break;
case 0x2d: case 0x2e: case 0x2f:
SetPrescaler(addr-0x2d);
break;
case 0x1a0: case 0x1a1: case 0x1a2:
c += 3;
case 0xa0: case 0xa1: case 0xa2:
fnum[c] = data + fnum2[c] * 0x100;
ch[c].SetFNum(fnum[c]);
break;
case 0x1a4: case 0x1a5: case 0x1a6:
c += 3;
case 0xa4 : case 0xa5: case 0xa6:
fnum2[c] = uint8(data);
break;
case 0xa8: case 0xa9: case 0xaa:
fnum3[c] = data + fnum2[c+6] * 0x100;
break;
case 0xac : case 0xad: case 0xae:
fnum2[c+6] = uint8(data);
break;
case 0x1b0: case 0x1b1: case 0x1b2:
c += 3;
case 0xb0: case 0xb1: case 0xb2:
ch[c].SetFB((data >> 3) & 7);
ch[c].SetAlgorithm(data & 7);
break;
case 0x1b4: case 0x1b5: case 0x1b6:
c += 3;
case 0xb4: case 0xb5: case 0xb6:
pan[c] = (data >> 6) & 3;
ch[c].SetMS(data);
break;
case 0x22:
modified = reg22 ^ data;
reg22 = data;
if (modified & 0x8)
lfocount = 0;
lfodcount = reg22 & 8 ? lfotable[reg22 & 7] : 0;
break;
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
psg.SetReg(addr, data);
break;
default:
if (c < 3)
{
if (addr & 0x100)
c += 3;
OPNBase::SetParameter(&ch[c], addr, data);
}
break;
}
}
void OPNABase::SetADPCMBReg(uint addr, uint data)
{
switch (addr)
{
case 0x00: if ((data & 0x80) && !adpcmplay)
{
adpcmplay = true;
memaddr = startaddr;
adpcmx = 0, adpcmd = 127;
adplc = 0;
}
if (data & 1)
{
adpcmplay = false;
}
control1 = data;
break;
case 0x01: control2 = data;
granuality = control2 & 2 ? 1 : 4;
break;
case 0x02: case 0x03: adpcmreg[addr - 0x02 + 0] = data;
startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 6;
memaddr = startaddr;
break;
case 0x04: case 0x05: adpcmreg[addr - 0x04 + 2] = data;
stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 6;
break;
case 0x08: if ((control1 & 0x60) == 0x60)
{
WriteRAM(data);
}
break;
case 0x09: case 0x0a: adpcmreg[addr - 0x09 + 4] = data;
deltan = adpcmreg[5]*256+adpcmreg[4];
deltan = Max(256, deltan);
adpld = deltan * adplbase >> 16;
break;
case 0x0b: adpcmlevel = data;
adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
break;
case 0x0c: case 0x0d: adpcmreg[addr - 0x0c + 6] = data;
limitaddr = (adpcmreg[7]*256+adpcmreg[6] + 1) << 6;
break;
case 0x10: if (data & 0x80)
{
status = 0;
UpdateStatus();
}
else
{
stmask = ~(data & 0x1f);
}
break;
}
}
uint OPNA::GetReg(uint addr)
{
if (addr < 0x10)
return psg.GetReg(addr);
if (addr == 0x108)
{
uint data = adpcmreadbuf & 0xff;
adpcmreadbuf >>= 8;
if ((control1 & 0x60) == 0x20)
{
adpcmreadbuf |= ReadRAM() << 8;
}
return data;
}
if (addr == 0xff)
return 1;
return 0;
}
void OPNABase::SetStatus(uint bits)
{
if (!(status & bits))
{
status |= bits & stmask;
UpdateStatus();
}
}
void OPNABase::ResetStatus(uint bits)
{
status &= ~bits;
UpdateStatus();
}
inline void OPNABase::UpdateStatus()
{
Intr((status & stmask & reg29) != 0);
}
void OPNABase::WriteRAM(uint data)
{
#ifndef NO_BITTYPE_EMULATION
if (!(control2 & 2))
{
adpcmbuf[(memaddr >> 4) & 0x3ffff] = data;
memaddr += 16;
}
else
{
uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff];
uint bank = (memaddr >> 1) & 7;
uint8 mask = 1 << bank;
data <<= bank;
p[0x00000] = (p[0x00000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x08000] = (p[0x08000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x10000] = (p[0x10000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x18000] = (p[0x18000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x20000] = (p[0x20000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x28000] = (p[0x28000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x30000] = (p[0x30000] & ~mask) | (uint8(data) & mask); data >>= 1;
p[0x38000] = (p[0x38000] & ~mask) | (uint8(data) & mask);
memaddr += 2;
}
#else
adpcmbuf[(memaddr >> granuality) & 0x3ffff] = data;
memaddr += 1 << granuality;
#endif
if (memaddr == stopaddr)
{
SetStatus(4);
statusnext = 0x04; memaddr &= 0x3fffff;
}
if (memaddr == limitaddr)
{
memaddr = 0;
}
SetStatus(8);
}
uint OPNABase::ReadRAM()
{
uint data;
#ifndef NO_BITTYPE_EMULATION
if (!(control2 & 2))
{
data = adpcmbuf[(memaddr >> 4) & 0x3ffff];
memaddr += 16;
}
else
{
uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff];
uint bank = (memaddr >> 1) & 7;
uint8 mask = 1 << bank;
data = (p[0x38000] & mask);
data = data * 2 + (p[0x30000] & mask);
data = data * 2 + (p[0x28000] & mask);
data = data * 2 + (p[0x20000] & mask);
data = data * 2 + (p[0x18000] & mask);
data = data * 2 + (p[0x10000] & mask);
data = data * 2 + (p[0x08000] & mask);
data = data * 2 + (p[0x00000] & mask);
data >>= bank;
memaddr += 2;
}
#else
data = adpcmbuf[(memaddr >> granuality) & 0x3ffff];
memaddr += 1 << granuality;
#endif
if (memaddr == stopaddr)
{
SetStatus(4);
statusnext = 0x04; memaddr &= 0x3fffff;
}
if (memaddr == limitaddr)
{
memaddr = 0;
}
if (memaddr < stopaddr)
SetStatus(8);
return data;
}
inline int OPNABase::DecodeADPCMBSample(uint data)
{
static const int table1[16] =
{
1, 3, 5, 7, 9, 11, 13, 15,
-1, -3, -5, -7, -9, -11, -13, -15,
};
static const int table2[16] =
{
57, 57, 57, 57, 77, 102, 128, 153,
57, 57, 57, 57, 77, 102, 128, 153,
};
adpcmx = Limit(adpcmx + table1[data] * adpcmd / 8, 32767, -32768);
adpcmd = Limit(adpcmd * table2[data] / 64, 24576, 127);
return adpcmx;
}
int OPNABase::ReadRAMN()
{
uint data;
if (granuality > 0)
{
#ifndef NO_BITTYPE_EMULATION
if (!(control2 & 2))
{
data = adpcmbuf[(memaddr >> 4) & 0x3ffff];
memaddr += 8;
if (memaddr & 8)
return DecodeADPCMBSample(data >> 4);
data &= 0x0f;
}
else
{
uint8* p = &adpcmbuf[(memaddr >> 4) & 0x7fff] + ((~memaddr & 1) << 17);
uint bank = (memaddr >> 1) & 7;
uint8 mask = 1 << bank;
data = (p[0x18000] & mask);
data = data * 2 + (p[0x10000] & mask);
data = data * 2 + (p[0x08000] & mask);
data = data * 2 + (p[0x00000] & mask);
data >>= bank;
memaddr ++;
if (memaddr & 1)
return DecodeADPCMBSample(data);
}
#else
data = adpcmbuf[(memaddr >> granuality) & adpcmmask];
memaddr += 1 << (granuality-1);
if (memaddr & (1 << (granuality-1)))
return DecodeADPCMBSample(data >> 4);
data &= 0x0f;
#endif
}
else
{
data = adpcmbuf[(memaddr >> 1) & adpcmmask];
++memaddr;
if (memaddr & 1)
return DecodeADPCMBSample(data >> 4);
data &= 0x0f;
}
DecodeADPCMBSample(data);
if (memaddr == stopaddr)
{
if (control1 & 0x10)
{
memaddr = startaddr;
data = adpcmx;
adpcmx = 0, adpcmd = 127;
return data;
}
else
{
memaddr &= adpcmmask; SetStatus(adpcmnotice);
adpcmplay = false;
}
}
if (memaddr == limitaddr)
memaddr = 0;
return adpcmx;
}
uint OPNABase::ReadStatusEx()
{
uint r = ((status | 8) & stmask) | (adpcmplay ? 0x20 : 0);
status |= statusnext;
statusnext = 0;
return r;
}
inline void OPNABase::DecodeADPCMB()
{
apout0 = apout1;
int n = (ReadRAMN() * adpcmvolume) >> 13;
apout1 = adpcmout + n;
adpcmout = n;
}
void OPNABase::ADPCMBMix(Sample* dest, uint count)
{
uint maskl = control2 & 0x80 ? -1 : 0;
uint maskr = control2 & 0x40 ? -1 : 0;
if (adpcmmask_)
{
maskl = maskr = 0;
}
if (adpcmplay)
{
if (adpld <= 8192) {
for (; count>0; count--)
{
if (adplc < 0)
{
adplc += 8192;
DecodeADPCMB();
if (!adpcmplay)
break;
}
int s = (adplc * apout0 + (8192-adplc) * apout1) >> 13;
StoreSample(dest[0], s & maskl);
StoreSample(dest[1], s & maskr);
dest += 2;
adplc -= adpld;
}
for (; count>0 && apout0; count--)
{
if (adplc < 0)
{
apout0 = apout1, apout1 = 0;
adplc += 8192;
}
int s = (adplc * apout1) >> 13;
StoreSample(dest[0], s & maskl);
StoreSample(dest[1], s & maskr);
dest += 2;
adplc -= adpld;
}
}
else {
int t = (-8192*8192)/adpld;
for (; count>0; count--)
{
int s = apout0 * (8192+adplc);
while (adplc < 0)
{
DecodeADPCMB();
if (!adpcmplay)
goto stop;
s -= apout0 * Max(adplc, t);
adplc -= t;
}
adplc -= 8192;
s >>= 13;
StoreSample(dest[0], s & maskl);
StoreSample(dest[1], s & maskr);
dest += 2;
}
stop:
;
}
}
if (!adpcmplay)
{
apout0 = apout1 = adpcmout = 0;
adplc = 0;
}
}
void OPNABase::FMMix(Sample* buffer, int nsamples)
{
if (fmvolume > 0)
{
if (!(regtc & 0xc0))
csmch->SetFNum(fnum[csmch-ch]);
else
{
csmch->op[0].SetFNum(fnum3[1]); csmch->op[1].SetFNum(fnum3[2]);
csmch->op[2].SetFNum(fnum3[0]); csmch->op[3].SetFNum(fnum[2]);
}
int act = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare();
if (reg29 & 0x80)
act |= (ch[3].Prepare() | ((ch[4].Prepare() | (ch[5].Prepare() << 2)) << 2)) << 6;
if (!(reg22 & 0x08))
act &= 0x555;
if (!ch6dac_disable) act &= ~(3 << 10);
if (act & 0x555)
{
Mix6(buffer, nsamples, act);
}
}
}
void OPNABase::MixSubSL(int activech, ISample** dest)
{
if (activech & 0x001) (*dest[0] = ch[0].CalcL());
if (activech & 0x004) (*dest[1] += ch[1].CalcL());
if (activech & 0x010) (*dest[2] += ch[2].CalcL());
if (activech & 0x040) (*dest[3] += ch[3].CalcL());
if (activech & 0x100) (*dest[4] += ch[4].CalcL());
if (activech & 0x400) (*dest[5] += ch[5].CalcL());
}
inline void OPNABase::MixSubS(int activech, ISample** dest)
{
if (activech & 0x001) (*dest[0] = ch[0].Calc());
if (activech & 0x004) (*dest[1] += ch[1].Calc());
if (activech & 0x010) (*dest[2] += ch[2].Calc());
if (activech & 0x040) (*dest[3] += ch[3].Calc());
if (activech & 0x100) (*dest[4] += ch[4].Calc());
if (activech & 0x400) (*dest[5] += ch[5].Calc());
}
void OPNABase::BuildLFOTable()
{
if (amtable[0] == -1)
{
for (int c=0; c<256; c++)
{
int v;
if (c < 0x40) v = c * 2 + 0x80;
else if (c < 0xc0) v = 0x7f - (c - 0x40) * 2 + 0x80;
else v = (c - 0xc0) * 2;
pmtable[c] = c;
if (c < 0x80) v = 0xff - c * 2;
else v = (c - 0x80) * 2;
amtable[c] = v & ~3;
}
}
}
inline void OPNABase::LFO()
{
chip.SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
chip.SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
lfocount += lfodcount;
}
#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14)
void OPNABase::Mix6(Sample* buffer, int nsamples, int activech)
{
ISample ibuf[4];
ISample* idest[6];
idest[0] = &ibuf[pan[0]];
idest[1] = &ibuf[pan[1]];
idest[2] = &ibuf[pan[2]];
idest[3] = &ibuf[pan[3]];
idest[4] = &ibuf[pan[4]];
idest[5] = &ibuf[pan[5]];
Sample* limit = buffer + nsamples * 2;
for (Sample* dest = buffer; dest < limit; dest+=2)
{
ibuf[1] = ibuf[2] = ibuf[3] = 0;
if (activech & 0xaaa)
LFO(), MixSubSL(activech, idest);
else
MixSubS(activech, idest);
StoreSample(dest[0], IStoSample(ibuf[2] + ibuf[3]));
StoreSample(dest[1], IStoSample(ibuf[1] + ibuf[3]));
}
}
#endif
#ifdef BUILD_OPNA
OPNA::OPNA()
{
for (int i=0; i<6; i++)
{
rhythm[i].sample = 0;
rhythm[i].pos = 0;
rhythm[i].size = 0;
rhythm[i].volume = 0;
rhythm[i].level = 0;
rhythm[i].pan = 0;
}
rhythmtvol = 0;
adpcmmask = 0x3ffff;
adpcmnotice = 4;
csmch = &ch[2];
}
OPNA::~OPNA()
{
if (adpcmbuf) delete[] adpcmbuf;
for (int i=0; i<6; i++)
if (rhythm[i].sample) delete[] rhythm[i].sample;
}
bool OPNA::Init(uint c, uint r, bool ipflag, const char* path)
{
rate = 8000;
LoadRhythmSample(path);
if (!adpcmbuf)
adpcmbuf = new uint8[0x40000];
if (!adpcmbuf)
return false;
if (!SetRate(c, r, ipflag))
return false;
if (!OPNABase::Init(c, r, ipflag))
return false;
Reset();
SetVolumeADPCM(0);
SetVolumeRhythmTotal(0);
for (int i=0; i<6; i++)
SetVolumeRhythm(i, 0);
return true;
}
void OPNA::Reset()
{
reg29 = 0x1f;
rhythmkey = 0;
limitaddr = 0x3ffff;
OPNABase::Reset();
}
bool OPNA::SetRate(uint c, uint r, bool ipflag)
{
if (!OPNABase::SetRate(c, r, ipflag))
return false;
for (int i=0; i<6; i++)
{
rhythm[i].step = rhythm[i].rate * 1024 / r;
}
return true;
}
bool OPNA::LoadRhythmSample(const char* path)
{
static const char* rhythmname[6] =
{
"BD", "SD", "TOP", "HH", "TOM", "RIM",
};
int i;
for (i=0; i<6; i++)
rhythm[i].pos = ~0;
for (i=0; i<6; i++)
{
FileIO file;
uint32 fsize;
char buf[PATH_MAX] = "";
snprintf(buf, sizeof buf, "%s/2608_%s.WAV", path ? path : "/sdcard/s98", rhythmname[i]);
if (!file.Open(buf, FileIO::readonly))
{
if (i != 5)
break;
snprintf(buf, sizeof buf, "%s/2608_RYM.WAV", path ? path : "/sdcard/s98");
if (!file.Open(buf, FileIO::readonly))
break;
}
struct
{
uint32 chunksize;
uint16 tag;
uint16 nch;
uint32 rate;
uint32 avgbytes;
uint16 align;
uint16 bps;
uint16 size;
} whdr;
file.Seek(0x10, FileIO::begin);
file.Read(&whdr, sizeof(whdr));
uint8 subchunkname[4];
fsize = 4 + whdr.chunksize - sizeof(whdr);
do
{
file.Seek(fsize, FileIO::current);
file.Read(&subchunkname, 4);
file.Read(&fsize, 4);
} while (memcmp("data", subchunkname, 4));
fsize /= 2;
if (fsize >= 0x100000 || whdr.tag != 1 || whdr.nch != 1)
break;
fsize = Max(fsize, (1<<31)/1024);
if (rhythm[i].sample) delete[] rhythm[i].sample;
rhythm[i].sample = new int16[fsize];
if (!rhythm[i].sample)
break;
file.Read(rhythm[i].sample, fsize * 2);
rhythm[i].rate = whdr.rate;
rhythm[i].step = rhythm[i].rate * 1024 / rate;
rhythm[i].pos = rhythm[i].size = fsize * 1024;
}
if (i != 6)
{
for (i=0; i<6; i++)
{
if (rhythm[i].sample) delete[] rhythm[i].sample;
rhythm[i].sample = 0;
}
return false;
}
return true;
}
void OPNA::SetReg(uint addr, uint data)
{
addr &= 0x1ff;
switch (addr)
{
case 0x29:
reg29 = data;
break;
case 0x10: if (!(data & 0x80)) {
rhythmkey |= data & 0x3f;
if (data & 0x01) rhythm[0].pos = 0;
if (data & 0x02) rhythm[1].pos = 0;
if (data & 0x04) rhythm[2].pos = 0;
if (data & 0x08) rhythm[3].pos = 0;
if (data & 0x10) rhythm[4].pos = 0;
if (data & 0x20) rhythm[5].pos = 0;
}
else
{ rhythmkey &= ~data;
}
break;
case 0x11:
rhythmtl = ~data & 63;
break;
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: rhythm[addr & 7].pan = (data >> 6) & 3;
rhythm[addr & 7].level = ~data & 31;
break;
case 0x100: case 0x101:
case 0x102: case 0x103:
case 0x104: case 0x105:
case 0x108: case 0x109:
case 0x10a: case 0x10b:
case 0x10c: case 0x10d:
case 0x110:
OPNABase::SetADPCMBReg(addr - 0x100, data);
break;
default:
OPNABase::SetReg(addr, data);
break;
}
}
void OPNA::RhythmMix(Sample* buffer, uint count)
{
if (rhythmtvol < 128 && rhythm[0].sample && (rhythmkey & 0x3f))
{
Sample* limit = buffer + count * 2;
for (int i=0; i<6; i++)
{
Rhythm& r = rhythm[i];
if ((rhythmkey & (1 << i)) && r.level < 128)
{
int db = Limit(rhythmtl+rhythmtvol+r.level+r.volume, 127, -31);
int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4;
int maskl = -((r.pan >> 1) & 1);
int maskr = -(r.pan & 1);
if (rhythmmask_ & (1 << i))
{
maskl = maskr = 0;
}
for (Sample* dest = buffer; dest<limit && r.pos < r.size; dest+=2)
{
int sample = (r.sample[r.pos / 1024] * vol) >> 12;
r.pos += r.step;
StoreSample(dest[0], sample & maskl);
StoreSample(dest[1], sample & maskr);
}
}
}
}
}
void OPNA::SetVolumeRhythmTotal(int db)
{
db = Min(db, 20);
rhythmtvol = -(db * 2 / 3);
}
void OPNA::SetVolumeRhythm(int index, int db)
{
db = Min(db, 20);
rhythm[index].volume = -(db * 2 / 3);
}
void OPNA::SetVolumeADPCM(int db)
{
db = Min(db, 20);
if (db > -192)
adpcmvol = int(65536.0 * pow(10.0, db / 40.0));
else
adpcmvol = 0;
adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
}
void OPNA::Mix(Sample* buffer, int nsamples)
{
FMMix(buffer, nsamples);
psg.Mix(buffer, nsamples);
ADPCMBMix(buffer, nsamples);
RhythmMix(buffer, nsamples);
}
#endif
#ifdef BUILD_OPNB
OPNB::OPNB()
{
adpcmabuf = 0;
adpcmasize = 0;
for (int i=0; i<6; i++)
{
adpcma[i].pan = 0;
adpcma[i].level = 0;
adpcma[i].volume = 0;
adpcma[i].pos = 0;
adpcma[i].step = 0;
adpcma[i].volume = 0;
adpcma[i].start = 0;
adpcma[i].stop = 0;
adpcma[i].adpcmx = 0;
adpcma[i].adpcmd = 0;
}
adpcmatl = 0;
adpcmakey = 0;
adpcmatvol = 0;
adpcmmask = 0;
adpcmnotice = 0x8000;
granuality = -1;
csmch = &ch[2];
InitADPCMATable();
}
OPNB::~OPNB()
{
}
bool OPNB::Init(uint c, uint r, bool ipflag,
uint8 *_adpcma, int _adpcma_size,
uint8 *_adpcmb, int _adpcmb_size)
{
int i;
if (!SetRate(c, r, ipflag))
return false;
if (!OPNABase::Init(c, r, ipflag))
return false;
adpcmabuf = _adpcma;
adpcmasize = _adpcma_size;
adpcmbuf = _adpcmb;
for (i=0; i<=24; i++) {
if (_adpcmb_size <= (1 << i))
{
adpcmmask = (1 << i) - 1;
break;
}
}
limitaddr = adpcmmask;
Reset();
SetVolumeFM(0);
SetVolumePSG(0);
SetVolumeADPCMB(0);
SetVolumeADPCMATotal(0);
for (i=0; i<6; i++)
SetVolumeADPCMA(i, 0);
SetChannelMask(0);
return true;
}
void OPNB::Reset()
{
OPNABase::Reset();
stmask = ~0;
adpcmakey = 0;
reg29 = ~0;
for (int i=0; i<6; i++)
{
adpcma[i].pan = 0;
adpcma[i].level = 0;
adpcma[i].volume = 0;
adpcma[i].pos = 0;
adpcma[i].step = 0;
adpcma[i].volume = 0;
adpcma[i].start = 0;
adpcma[i].stop = 0;
adpcma[i].adpcmx = 0;
adpcma[i].adpcmd = 0;
}
}
bool OPNB::SetRate(uint c, uint r, bool ipflag)
{
if (!OPNABase::SetRate(c, r, ipflag))
return false;
adpcmastep = int(double(c) / 54 * 8192 / r);
return true;
}
void OPNB::SetReg(uint addr, uint data)
{
addr &= 0x1ff;
switch (addr)
{
case 0x29:
case 0x2d: case 0x2e: case 0x2f:
break;
case 0x100: if (!(data & 0x80)) {
adpcmakey |= data & 0x3f;
for (int c=0; c<6; c++)
{
if (data & (1<<c))
{
ResetStatus(0x100 << c);
adpcma[c].pos = adpcma[c].start;
adpcma[c].step = 0;
adpcma[c].adpcmx = 0;
adpcma[c].adpcmd = 0;
adpcma[c].nibble = 0;
}
}
}
else
{ adpcmakey &= ~data;
}
break;
case 0x101:
adpcmatl = ~data & 63;
break;
case 0x108: case 0x109: case 0x10a:
case 0x10b: case 0x10c: case 0x10d:
adpcma[addr & 7].pan = (data >> 6) & 3;
adpcma[addr & 7].level = ~data & 31;
break;
case 0x110: case 0x111: case 0x112: case 0x113: case 0x114: case 0x115:
case 0x118: case 0x119: case 0x11a: case 0x11b: case 0x11c: case 0x11d:
adpcmareg[addr - 0x110] = data;
adpcma[addr & 7].pos = adpcma[addr & 7].start =
(adpcmareg[(addr&7)+8]*256+adpcmareg[addr&7]) << 9;
break;
case 0x120: case 0x121: case 0x122: case 0x123: case 0x124: case 0x125:
case 0x128: case 0x129: case 0x12a: case 0x12b: case 0x12c: case 0x12d:
adpcmareg[addr - 0x110] = data;
adpcma[addr & 7].stop =
(adpcmareg[(addr&7)+24]*256+adpcmareg[(addr&7)+16] + 1) << 9;
break;
case 0x10:
if ((data & 0x80) && !adpcmplay)
{
adpcmplay = true;
memaddr = startaddr;
adpcmx = 0, adpcmd = 127;
adplc = 0;
}
if (data & 1)
adpcmplay = false;
control1 = data & 0x91;
break;
case 0x11: control2 = data & 0xc0;
break;
case 0x12: case 0x13: adpcmreg[addr - 0x12 + 0] = data;
startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 9;
memaddr = startaddr;
break;
case 0x14: case 0x15: adpcmreg[addr - 0x14 + 2] = data;
stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 9;
break;
case 0x19: case 0x1a: adpcmreg[addr - 0x19 + 4] = data;
deltan = adpcmreg[5]*256+adpcmreg[4];
deltan = Max(256, deltan);
adpld = deltan * adplbase >> 16;
break;
case 0x1b: adpcmlevel = data;
adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
break;
case 0x1c: stmask = ~((data & 0xbf) << 8);
status &= stmask;
UpdateStatus();
break;
default:
OPNABase::SetReg(addr, data);
break;
}
}
uint OPNB::GetReg(uint addr)
{
if (addr < 0x10)
return psg.GetReg(addr);
return 0;
}
uint OPNB::ReadStatusEx()
{
return (status & stmask) >> 8;
}
int OPNB::jedi_table[(48+1)*16];
void OPNB::InitADPCMATable()
{
const static int8 table2[] =
{
1, 3, 5, 7, 9, 11, 13, 15,
-1, -3, -5, -7, -9,-11,-13,-15,
};
for (int i=0; i<=48; i++)
{
int s = int(16.0 * pow (1.1, i) * 3);
for (int j=0; j<16; j++)
{
jedi_table[i*16+j] = s * table2[j] / 8;
}
}
}
void OPNB::ADPCMAMix(Sample* buffer, uint count)
{
const static int decode_tableA1[16] =
{
-1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16,
-1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16
};
if (adpcmatvol < 128 && (adpcmakey & 0x3f))
{
Sample* limit = buffer + count * 2;
for (int i=0; i<6; i++)
{
ADPCMA& r = adpcma[i];
if ((adpcmakey & (1 << i)) && r.level < 128)
{
uint maskl = r.pan & 2 ? -1 : 0;
uint maskr = r.pan & 1 ? -1 : 0;
if (rhythmmask_ & (1 << i))
{
maskl = maskr = 0;
}
int db = Limit(adpcmatl+adpcmatvol+r.level+r.volume, 127, -31);
int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4;
Sample* dest = buffer;
for ( ; dest<limit; dest+=2)
{
r.step += adpcmastep;
if (r.pos >= r.stop)
{
SetStatus(0x100 << i);
adpcmakey &= ~(1<<i);
break;
}
for (; r.step > 0x10000; r.step -= 0x10000)
{
int data;
if (!(r.pos & 1))
{
r.nibble = adpcmabuf[r.pos>>1];
data = r.nibble >> 4;
}
else
{
data = r.nibble & 0x0f;
}
r.pos++;
r.adpcmx += jedi_table[r.adpcmd + data];
r.adpcmx = Limit(r.adpcmx, 2048*3-1, -2048*3);
r.adpcmd += decode_tableA1[data];
r.adpcmd = Limit(r.adpcmd, 48*16, 0);
}
int sample = (r.adpcmx * vol) >> 10;
StoreSample(dest[0], sample & maskl);
StoreSample(dest[1], sample & maskr);
}
}
}
}
}
void OPNB::SetVolumeADPCMATotal(int db)
{
db = Min(db, 20);
adpcmatvol = -(db * 2 / 3);
}
void OPNB::SetVolumeADPCMA(int index, int db)
{
db = Min(db, 20);
adpcma[index].volume = -(db * 2 / 3);
}
void OPNB::SetVolumeADPCMB(int db)
{
db = Min(db, 20);
if (db > -192)
adpcmvol = int(65536.0 * pow(10.0, db / 40.0));
else
adpcmvol = 0;
}
void OPNB::Mix(Sample* buffer, int nsamples)
{
FMMix(buffer, nsamples);
psg.Mix(buffer, nsamples);
ADPCMBMix(buffer, nsamples);
ADPCMAMix(buffer, nsamples);
}
#endif
#ifdef BUILD_OPN2
#define OPN2_DAC_FIFO_SIZE 0x8000
OPN2::OPN2()
{
ch6dac_fifo = 0;
csmch = &ch[2];
}
OPN2::~OPN2()
{
if(ch6dac_fifo) delete[] ch6dac_fifo;
}
bool OPN2::Init(uint c, uint r, bool ipflag, const char*)
{
rate = 8000;
ch6dac_interpolation = ipflag;
if (!ch6dac_fifo)
ch6dac_fifo = new uint8[OPN2_DAC_FIFO_SIZE + 1];
if (!ch6dac_fifo)
return false;
if (!SetRate(c, r, ipflag))
return false;
if (!OPNABase::Init(c, r, ipflag))
return false;
Reset();
return true;
}
void OPN2::Reset()
{
OPNABase::Reset();
SetVolumePCM(0);
if (ch6dac_fifo) ch6dac_fifo[0] = 0x80;
ch6dac_ptr = 0;
ch6dac_enable = 0;
ch6dac_disable = 0x80;
ch6dac_data = 0;
ch6dac_pan = 3;
stmask = ~0;
reg29 = ~0;
}
bool OPN2::SetRate(uint c, uint r, bool ipflag)
{
if (!OPNABase::SetRate(c, r, ipflag))
return false;
return true;
}
void OPN2::SetReg(uint addr, uint data)
{
addr &= 0x1ff;
switch (addr)
{
case 0x29:
case 0x2d: case 0x2e: case 0x2f:
break;
case 0x2a:
ch6dac_data = data;
if (ch6dac_fifo && ch6dac_ptr < OPN2_DAC_FIFO_SIZE)
{
ch6dac_fifo[ch6dac_ptr++] = data;
ch6dac_fifo[ch6dac_ptr] = data;
}
break;
case 0x2b:
if (data & 0x80)
{
ch6dac_enable = 0x80;
ch6dac_disable = 0;
}
else
{
ch6dac_disable = 0x80;
}
break;
case 0x1b6:
ch6dac_pan = (data & 0xc0) >> 6;
OPNABase::SetReg(addr, data);
break;
default:
OPNABase::SetReg(addr, data);
break;
}
}
void OPN2::SetVolumePCM(int db)
{
db = Min(db, 20);
db = -(db * 2 / 3);
db = Limit(db, 127, -31);
ch6dac_vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4;
}
void OPN2::PCMMix(Sample* dest, uint count)
{
if (!ch6dac_fifo) return;
Sample dacout = 0;
if (ch6dac_enable)
{
if (ch6dac_interpolation)
{
int spd, phase, phasemax;
phase = 0;
if (count && ch6dac_ptr)
{
phasemax = ch6dac_ptr << 16;
spd = (phasemax / count);
while (count && phase < phasemax)
{
dacout = ch6dac_fifo[(phase >> 16)+1];
dacout = (dacout - 0x80) << 6;
if (ch6dac_pan & 2) dest[0] += dacout;
if (ch6dac_pan & 1) dest[1] += dacout;
dest += 2;
count--;
phase += spd;
}
ch6dac_fifo[0] = ch6dac_fifo[ch6dac_ptr];
ch6dac_ptr = 0;
}
if (count)
{
dacout = ch6dac_fifo[0];
dacout = ((dacout - 0x80) << 6);
while (count)
{
if (ch6dac_pan & 2) dest[0] += dacout;
if (ch6dac_pan & 1) dest[1] += dacout;
dest += 2;
count--;
}
}
} else {
while (count)
{
dacout = ch6dac_data;
dacout = ((dacout - 0x80) << 6);
if (ch6dac_pan & 2) dest[0] += dacout;
if (ch6dac_pan & 1) dest[1] += dacout;
dest += 2;
count--;
}
}
}
else
{
ch6dac_fifo[0] = ch6dac_fifo[ch6dac_ptr];
ch6dac_ptr = 0;
}
ch6dac_enable = !ch6dac_disable;
}
void OPN2::Mix(Sample* buffer, int nsamples)
{
FMMix(buffer, nsamples);
PCMMix(buffer, nsamples);
}
#endif
}