zmusic/thirdparty/opnmidi/chips/np2/fmgen_opna.cpp
Wohlstand 72c23d98a3 Update libOPNMIDI library to 1.5.0
## 1.5.0   2020-09-28
 * Drum note length expanding is now supported in real-time mode (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Added support for OPNA chip with Neko Project II Kai YM2602 emulator usage (Thanks to [Jean Pierre Cimalando](https://github.com/jpcima) for a work!)
 * Added VGM file dumper which allows to output OPN2 commands into VGM file. (A new MIDI to VGM tool is now created with basing on libOPNMIDI)
 * Fixed an incorrect work of CC-121 (See https://github.com/Wohlstand/libADLMIDI/issues/227 for details)
 * Internality has been refactored and improved
2020-10-04 08:03:44 +02:00

2142 lines
47 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ---------------------------------------------------------------------------
// OPN/A/B interface with ADPCM support
// Copyright (C) cisc 1998, 2001.
// ---------------------------------------------------------------------------
// $Id: opna.cpp,v 1.68 2003/06/12 14:03:44 cisc Exp $
#include "fmgen_types.h"
#include "fmgen_headers.h"
#include "fmgen_misc.h"
#include "fmgen_opna.h"
#include "fmgen_fmgeninl.h"
#define BUILD_OPN
#define BUILD_OPNA
#define BUILD_OPNB
// TOFIX:
// OPN ch3 <20><><EFBFBD><EFBFBD><EFBFBD>Prepare<72>̑ΏۂƂȂ<C682><C882>Ă<EFBFBD><C482>܂<EFBFBD><DC82><EFBFBD>Q
// ---------------------------------------------------------------------------
// OPNA: ADPCM <20>f<EFBFBD>[<5B>^<5E>̊i<CC8A>[<5B><><EFBFBD><EFBFBD><EFBFBD>̈Ⴂ (8bit/1bit) <20><><EFBFBD>G<EFBFBD>~<7E><><EFBFBD><EFBFBD><EFBFBD>[<5B>g<EFBFBD><67><EFBFBD>Ȃ<EFBFBD>
// <09><><EFBFBD>̃I<CC83>v<EFBFBD>V<EFBFBD><56><EFBFBD><EFBFBD><EFBFBD><EFBFBD>L<EFBFBD><4C><EFBFBD>ɂ<EFBFBD><C982><EFBFBD><EFBFBD> ADPCM <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ւ̃A<CC83>N<EFBFBD>Z<EFBFBD>X(<28><><EFBFBD><EFBFBD> 8bit <20><><EFBFBD>[<5B>h)<29><>
// <09><><EFBFBD><EFBFBD><EFBFBD>y<EFBFBD><79><EFBFBD>Ȃ邩<C882><E982A9>
//
//#define NO_BITTYPE_EMULATION
#ifdef BUILD_OPNA
#include "fmgen_file.h"
#endif
namespace FM
{
// ---------------------------------------------------------------------------
// OPNBase
#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB)
uint32 OPNBase::lfotable[8]; // OPNA/B <20>p
OPNBase::OPNBase()
{
prescale = 0;
}
// <09>p<EFBFBD><70><EFBFBD><EFBFBD><EFBFBD>[<5B>^<5E>Z<EFBFBD>b<EFBFBD>g
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: // 30-3E DT/MULTI
op->SetDT((data >> 4) & 0x07);
op->SetMULTI(data & 0x0f);
break;
case 4: // 40-4E TL
op->SetTL(data & 0x7f, (regtc & 0x80) && (csmch == ch));
break;
case 5: // 50-5E KS/AR
op->SetKS((data >> 6) & 3);
op->SetAR((data & 0x1f) * 2);
break;
case 6: // 60-6E DR/AMON
op->SetDR((data & 0x1f) * 2);
op->SetAMON((data & 0x80) != 0);
break;
case 7: // 70-7E SR
op->SetSR((data & 0x1f) * 2);
break;
case 8: // 80-8E SL/RR
op->SetSL(sltable[(data >> 4) & 15]);
op->SetRR((data & 0x0f) * 4 + 2);
break;
case 9: // 90-9E SSG-EC
op->SetSSGEC(data & 0x0f);
break;
}
}
}
// <09><><EFBFBD>Z<EFBFBD>b<EFBFBD>g
void OPNBase::Reset()
{
status = 0;
SetPrescaler(0);
Timer::Reset();
psg.Reset();
}
// <09>v<EFBFBD><76><EFBFBD>X<EFBFBD>P<EFBFBD>[<5B><><EFBFBD>ݒ<EFBFBD>
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 };
// 512
if (prescale != p)
{
prescale = p;
/*warning: comparison is always true due to limited range of data type [-Wtype-limits]*/
// assert(0 <= prescale && prescale < 3);
assert(prescale < 3);
uint fmclock = clock / table[p][0] / 12;
rate = psgrate;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD><67><EFBFBD>Əo<C68F>͎<EFBFBD><CD8E>g<EFBFBD><67><EFBFBD>̔<EFBFBD>
assert(fmclock < (0x80000000 >> FM_RATIOBITS));
uint ratio = ((fmclock << FM_RATIOBITS) + rate/2) / rate;
SetTimerBase(fmclock);
// MakeTimeTable(ratio);
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];
}
}
}
// <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
bool OPNBase::Init(uint c, uint r)
{
clock = c;
psgrate = r;
return true;
}
// <09><><EFBFBD>ʐݒ<CA90>
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;
}
// <09>^<5E>C<EFBFBD>}<7D>[<5B><><EFBFBD>ԏ<EFBFBD><D48F><EFBFBD>
void OPNBase::TimerA()
{
if (regtc & 0x80)
{
csmch->KeyControl(0x00);
csmch->KeyControl(0x0f);
}
}
void OPNBase::DataSave(struct OPNBaseData* data) {
Timer::DataSave(&data->timer);
data->fmvolume = fmvolume;
data->clock = clock;
data->rate = rate;
data->psgrate = psgrate;
data->status = status;
data->prescale = prescale;
chip.DataSave(&data->chip);
psg.DataSave(&data->psg);
}
void OPNBase::DataLoad(struct OPNBaseData* data) {
Timer::DataLoad(&data->timer);
fmvolume = data->fmvolume;
clock = data->clock;
rate = data->rate;
psgrate = data->psgrate;
status = data->status;
prescale = data->prescale;
chip.DataLoad(&data->chip);
psg.DataLoad(&data->psg);
}
#endif // defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB)
// ---------------------------------------------------------------------------
// YM2203
//
#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);
}
}
// <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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);
return true;
}
// <09>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>[<5B>g<EFBFBD>ύX
bool OPN::SetRate(uint c, uint r, bool)
{
OPNBase::Init(c, r);
RebuildTimeTable();
return true;
}
// <09><><EFBFBD>Z<EFBFBD>b<EFBFBD>g
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();
}
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>ǂݍ<C782><DD8D><EFBFBD>
uint OPN::GetReg(uint addr)
{
if (addr < 0x10)
return psg.GetReg(addr);
else
return 0;
}
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>A<EFBFBD><41><EFBFBD>C<EFBFBD>Ƀf<C983>[<5B>^<5E><>ݒ<EFBFBD>
void OPN::SetReg(uint addr, uint data)
{
// LOG2("reg[%.2x] <- %.2x\n", addr, 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: // Key On/Off
if ((data & 3) < 3)
ch[data & 3].KeyControl(data >> 4);
break;
case 0x2d: case 0x2e: case 0x2f:
SetPrescaler(addr-0x2d);
break;
// F-Number
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;
}
}
// <09>X<EFBFBD>e<EFBFBD>[<5B>^<5E>X<EFBFBD>t<EFBFBD><74><EFBFBD>O<EFBFBD>ݒ<EFBFBD>
void OPN::SetStatus(uint bits)
{
if (!(status & bits))
{
status |= bits;
Intr(true);
}
}
void OPN::ResetStatus(uint bit)
{
status &= ~bit;
if (!status)
Intr(false);
}
// <09>}<7D>X<EFBFBD>N<EFBFBD>ݒ<EFBFBD>
void OPN::SetChannelMask(uint mask)
{
for (int i=0; i<3; i++)
ch[i].Mute(!!(mask & (1 << i)));
psg.SetChannelMask(mask >> 6);
}
void OPN::DataSave(struct OPNData* data) {
OPNBase::DataSave(&data->opnbase);
memcpy(data->fnum, fnum, sizeof(uint) * 3);
memcpy(data->fnum3, fnum3, sizeof(uint) * 3);
memcpy(data->fnum2, fnum2, 6);
for(int i = 0; i < 3; i++) {
ch[i].DataSave(&data->ch[i]);
}
}
void OPN::DataLoad(struct OPNData* data) {
OPNBase::DataLoad(&data->opnbase);
memcpy(fnum, data->fnum, sizeof(uint) * 3);
memcpy(fnum3, data->fnum3, sizeof(uint) * 3);
memcpy(fnum2, data->fnum2, 6);
for(int i = 0; i < 3; i++) {
ch[i].DataLoad(&data->ch[i]);
}
csmch = &ch[2];
for (int i=0; i<3; i++)
{
ch[i].SetChip(&chip);
ch[i].SetType(typeN);
}
}
// <09><><EFBFBD><EFBFBD>(2ch)
void OPN::Mix(Sample* buffer, int nsamples)
{
//printf("M:%d\n",nsamples);
#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14)
psg.Mix(buffer, nsamples);
// Set F-Number
ch[0].SetFNum(fnum[0]);
ch[1].SetFNum(fnum[1]);
if (!(regtc & 0xc0))
ch[2].SetFNum(fnum[2]);
else
{ // <20><><EFBFBD>ʉ<EFBFBD>
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();
//printf("a %X\n",actch);
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], s);
StoreSample(dest[1], s);
//printf("%08X,%08X\n",dest[0],dest[1]);
}
}
#undef IStoSample
}
#endif // BUILD_OPN
// ---------------------------------------------------------------------------
// YM2608/2610 common part
// ---------------------------------------------------------------------------
#if defined(BUILD_OPNA) || defined(BUILD_OPNB)
int OPNABase::amtable[FM_LFOENTS] = { -1, };
int OPNABase::pmtable[FM_LFOENTS];
int32 OPNABase::tltable[FM_TLENTS+FM_TLPOS];
bool OPNABase::tablehasmade = false;
OPNABase::OPNABase()
{
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()
{
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//
bool OPNABase::Init(uint, uint, bool)
{
RebuildTimeTable();
Reset();
SetVolumeFM(0);
SetVolumePSG(0);
SetChannelMask(0);
return true;
}
// ---------------------------------------------------------------------------
// <09>e<EFBFBD>[<5B>u<EFBFBD><75><EFBFBD>
//
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;
}
}
// libOPNMIDI: soft panning
static const uint16 panlawtable[] =
{
65535, 65529, 65514, 65489, 65454, 65409, 65354, 65289,
65214, 65129, 65034, 64929, 64814, 64689, 64554, 64410,
64255, 64091, 63917, 63733, 63540, 63336, 63123, 62901,
62668, 62426, 62175, 61914, 61644, 61364, 61075, 60776,
60468, 60151, 59825, 59489, 59145, 58791, 58428, 58057,
57676, 57287, 56889, 56482, 56067, 55643, 55211, 54770,
54320, 53863, 53397, 52923, 52441, 51951, 51453, 50947,
50433, 49912, 49383, 48846, 48302, 47750, 47191,
46340, /* Center left */
46340, /* Center right */
45472, 44885, 44291, 43690, 43083, 42469, 41848, 41221,
40588, 39948, 39303, 38651, 37994, 37330, 36661, 35986,
35306, 34621, 33930, 33234, 32533, 31827, 31116, 30400,
29680, 28955, 28225, 27492, 26754, 26012, 25266, 24516,
23762, 23005, 22244, 21480, 20713, 19942, 19169, 18392,
17613, 16831, 16046, 15259, 14469, 13678, 12884, 12088,
11291, 10492, 9691, 8888, 8085, 7280, 6473, 5666,
4858, 4050, 3240, 2431, 1620, 810, 0
};
// ---------------------------------------------------------------------------
// <09><><EFBFBD>Z<EFBFBD>b<EFBFBD>g
//
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;
panvolume_l[i] = panvolume_r[i] = panlawtable[64];
ch[i].Reset();
}
stmask = ~0x1c;
statusnext = 0;
memaddr = 0;
adpcmd = 127;
adpcmx = 0;
lfocount = 0;
adpcmplay = false;
adplc = 0;
adpld = 0x100;
status = 0;
UpdateStatus();
}
// ---------------------------------------------------------------------------
// <09>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>[<5B>g<EFBFBD>ύX
//
bool OPNABase::SetRate(uint c, uint r, bool)
{
c /= 2; // <20>]<5D><><EFBFBD>łƂ̌݊<CC8C><DD8A><EFBFBD><EFBFBD><EFBFBD><EFBFBD>d<EFBFBD><64><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>R<EFBFBD><52><EFBFBD><EFBFBD><EFBFBD>g<EFBFBD>A<EFBFBD>E<EFBFBD>g<EFBFBD><67><EFBFBD>
OPNBase::Init(c, r);
adplbase = int(8192. * (clock/72.) / r);
adpld = deltan * adplbase >> 16;
RebuildTimeTable();
lfodcount = reg22 & 0x08 ? lfotable[reg22 & 7] : 0;
return true;
}
// ---------------------------------------------------------------------------
// <09>`<60><><EFBFBD><EFBFBD><EFBFBD>l<EFBFBD><6C><EFBFBD>}<7D>X<EFBFBD>N<EFBFBD>̐ݒ<CC90>
//
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::SetPan(uint c, uint8 p)
{
panvolume_l[c] = panlawtable[p & 0x7f];
panvolume_r[c] = panlawtable[0x7f - (p & 0x7f)];
}
// ---------------------------------------------------------------------------
void OPNABase::DataSave(struct OPNABaseData* data) {
OPNBase::DataSave(&data->opnbase);
memcpy(data->pan, pan, 6);
memcpy(data->panvolume_l, panvolume_l, sizeof(uint16) * 6);
memcpy(data->panvolume_r, panvolume_r, sizeof(uint16) * 6);
memcpy(data->fnum2, fnum2, 9);
data->reg22 = reg22;
data->reg29 = reg29;
data->stmask = stmask;
data->statusnext = statusnext;
data->lfocount = lfocount;
data->lfodcount = lfodcount;
memcpy(data->fnum, fnum, sizeof(uint) * 6);
memcpy(data->fnum3, fnum3, sizeof(uint) * 3);
data->is_adpcmbuf = 0;
if(adpcmbuf) {
data->is_adpcmbuf = 1;
memcpy(data->adpcmbuf, adpcmbuf, 0x40000);
}
data->adpcmmask = adpcmmask;
data->adpcmnotice = adpcmnotice;
data->startaddr = startaddr;
data->stopaddr = stopaddr;
data->memaddr = memaddr;
data->limitaddr = limitaddr;
data->adpcmlevel = adpcmlevel;
data->adpcmvolume = adpcmvolume;
data->adpcmvol = adpcmvol;
data->deltan = deltan;
data->adplc = adplc;
data->adpld = adpld;
data->adplbase = adplbase;
data->adpcmx = adpcmx;
data->adpcmd = adpcmd;
data->adpcmout = adpcmout;
data->apout0 = apout0;
data->apout1 = apout1;
data->adpcmreadbuf = adpcmreadbuf;
data->adpcmplay = adpcmplay;
data->granuality = granuality;
data->control1 = control1;
data->control2 = control2;
memcpy(data->adpcmreg, adpcmreg, 8);
data->rhythmmask_ = rhythmmask_;
for(int i = 0; i < 6; i++) {
ch[i].DataSave(&data->ch[i]);
}
}
// ---------------------------------------------------------------------------
void OPNABase::DataLoad(struct OPNABaseData* data) {
OPNBase::DataLoad(&data->opnbase);
memcpy(pan, data->pan, 6);
memcpy(panvolume_l, data->panvolume_l, sizeof(uint16) * 6);
memcpy(panvolume_r, data->panvolume_r, sizeof(uint16) * 6);
memcpy(fnum2, data->fnum2, 9);
reg22 = data->reg22;
reg29 = data->reg29;
stmask = data->stmask;
statusnext = data->statusnext;
lfocount = data->lfocount;
lfodcount = data->lfodcount;
memcpy(fnum, data->fnum, sizeof(uint) * 6);
memcpy(fnum3, data->fnum3, sizeof(uint) * 3);
if(data->is_adpcmbuf) {
if(!adpcmbuf)
adpcmbuf = new uint8[0x40000];
memcpy(adpcmbuf, data->adpcmbuf, 0x40000);
}
adpcmmask = data->adpcmmask;
adpcmnotice = data->adpcmnotice;
startaddr = data->startaddr;
stopaddr = data->stopaddr;
memaddr = data->memaddr;
limitaddr = data->limitaddr;
adpcmlevel = data->adpcmlevel;
adpcmvolume = data->adpcmvolume;
adpcmvol = data->adpcmvol;
deltan = data->deltan;
adplc = data->adplc;
adpld = data->adpld;
adplbase = data->adplbase;
adpcmx = data->adpcmx;
adpcmd = data->adpcmd;
adpcmout = data->adpcmout;
apout0 = data->apout0;
apout1 = data->apout1;
adpcmreadbuf = data->adpcmreadbuf;
adpcmplay = data->adpcmplay;
granuality = data->granuality;
control1 = data->control1;
control2 = data->control2;
memcpy(adpcmreg, data->adpcmreg, 8);
rhythmmask_ = data->rhythmmask_;
for(int i = 0; i < 6; i++) {
ch[i].DataLoad(&data->ch[i]);
ch[i].SetChip(&chip);
}
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>A<EFBFBD><41><EFBFBD>C<EFBFBD>Ƀf<C983>[<5B>^<5E><>ݒ<EFBFBD>
//
void OPNABase::SetReg(uint addr, uint data)
{
int c = addr & 3;
switch (addr)
{
uint modified;
// Timer -----------------------------------------------------------------
case 0x24: case 0x25:
SetTimerA(addr, data);
break;
case 0x26:
SetTimerB(data);
break;
case 0x27:
SetTimerControl(data);
break;
// Misc ------------------------------------------------------------------
case 0x28: // Key On/Off
if ((data & 3) < 3)
{
c = (data & 3) + (data & 4 ? 3 : 0);
ch[c].KeyControl(data >> 4);
}
break;
// Status Mask -----------------------------------------------------------
case 0x29:
reg29 = data;
// UpdateStatus(); //?
break;
// Prescaler -------------------------------------------------------------
case 0x2d: case 0x2e: case 0x2f:
SetPrescaler(addr-0x2d);
break;
// F-Number --------------------------------------------------------------
case 0x1a0: case 0x1a1: case 0x1a2:
c += 3;
// fall through
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;
// fall through
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;
// Algorithm -------------------------------------------------------------
case 0x1b0: case 0x1b1: case 0x1b2:
c += 3;
// fall through
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;
// fall through
case 0xb4: case 0xb5: case 0xb6:
pan[c] = (data >> 6) & 3;
ch[c].SetMS(data);
break;
// LFO -------------------------------------------------------------------
case 0x22:
modified = reg22 ^ data;
reg22 = data;
if (modified & 0x8)
lfocount = 0;
lfodcount = reg22 & 8 ? lfotable[reg22 & 7] : 0;
break;
// PSG -------------------------------------------------------------------
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;
// <20><><EFBFBD>F ------------------------------------------------------------------
default:
if (c < 3)
{
if (addr & 0x100)
c += 3;
OPNBase::SetParameter(&ch[c], addr, data);
}
break;
}
}
// ---------------------------------------------------------------------------
// ADPCM B
//
void OPNABase::SetADPCMBReg(uint addr, uint data)
{
switch (addr)
{
case 0x00: // Control Register 1
if ((data & 0x80) && !adpcmplay)
{
adpcmplay = true;
memaddr = startaddr;
adpcmx = 0, adpcmd = 127;
adplc = 0;
}
if (data & 1)
{
adpcmplay = false;
}
control1 = data;
break;
case 0x01: // Control Register 2
control2 = data;
granuality = control2 & 2 ? 1 : 4;
break;
case 0x02: // Start Address L
case 0x03: // Start Address H
adpcmreg[addr - 0x02 + 0] = data;
startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 6;
memaddr = startaddr;
// LOG1(" startaddr %.6x", startaddr);
break;
case 0x04: // Stop Address L
case 0x05: // Stop Address H
adpcmreg[addr - 0x04 + 2] = data;
stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 6;
// LOG1(" stopaddr %.6x", stopaddr);
break;
case 0x08: // ADPCM data
if ((control1 & 0x60) == 0x60)
{
// LOG2(" Wr [0x%.5x] = %.2x", memaddr, data);
WriteRAM(data);
}
break;
case 0x09: // delta-N L
case 0x0a: // delta-N H
adpcmreg[addr - 0x09 + 4] = data;
deltan = adpcmreg[5]*256+adpcmreg[4];
deltan = Max(256, deltan);
adpld = deltan * adplbase >> 16;
break;
case 0x0b: // Level Control
adpcmlevel = data;
adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
break;
case 0x0c: // Limit Address L
case 0x0d: // Limit Address H
adpcmreg[addr - 0x0c + 6] = data;
limitaddr = (adpcmreg[7]*256+adpcmreg[6] + 1) << 6;
// LOG1(" limitaddr %.6x", limitaddr);
break;
case 0x10: // Flag Control
if (data & 0x80)
{
status = 0;
UpdateStatus();
}
else
{
stmask = ~(data & 0x1f);
// UpdateStatus(); //???
}
break;
}
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>
//
uint OPNA::GetReg(uint addr)
{
if (addr < 0x10)
return psg.GetReg(addr);
if (addr == 0x108)
{
// LOG1("%d:reg[108] -> ", Diag::GetCPUTick());
uint data = adpcmreadbuf & 0xff;
adpcmreadbuf >>= 8;
if ((control1 & 0x60) == 0x20)
{
adpcmreadbuf |= ReadRAM() << 8;
// LOG2("Rd [0x%.6x:%.2x] ", memaddr, adpcmreadbuf >> 8);
}
// LOG0("%.2x\n");
return data;
}
if (addr == 0xff)
return 1;
return 0;
}
// ---------------------------------------------------------------------------
// <09>X<EFBFBD>e<EFBFBD>[<5B>^<5E>X<EFBFBD>t<EFBFBD><74><EFBFBD>O<EFBFBD>ݒ<EFBFBD>
//
void OPNABase::SetStatus(uint bits)
{
if (!(status & bits))
{
// LOG2("SetStatus(%.2x %.2x)\n", bits, stmask);
status |= bits & stmask;
UpdateStatus();
}
// else
// LOG1("SetStatus(%.2x) - ignored\n", bits);
}
void OPNABase::ResetStatus(uint bits)
{
status &= ~bits;
// LOG1("ResetStatus(%.2x)\n", bits);
UpdateStatus();
}
inline void OPNABase::UpdateStatus()
{
// LOG2("%d:INT = %d\n", Diag::GetCPUTick(), (status & stmask & reg29) != 0);
Intr((status & stmask & reg29) != 0);
}
// ---------------------------------------------------------------------------
// ADPCM RAM <20>ւ̏<D682><CC8F><EFBFBD><EFBFBD>ݑ<EFBFBD><DD91><EFBFBD>
//
void OPNABase::WriteRAM(uint data)
{
#ifndef NO_BITTYPE_EMULATION
if (!(control2 & 2))
{
// 1 bit mode
adpcmbuf[(memaddr >> 4) & 0x3ffff] = data;
memaddr += 16;
}
else
{
// 8 bit mode
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; // EOS
memaddr &= 0x3fffff;
}
if (memaddr == limitaddr)
{
// LOG1("Limit ! (%.8x)\n", limitaddr);
memaddr = 0;
}
SetStatus(8);
}
// ---------------------------------------------------------------------------
// ADPCM RAM <20><><EFBFBD><EFBFBD>̓ǂݍ<C782><DD8D>ݑ<EFBFBD><DD91><EFBFBD>
//
uint OPNABase::ReadRAM()
{
uint data;
#ifndef NO_BITTYPE_EMULATION
if (!(control2 & 2))
{
// 1 bit mode
data = adpcmbuf[(memaddr >> 4) & 0x3ffff];
memaddr += 16;
}
else
{
// 8 bit mode
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; // EOS
memaddr &= 0x3fffff;
}
if (memaddr == limitaddr)
{
// LOG1("Limit ! (%.8x)\n", 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;
}
// ---------------------------------------------------------------------------
// ADPCM RAM <20><><EFBFBD><EFBFBD><EFBFBD> nibble <20>ǂݍ<C782><DD8D>݋y<DD8B><79> ADPCM <20>W<EFBFBD>J
//
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);
// check
if (memaddr == stopaddr)
{
if (control1 & 0x10)
{
memaddr = startaddr;
data = adpcmx;
adpcmx = 0, adpcmd = 127;
return data;
}
else
{
memaddr &= adpcmmask; //0x3fffff;
SetStatus(adpcmnotice);
adpcmplay = false;
}
}
if (memaddr == limitaddr)
memaddr = 0;
return adpcmx;
}
// ---------------------------------------------------------------------------
// <09>g<EFBFBD><67><EFBFBD>X<EFBFBD>e<EFBFBD>[<5B>^<5E>X<EFBFBD><58>ǂ݂<C782><DD82><EFBFBD>
//
uint OPNABase::ReadStatusEx()
{
uint r = ((status | 8) & stmask) | (adpcmplay ? 0x20 : 0);
status |= statusnext;
statusnext = 0;
return r;
}
// ---------------------------------------------------------------------------
// ADPCM <20>W<EFBFBD>J
//
inline void OPNABase::DecodeADPCMB()
{
apout0 = apout1;
int n = (ReadRAMN() * adpcmvolume) >> 13;
apout1 = adpcmout + n;
adpcmout = n;
}
// ---------------------------------------------------------------------------
// ADPCM <20><><EFBFBD><EFBFBD>
//
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)
{
// LOG2("ADPCM Play: %d DeltaN: %d\n", adpld, deltan);
if (adpld <= 8192) // fplay < fsamp
{
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 // fplay > fsamp (adpld = fplay/famp*8192)
{
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;
}
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD>
// in: buffer <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// nsamples <09><><EFBFBD><EFBFBD><EFBFBD>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD>
//
void OPNABase::FMMix(Sample* buffer, int nsamples)
{
if (fmvolume > 0)
{
// <20><><EFBFBD><EFBFBD>
// Set F-Number
if (!(regtc & 0xc0))
csmch->SetFNum(fnum[csmch-ch]);
else
{
// <20><><EFBFBD>ʉ<EFBFBD><CA89><EFBFBD><EFBFBD>[<5B>h
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 (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()
{
// LOG3("%4d - %8d, %8d\n", c, lfocount, lfodcount);
// Operator::SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
// Operator::SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
chip.SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
chip.SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
lfocount += lfodcount;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD>
//
#define IStoSample(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14)
void OPNABase::Mix6(Sample* buffer, int nsamples, int activech)
{
// Mix
// libOPNMIDI: rewrite for panning support
const uint activechmask[6] = {0x001, 0x004, 0x010, 0x040, 0x100, 0x400};
Sample* limit = buffer + nsamples * 2;
for (Sample* dest = buffer; dest < limit; dest+=2)
{
ISample out[6];
if (activech & 0xaaa)
{
LFO();
for (uint c = 0; c<6; ++c)
out[c] = (activechmask[c] & activech) ? ch[c].CalcL() : 0;
}
else
{
for (uint c = 0; c<6; ++c)
out[c] = (activechmask[c] & activech) ? ch[c].Calc() : 0;
}
int lrouts[2] = {0, 0};
for (uint c = 0; c<6; ++c)
{
int panl = panvolume_l[c];
int panr = panvolume_r[c];
panl = (pan[c] & 2) ? panl : 0;
panr = (pan[c] & 1) ? panr : 0;
lrouts[0] += out[c] * panl / 65535;
lrouts[1] += out[c] * panr / 65535;
}
StoreSample(dest[0], lrouts[0]);
StoreSample(dest[1], lrouts[1]);
}
}
#endif // defined(BUILD_OPNA) || defined(BUILD_OPNB)
// ---------------------------------------------------------------------------
// YM2608(OPNA)
// ---------------------------------------------------------------------------
#ifdef BUILD_OPNA
// ---------------------------------------------------------------------------
// <09>\<5C>z
//
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;
}
rhythmtvol = 0;
adpcmmask = 0x3ffff;
adpcmnotice = 4;
csmch = &ch[2];
}
// ---------------------------------------------------------------------------
OPNA::~OPNA()
{
delete[] adpcmbuf;
for (int i=0; i<6; i++)
delete[] rhythm[i].sample;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//
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(0, 0);
return true;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>Z<EFBFBD>b<EFBFBD>g
//
void OPNA::Reset()
{
reg29 = 0x1f;
rhythmkey = 0;
limitaddr = 0x3ffff;
OPNABase::Reset();
}
// ---------------------------------------------------------------------------
// <09>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>[<5B>g<EFBFBD>ύX
//
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;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>Y<EFBFBD><59><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǂ݂<C782><DD82><EFBFBD>
//
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 = ~0u;
for (i=0; i<6; i++)
{
FileIO file;
uint32 fsize;
char buf[MAX_PATH + 1] = "";
if (path)
strncpy(buf, path, MAX_PATH);
strncat(buf, "2608_", MAX_PATH);
strncat(buf, rhythmname[i], MAX_PATH);
strncat(buf, ".wav", MAX_PATH);
if (!file.Open(buf, FileIO::readonly))
{
if (i != 5)
break;
if (path)
strncpy(buf, path, MAX_PATH);
strncpy(buf, "2608_rym.wav", MAX_PATH);
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++)
{
delete[] rhythm[i].sample;
rhythm[i].sample = 0;
}
return false;
}
return true;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>A<EFBFBD><41><EFBFBD>C<EFBFBD>Ƀf<C983>[<5B>^<5E><>ݒ<EFBFBD>
//
void OPNA::SetReg(uint addr, uint data)
{
addr &= 0x1ff;
switch (addr)
{
case 0x29:
reg29 = data;
// UpdateStatus(); //?
break;
// Rhythm ----------------------------------------------------------------
case 0x10: // DM/KEYON
if (!(data & 0x80)) // KEY ON
{
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
{ // DUMP
rhythmkey &= ~data;
}
break;
case 0x11:
rhythmtl = ~data & 63;
break;
case 0x18: // Bass Drum
case 0x19: // Snare Drum
case 0x1a: // Top Cymbal
case 0x1b: // Hihat
case 0x1c: // Tom-tom
case 0x1d: // Rim shot
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::DataSave(struct OPNAData* data) {
OPNABase::DataSave(&data->opnabase);
memcpy(data->rhythm, rhythm, sizeof(Rhythm) * 6);
data->rhythmtl = rhythmtl;
data->rhythmtvol = rhythmtvol;
data->rhythmkey = rhythmkey;
}
// ---------------------------------------------------------------------------
void OPNA::DataLoad(struct OPNAData* data) {
OPNABase::DataLoad(&data->opnabase);
memcpy(rhythm, data->rhythm, sizeof(Rhythm) * 6);
rhythmtl = data->rhythmtl;
rhythmtvol = data->rhythmtvol;
rhythmkey = data->rhythmkey;
csmch = &ch[2];
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>Y<EFBFBD><59><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//
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*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max
{
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);
}
}
}
}
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>ʐݒ<CA90>
//
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;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD>
// in: buffer <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// nsamples <09><><EFBFBD><EFBFBD><EFBFBD>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD>
//
void OPNA::Mix(Sample* buffer, int nsamples)
{
FMMix(buffer, nsamples);
psg.Mix(buffer, nsamples);
ADPCMBMix(buffer, nsamples);
RhythmMix(buffer, nsamples);
}
#endif // BUILD_OPNA
// ---------------------------------------------------------------------------
// YM2610(OPNB)
// ---------------------------------------------------------------------------
#ifdef BUILD_OPNB
// ---------------------------------------------------------------------------
// <09>\<5C>z
//
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()
{
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//
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++) // max 16M bytes
{
if (_adpcmb_size <= (1 << i))
{
adpcmmask = (1 << i) - 1;
break;
}
}
// adpcmmask = _adpcmb_size - 1;
limitaddr = adpcmmask;
Reset();
SetVolumeFM(0);
SetVolumePSG(0);
SetVolumeADPCMB(0);
SetVolumeADPCMATotal(0);
for (i=0; i<6; i++)
SetVolumeADPCMA(0, 0);
SetChannelMask(0);
return true;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>Z<EFBFBD>b<EFBFBD>g
//
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;
}
}
// ---------------------------------------------------------------------------
// <09>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD><EFBFBD>O<EFBFBD><4F><EFBFBD>[<5B>g<EFBFBD>ύX
//
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;
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>A<EFBFBD><41><EFBFBD>C<EFBFBD>Ƀf<C983>[<5B>^<5E><>ݒ<EFBFBD>
//
void OPNB::SetReg(uint addr, uint data)
{
addr &= 0x1ff;
switch (addr)
{
// omitted registers
case 0x29:
case 0x2d: case 0x2e: case 0x2f:
break;
// ADPCM A ---------------------------------------------------------------
case 0x100: // DM/KEYON
if (!(data & 0x80)) // KEY ON
{
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 = 0x10000 - adpcma[c].step;
adpcma[c].step = 0;
adpcma[c].adpcmx = 0;
adpcma[c].adpcmd = 0;
adpcma[c].nibble = 0;
}
}
}
else
{ // DUMP
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: // START ADDRESS (L)
case 0x113: case 0x114: case 0x115:
case 0x118: case 0x119: case 0x11a: // START ADDRESS (H)
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: // END ADDRESS (L)
case 0x123: case 0x124: case 0x125:
case 0x128: case 0x129: case 0x12a: // END ADDRESS (H)
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;
// ADPCMB -----------------------------------------------------------------
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: // Control Register 2
control2 = data & 0xc0;
break;
case 0x12: // Start Address L
case 0x13: // Start Address H
adpcmreg[addr - 0x12 + 0] = data;
startaddr = (adpcmreg[1]*256+adpcmreg[0]) << 9;
memaddr = startaddr;
break;
case 0x14: // Stop Address L
case 0x15: // Stop Address H
adpcmreg[addr - 0x14 + 2] = data;
stopaddr = (adpcmreg[3]*256+adpcmreg[2] + 1) << 9;
// LOG1(" stopaddr %.6x", stopaddr);
break;
case 0x19: // delta-N L
case 0x1a: // delta-N H
adpcmreg[addr - 0x19 + 4] = data;
deltan = adpcmreg[5]*256+adpcmreg[4];
deltan = Max(256, deltan);
adpld = deltan * adplbase >> 16;
break;
case 0x1b: // Level Control
adpcmlevel = data;
adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
break;
case 0x1c: // Flag Control
stmask = ~((data & 0xbf) << 8);
status &= stmask;
UpdateStatus();
break;
default:
OPNABase::SetReg(addr, data);
break;
}
// LOG0("\n");
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>W<EFBFBD>X<EFBFBD>^<5E>
//
uint OPNB::GetReg(uint addr)
{
if (addr < 0x10)
return psg.GetReg(addr);
return 0;
}
// ---------------------------------------------------------------------------
// <09>g<EFBFBD><67><EFBFBD>X<EFBFBD>e<EFBFBD>[<5B>^<5E>X<EFBFBD><58>ǂ݂<C782><DD82><EFBFBD>
//
uint OPNB::ReadStatusEx()
{
return (status & stmask) >> 8;
}
// ---------------------------------------------------------------------------
// YM2610
//
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;
}
}
}
// ---------------------------------------------------------------------------
// ADPCMA <20><><EFBFBD><EFBFBD>
//
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*/) // libOPNMIDI: Useless condition, signed int 8t has 127 max
{
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);
}
}
}
}
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD>ʐݒ<CA90>
//
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, db / 40.0));
else
adpcmvol = 0;
}
// ---------------------------------------------------------------------------
void OPNB::DataSave(struct OPNBData* data, void* adpcmadata) {
OPNABase::DataSave(&data->opnabase);
if(adpcmasize) {
adpcmadata = malloc(adpcmasize);
memcpy(adpcmadata, adpcmabuf, adpcmasize);
}
data->adpcmasize = adpcmasize;
memcpy(data->adpcma, adpcma, sizeof(ADPCMA) * 6);
data->adpcmatl = adpcmatl;
data->adpcmatvol = adpcmatvol;
data->adpcmakey = adpcmakey;
data->adpcmastep = adpcmastep;
memcpy(data->adpcmareg, adpcmareg, 32);
for(int i = 0; i < 6; i++) {
ch[i].DataSave(&data->ch[i]);
}
}
// ---------------------------------------------------------------------------
void OPNB::DataLoad(struct OPNBData* data, void* adpcmadata) {
OPNABase::DataLoad(&data->opnabase); // libOPNMIDI: bugfix DataLoad (was DataSave here)
if(data->adpcmasize) {
adpcmabuf = (uint8*)malloc(data->adpcmasize);
memcpy(adpcmabuf, adpcmadata, data->adpcmasize);
}
adpcmasize = data->adpcmasize;
memcpy(adpcma, data->adpcma, sizeof(ADPCMA) * 6);
adpcmatl = data->adpcmatl;
adpcmatvol = data->adpcmatvol;
adpcmakey = data->adpcmakey;
adpcmastep = data->adpcmastep;
memcpy(adpcmareg, data->adpcmareg, 32);
for(int i = 0; i < 6; i++) {
ch[i].DataLoad(&data->ch[i]);
}
csmch = &ch[2];
}
// ---------------------------------------------------------------------------
// <09><><EFBFBD><EFBFBD>
// in: buffer <09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// nsamples <09><><EFBFBD><EFBFBD><EFBFBD>T<EFBFBD><54><EFBFBD>v<EFBFBD><76><EFBFBD><EFBFBD>
//
void OPNB::Mix(Sample* buffer, int nsamples)
{
FMMix(buffer, nsamples);
psg.Mix(buffer, nsamples);
ADPCMBMix(buffer, nsamples);
ADPCMAMix(buffer, nsamples);
}
#endif // BUILD_OPNB
} // namespace FM