mirror of
https://github.com/ZDoom/ZMusic.git
synced 2024-12-11 13:00:53 +00:00
72c23d98a3
## 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
2142 lines
47 KiB
C++
2142 lines
47 KiB
C++
// ---------------------------------------------------------------------------
|
||
// 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
|