#include "adlib.h"
const uint8_t CadlibDriver::percMasks[5] =
{0x10, 0x08, 0x04, 0x02, 0x01};
uint8_t CadlibDriver::pianoParamsOp0[nbLocParam] =
{ 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 };
uint8_t CadlibDriver::pianoParamsOp1[nbLocParam] =
{ 0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 };
uint8_t CadlibDriver::bdOpr0[nbLocParam] =
{ 0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0 };
uint8_t CadlibDriver::bdOpr1[nbLocParam] =
{ 0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0 };
uint8_t CadlibDriver::sdOpr[nbLocParam] =
{ 0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0 };
uint8_t CadlibDriver::tomOpr[nbLocParam] =
{ 0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 };
uint8_t CadlibDriver::cymbOpr[nbLocParam] =
{ 0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0 };
uint8_t CadlibDriver::hhOpr[nbLocParam] =
{ 0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 };
uint8_t CadlibDriver::slotVoice[9][2] = {
{ 0, 3 },
{ 1, 4 },
{ 2, 5 },
{ 6, 9 },
{ 7, 10 },
{ 8, 11 },
{ 12, 15 },
{ 13, 16 },
{ 14, 17 }
};
uint8_t CadlibDriver::slotPerc[5][2] = {
{ 12, 15 },
{ 16, 0 },
{ 14, 0 },
{ 17, 0 },
{ 13, 0 }
};
uint8_t CadlibDriver::offsetSlot[18] = {
0, 1, 2, 3, 4, 5,
8, 9, 10, 11, 12, 13,
16, 17, 18, 19, 20, 21
};
uint8_t CadlibDriver::operSlot[18] = {
0, 0, 0,
1, 1, 1,
0, 0, 0,
1, 1, 1,
0, 0, 0,
1, 1, 1,
};
uint8_t CadlibDriver::voiceSlot[18] = {
0, 1, 2,
0, 1, 2,
3, 4, 5,
3, 4, 5,
6, 7, 8,
6, 7, 8,
};
void CadlibDriver::SoundWarmInit()
{
for (int i = 0; i < MAX_VOICES; i++)
{
fNumFreqPtr[i] = 0;
voiceKeyOn[i] = 0;
notePitch[i] = 0;
}
amDepth = 0;
vibDepth = 0;
noteSel = 0;
InitSlotVolume();
InitFNums();
SetMode(0);
SetGParam(0, 0, 0);
for (int i = 0; i < 9; i++)
SoundChut(i);
SetPitchRange(1);
SetWaveSel(1);
}
void CadlibDriver::SetMode(int mode)
{
if (mode)
{
SoundChut(BD);
SoundChut(SD);
SoundChut(TOM);
SetFreq(TOM, TOM_PITCH, 0);
SetFreq(SD, SD_PITCH, 0);
}
percussion = mode;
percBits = 0;
InitSlotParams();
SndSAmVibRhythm();
}
void CadlibDriver::SetWaveSel(int state)
{
modeWaveSel = state ? 0x20 : 0;
for (int i = 0; i < 18; i++)
opl->write(0xE0 + offsetSlot[i], 0);
opl->write(1, modeWaveSel);
}
void CadlibDriver::SetPitchRange(uint8_t pR)
{
if (pR > 12)
pR = 12;
if (pR < 1)
pR = 1;
pitchRange = pR;
pitchRangeStep = pitchRange * NR_STEP_PITCH;
}
void CadlibDriver::SetGParam(int amD, int vibD, int nSel)
{
amDepth = amD;
vibDepth = vibD;
noteSel = nSel;
SndSAmVibRhythm();
SndSNoteSel();
}
void CadlibDriver::SetVoiceTimbre(uint8_t voice, int16_t * paramArray)
{
int16_t wave0, wave1;
int16_t * prm1, * wavePtr;
wavePtr = paramArray + 2 * (nbLocParam - 1);
wave0 = *wavePtr++;
wave1 = *wavePtr;
prm1 = paramArray + nbLocParam - 1;
if (!percussion || voice < BD) {
SetSlotParam(slotVoice[voice][0], paramArray, wave0);
SetSlotParam(slotVoice[voice][1], prm1, wave1);
}
else if (voice == BD) {
SetSlotParam(slotPerc[0][0], paramArray, wave0);
SetSlotParam(slotPerc[0][1], prm1, wave1);
}
else
SetSlotParam(slotPerc[voice - BD][0], paramArray, wave0);
}
void CadlibDriver::SetVoiceVolume(uint8_t voice, uint8_t volume)
{
uint8_t slot;
#if 1
if (!percussion || voice < BD)
slot = slotVoice[voice][1];
else
slot = slotPerc[voice - BD][voice == BD ? 1 : 0];
if (volume > MAX_VOLUME)
volume = MAX_VOLUME;
slotRelVolume[slot] = volume;
SndSKslLevel(slot);
#else#endif
}
void CadlibDriver::SetVoicePitch(uint8_t voice, uint16_t pitchBend)
{
if (!percussion || voice <= BD) {
if (pitchBend > MAX_PITCH)
pitchBend = MAX_PITCH;
ChangePitch(voice, pitchBend);
SetFreq(voice, notePitch[voice], voiceKeyOn[voice]);
}
}
void CadlibDriver::NoteOn(uint8_t voice, int pitch)
{
pitch -= (MID_C - CHIP_MID_C);
if (pitch < 0)
pitch = 0;
if (pitch > 127)
pitch = 127;
if (voice < BD || !percussion)
SetFreq(voice, pitch, 1);
else {
if (voice == BD)
SetFreq(BD, pitch, 0);
else if (voice == TOM) {
SetFreq(TOM, pitch, 0);
SetFreq(SD, pitch + TOM_TO_SD, 0);
}
percBits |= percMasks[voice - BD];
SndSAmVibRhythm();
}
}
void CadlibDriver::NoteOff(uint8_t voice)
{
if (!percussion || voice < BD)
SetFreq(voice, notePitch[voice], 0);
else {
percBits &= ~percMasks[voice - BD];
SndSAmVibRhythm();
}
}
void CadlibDriver::InitSlotVolume()
{
for (int i = 0; i < 18; i++)
slotRelVolume[i] = MAX_VOLUME;
}
void CadlibDriver::InitFNums()
{
uint8_t i, j, k, num, numStep, pas;
numStep = 100 / NR_STEP_PITCH;
for (num = pas = 0; pas < NR_STEP_PITCH; pas++, num += numStep)
SetFNum(fNumNotes[pas], num, 100);
for (i = 0; i < MAX_VOICES; i++) {
fNumFreqPtr[i] = (uint16_t *)fNumNotes[0];
halfToneOffset[i] = 0;
}
k = 0;
for (i = 0; i < 8; i++)
for (j = 0; j < 12; j++, k++) {
noteDIV12[k] = i;
noteMOD12[k] = j;
}
}
void CadlibDriver::SoundChut(int voice)
{
opl->write(0xA0 + voice, 0);
opl->write(0xB0 + voice, 0);
}
void CadlibDriver::SetFreq(uint8_t voice, int pitch, uint8_t keyOn)
{
uint16_t fNbr, t1;
voiceKeyOn[voice] = keyOn;
notePitch[voice] = pitch;
pitch += halfToneOffset[voice];
if (pitch > 95)
pitch = 95;
if (pitch < 0)
pitch = 0;
fNbr = *(fNumFreqPtr[voice] + noteMOD12[pitch]);
opl->write(0xA0 + voice, fNbr & 0xFF);
t1 = keyOn ? 32 : 0;
t1 += (noteDIV12[pitch] << 2) + (0x3 & (fNbr >> 8));
opl->write(0xB0 + voice, t1);
}
void CadlibDriver::SndSAmVibRhythm()
{
uint8_t t1;
t1 = amDepth ? 0x80 : 0;
t1 |= vibDepth ? 0x40 : 0;
t1 |= percussion ? 0x20 : 0;
t1 |= percBits;
opl->write(0xBD, t1);
}
void CadlibDriver::SndSNoteSel()
{
opl->write(0x08, noteSel ? 64 : 0);
}
void CadlibDriver::SndSKslLevel(uint8_t slot)
{
unsigned t1;
t1 = 63 - (GetLocPrm(slot, prmLevel) & 0x3f);
t1 = slotRelVolume[slot] * t1;
t1 += t1 + MAX_VOLUME;
t1 = 63 - t1 / (2 * MAX_VOLUME);
t1 |= GetLocPrm(slot, prmKsl) << 6;
opl->write(0x40 + offsetSlot[slot], t1 & 0xFF);
}
void CadlibDriver::SndSFeedFm(uint8_t slot)
{
uint8_t t1;
if (operSlot[slot])
return;
t1 = GetLocPrm(slot, prmFeedBack) << 1;
t1 |= GetLocPrm(slot, prmFm) ? 0 : 1;
opl->write(0xC0 + (int)voiceSlot[slot], t1);
}
void CadlibDriver::SndSAttDecay(uint8_t slot)
{
uint8_t t1;
t1 = GetLocPrm(slot, prmAttack) << 4;
t1 |= GetLocPrm(slot, prmDecay) & 0xf;
opl->write(0x60 + (int)offsetSlot[slot], t1);
}
void CadlibDriver::SndSSusRelease(uint8_t slot)
{
uint8_t t1;
t1 = GetLocPrm(slot, prmSustain) << 4;
t1 |= GetLocPrm(slot, prmRelease) & 0xf;
opl->write(0x80 + (int)offsetSlot[slot], t1);
}
void CadlibDriver::SndSAVEK(uint8_t slot)
{
uint8_t t1;
t1 = GetLocPrm(slot, prmAm) ? 0x80 : 0;
t1 += GetLocPrm(slot, prmVib) ? 0x40 : 0;
t1 += GetLocPrm(slot, prmStaining) ? 0x20 : 0;
t1 += GetLocPrm(slot, prmKsr) ? 0x10 : 0;
t1 += GetLocPrm(slot, prmMulti) & 0xf;
opl->write(0x20 + (int)offsetSlot[slot], t1);
}
void CadlibDriver::SndWaveSelect(uint8_t slot)
{
uint8_t wave;
if (modeWaveSel)
wave = GetLocPrm(slot, prmWaveSel) & 0x03;
else
wave = 0;
opl->write(0xE0 + offsetSlot[slot], wave);
}
void CadlibDriver::SndSetAllPrm(uint8_t slot)
{
SndSAmVibRhythm();
SndSNoteSel();
SndSKslLevel(slot);
SndSFeedFm(slot);
SndSAttDecay(slot);
SndSSusRelease(slot);
SndSAVEK(slot);
SndWaveSelect(slot);
}
void CadlibDriver::SetSlotParam(uint8_t slot, int16_t * param, uint8_t waveSel)
{
for (int i = 0; i < nbLocParam - 1; i++)
paramSlot[slot][i] = *param++;
paramSlot[slot][nbLocParam - 1] = waveSel &= 0x3;
SndSetAllPrm(slot);
}
void CadlibDriver::SetCharSlotParam(uint8_t slot, uint8_t * cParam, uint8_t waveSel)
{
int16_t param[nbLocParam];
for (int i = 0; i < nbLocParam - 1; i++)
param[i] = *cParam++;
SetSlotParam(slot, param, waveSel);
}
void CadlibDriver::InitSlotParams()
{
for (int i = 0; i < 18; i++)
if (operSlot[i])
SetCharSlotParam(i, pianoParamsOp1, 0);
else
SetCharSlotParam(i, pianoParamsOp0, 0);
if (percussion)
{
SetCharSlotParam(12, bdOpr0, 0);
SetCharSlotParam(15, bdOpr1, 0);
SetCharSlotParam(16, sdOpr, 0);
SetCharSlotParam(14, tomOpr, 0);
SetCharSlotParam(17, cymbOpr, 0);
SetCharSlotParam(13, hhOpr, 0);
}
}
void CadlibDriver::SetFNum(uint16_t * fNumVec, int num, int den)
{
int i;
long val;
*fNumVec++ = (uint16_t)(4 + (val = CalcPremFNum(num, den))) >> 3;
for (i = 1; i < 12; i++) {
val *= 106;
*fNumVec++ = (uint16_t)(4 + (val /= 100)) >> 3;
}
}
void CadlibDriver::ChangePitch(int voice, int pitchBend)
{
int l, t1, t2, delta;
static int oldL = -1;
static int oldHt;
static uint16_t * oldPtr;
l = (int)(pitchBend - MID_PITCH) * pitchRangeStep;
if (oldL == l) {
fNumFreqPtr[voice] = oldPtr;
halfToneOffset[voice] = oldHt;
}
else {
t1 = l / MID_PITCH;
if (t1 < 0) {
t2 = NR_STEP_PITCH - 1 - t1;
oldHt = halfToneOffset[voice] = -(t2 / NR_STEP_PITCH);
delta = (t2 - NR_STEP_PITCH + 1) % NR_STEP_PITCH;
if (delta)
delta = NR_STEP_PITCH - delta;
}
else {
oldHt = halfToneOffset[voice] = t1 / NR_STEP_PITCH;
delta = t1 % NR_STEP_PITCH;
}
oldPtr = fNumFreqPtr[voice] = (uint16_t *)fNumNotes[delta];
oldL = l;
}
}
long CadlibDriver::CalcPremFNum(int numDeltaDemiTon, int denDeltaDemiTon)
{
long f8, fNum8, d100;
d100 = denDeltaDemiTon * 100;
f8 = (d100 + 6 * numDeltaDemiTon) * (26044L * 2L);
f8 /= d100 * 25;
fNum8 = f8 * 16384;
fNum8 *= 9L;
fNum8 /= 179L * 625L;
return fNum8;
}