mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-12-12 13:21:51 +00:00
87d46ddd11
## 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
1156 lines
28 KiB
C++
1156 lines
28 KiB
C++
// ---------------------------------------------------------------------------
|
|
// FM Sound Generator - Core Unit
|
|
// Copyright (C) cisc 1998, 2003.
|
|
// ---------------------------------------------------------------------------
|
|
// $Id: fmgen.cpp,v 1.49 2003/09/02 14:51:04 cisc Exp $
|
|
// ---------------------------------------------------------------------------
|
|
// 参考:
|
|
// FM sound generator for M.A.M.E., written by Tatsuyuki Satoh.
|
|
//
|
|
// 謎:
|
|
// OPNB の CSM モード(仕様がよくわからない)
|
|
//
|
|
// 制限:
|
|
// ・AR!=31 で SSGEC を使うと波形が実際と異なる可能性あり
|
|
//
|
|
// 謝辞:
|
|
// Tatsuyuki Satoh さん(fm.c)
|
|
// Hiromitsu Shioya さん(ADPCM-A)
|
|
// DMP-SOFT. さん(OPNB)
|
|
// KAJA さん(test program)
|
|
// ほか掲示板等で様々なご助言,ご支援をお寄せいただいた皆様に
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#include "fmgen_headers.h"
|
|
#include "fmgen_misc.h"
|
|
#include "fmgen_fmgen.h"
|
|
#include "fmgen_fmgeninl.h"
|
|
|
|
#define LOGNAME "fmgen"
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
#define FM_EG_BOTTOM 955
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Table/etc
|
|
//
|
|
namespace FM
|
|
{
|
|
const uint8 Operator::notetable[128] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3,
|
|
4, 4, 4, 4, 4, 4, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7,
|
|
8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11,
|
|
12, 12, 12, 12, 12, 12, 12, 13, 14, 15, 15, 15, 15, 15, 15, 15,
|
|
16, 16, 16, 16, 16, 16, 16, 17, 18, 19, 19, 19, 19, 19, 19, 19,
|
|
20, 20, 20, 20, 20, 20, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23,
|
|
24, 24, 24, 24, 24, 24, 24, 25, 26, 27, 27, 27, 27, 27, 27, 27,
|
|
28, 28, 28, 28, 28, 28, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31,
|
|
};
|
|
|
|
const int8 Operator::dttable[256] =
|
|
{
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4,
|
|
4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14, 16, 16, 16, 16,
|
|
2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10,
|
|
10, 12, 12, 14, 16, 16, 18, 20, 22, 24, 26, 28, 32, 32, 32, 32,
|
|
4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 12, 12, 14,
|
|
16, 16, 18, 20, 22, 24, 26, 28, 32, 34, 38, 40, 44, 44, 44, 44,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, -2, -2, -2, -2, -2, -2, -2, -2, -4, -4, -4, -4,
|
|
-4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,-16,-16,-16,-16,
|
|
-2, -2, -2, -2, -4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,
|
|
-10,-12,-12,-14,-16,-16,-18,-20,-22,-24,-26,-28,-32,-32,-32,-32,
|
|
-4, -4, -4, -4, -4, -6, -6, -6, -8, -8, -8,-10,-10,-12,-12,-14,
|
|
-16,-16,-18,-20,-22,-24,-26,-28,-32,-34,-38,-40,-44,-44,-44,-44,
|
|
};
|
|
|
|
const int8 Operator::decaytable1[64][8] =
|
|
{
|
|
{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0},
|
|
{1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1},
|
|
{1, 1, 1, 1, 1, 1, 1, 1}, {1, 1, 1, 1, 1, 1, 1, 1},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 0, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 0, 1, 0, 1, 0, 1, 0}, {1, 1, 1, 0, 1, 0, 1, 0},
|
|
{1, 1, 1, 0, 1, 1, 1, 0}, {1, 1, 1, 1, 1, 1, 1, 0},
|
|
{1, 1, 1, 1, 1, 1, 1, 1}, {2, 1, 1, 1, 2, 1, 1, 1},
|
|
{2, 1, 2, 1, 2, 1, 2, 1}, {2, 2, 2, 1, 2, 2, 2, 1},
|
|
{2, 2, 2, 2, 2, 2, 2, 2}, {4, 2, 2, 2, 4, 2, 2, 2},
|
|
{4, 2, 4, 2, 4, 2, 4, 2}, {4, 4, 4, 2, 4, 4, 4, 2},
|
|
{4, 4, 4, 4, 4, 4, 4, 4}, {8, 4, 4, 4, 8, 4, 4, 4},
|
|
{8, 4, 8, 4, 8, 4, 8, 4}, {8, 8, 8, 4, 8, 8, 8, 4},
|
|
{16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16},
|
|
{16,16,16,16,16,16,16,16}, {16,16,16,16,16,16,16,16},
|
|
};
|
|
|
|
const int Operator::decaytable2[16] =
|
|
{
|
|
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2047, 2047, 2047, 2047, 2047
|
|
};
|
|
|
|
const int8 Operator::attacktable[64][8] =
|
|
{
|
|
{-1,-1,-1,-1,-1,-1,-1,-1}, {-1,-1,-1,-1,-1,-1,-1,-1},
|
|
{ 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4},
|
|
{ 4, 4, 4, 4, 4, 4, 4, 4}, { 4, 4, 4, 4, 4, 4, 4, 4},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4,-1, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4,-1, 4,-1, 4,-1, 4,-1}, { 4, 4, 4,-1, 4,-1, 4,-1},
|
|
{ 4, 4, 4,-1, 4, 4, 4,-1}, { 4, 4, 4, 4, 4, 4, 4,-1},
|
|
{ 4, 4, 4, 4, 4, 4, 4, 4}, { 3, 4, 4, 4, 3, 4, 4, 4},
|
|
{ 3, 4, 3, 4, 3, 4, 3, 4}, { 3, 3, 3, 4, 3, 3, 3, 4},
|
|
{ 3, 3, 3, 3, 3, 3, 3, 3}, { 2, 3, 3, 3, 2, 3, 3, 3},
|
|
{ 2, 3, 2, 3, 2, 3, 2, 3}, { 2, 2, 2, 3, 2, 2, 2, 3},
|
|
{ 2, 2, 2, 2, 2, 2, 2, 2}, { 1, 2, 2, 2, 1, 2, 2, 2},
|
|
{ 1, 2, 1, 2, 1, 2, 1, 2}, { 1, 1, 1, 2, 1, 1, 1, 2},
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0},
|
|
{ 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0 ,0, 0, 0, 0, 0, 0},
|
|
};
|
|
|
|
#if 0 // libOPNMIDI: experimental SSG-EG
|
|
const int Operator::ssgenvtable[8][2][3][2] =
|
|
{
|
|
{{{1, 1}, {1, 1}, {1, 1}}, // 08
|
|
{{0, 1}, {1, 1}, {1, 1}}}, // 08 56~
|
|
{{{0, 1}, {2, 0}, {2, 0}}, // 09
|
|
{{0, 1}, {2, 0}, {2, 0}}}, // 09
|
|
{{{1,-1}, {0, 1}, {1,-1}}, // 10
|
|
{{0, 1}, {1,-1}, {0, 1}}}, // 10 60~
|
|
{{{1,-1}, {0, 0}, {0, 0}}, // 11
|
|
{{0, 1}, {0, 0}, {0, 0}}}, // 11 60~
|
|
{{{2,-1}, {2,-1}, {2,-1}}, // 12
|
|
{{1,-1}, {2,-1}, {2,-1}}}, // 12 56~
|
|
{{{1,-1}, {0, 0}, {0, 0}}, // 13
|
|
{{1,-1}, {0, 0}, {0, 0}}}, // 13
|
|
{{{0, 1}, {1,-1}, {0, 1}}, // 14
|
|
{{1,-1}, {0, 1}, {1,-1}}}, // 14 60~
|
|
{{{0, 1}, {2, 0}, {2, 0}}, // 15
|
|
{{1,-1}, {2, 0}, {2, 0}}}, // 15 60~
|
|
};
|
|
#endif
|
|
|
|
// fixed equasion-based tables
|
|
int pmtable[2][8][FM_LFOENTS];
|
|
uint amtable[2][4][FM_LFOENTS];
|
|
|
|
static bool tablemade = false;
|
|
}
|
|
|
|
namespace FM
|
|
{
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// テーブル作成
|
|
//
|
|
void MakeLFOTable()
|
|
{
|
|
if (tablemade)
|
|
return;
|
|
|
|
tablemade = true;
|
|
|
|
int i;
|
|
|
|
static const double pms[2][8] =
|
|
{
|
|
{ 0, 1/360., 2/360., 3/360., 4/360., 6/360., 12/360., 24/360., }, // OPNA
|
|
// { 0, 1/240., 2/240., 4/240., 10/240., 20/240., 80/240., 140/240., }, // OPM
|
|
{ 0, 1/480., 2/480., 4/480., 10/480., 20/480., 80/480., 140/480., }, // OPM
|
|
// { 0, 1/960., 2/960., 4/960., 10/960., 20/960., 80/960., 140/960., }, // OPM
|
|
};
|
|
// 3 6, 12 30 60 240 420 / 720
|
|
// 1.000963
|
|
// lfofref[level * max * wave];
|
|
// pre = lfofref[level][pms * wave >> 8];
|
|
static const uint8 amt[2][4] =
|
|
{
|
|
{ 31, 6, 4, 3 }, // OPNA
|
|
{ 31, 2, 1, 0 }, // OPM
|
|
};
|
|
|
|
for (int type = 0; type < 2; type++)
|
|
{
|
|
for (i=0; i<8; i++)
|
|
{
|
|
double pmb = pms[type][i];
|
|
for (int j=0; j<FM_LFOENTS; j++)
|
|
{
|
|
// double v = pow(2.0, pmb * (2 * j - FM_LFOENTS+1) / (FM_LFOENTS-1));
|
|
double w = 0.6 * pmb * sin(2 * j * 3.14159265358979323846 / FM_LFOENTS) + 1;
|
|
// pmtable[type][i][j] = int(0x10000 * (v - 1));
|
|
// if (type == 0)
|
|
pmtable[type][i][j] = int(0x10000 * (w - 1));
|
|
// else
|
|
// pmtable[type][i][j] = int(0x10000 * (v - 1));
|
|
|
|
// printf("pmtable[%d][%d][%.2x] = %5d %7.5f %7.5f\n", type, i, j, pmtable[type][i][j], v, w);
|
|
}
|
|
}
|
|
for (i=0; i<4; i++)
|
|
{
|
|
for (int j=0; j<FM_LFOENTS; j++)
|
|
{
|
|
amtable[type][i][j] = (((j * 4) >> amt[type][i]) * 2) << 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// チップ内で共通な部分
|
|
//
|
|
Chip::Chip()
|
|
: ratio_(0), aml_(0), pml_(0), pmv_(0)
|
|
//, optype_(typeN)
|
|
{
|
|
}
|
|
|
|
// クロック・サンプリングレート比に依存するテーブルを作成
|
|
void Chip::SetRatio(uint ratio)
|
|
{
|
|
if (ratio_ != ratio)
|
|
{
|
|
ratio_ = ratio;
|
|
MakeTable();
|
|
}
|
|
}
|
|
|
|
void Chip::MakeTable()
|
|
{
|
|
int h, l;
|
|
|
|
// PG Part
|
|
static const float dt2lv[4] = { 1.f, 1.414f, 1.581f, 1.732f };
|
|
for (h=0; h<4; h++)
|
|
{
|
|
assert(2 + FM_RATIOBITS - FM_PGBITS >= 0);
|
|
double rr = dt2lv[h] * double(ratio_) / (1 << (2 + FM_RATIOBITS - FM_PGBITS));
|
|
for (l=0; l<16; l++)
|
|
{
|
|
int mul = l ? l * 2 : 1;
|
|
multable_[h][l] = uint(mul * rr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Chip::DataSave(struct ChipData* data)
|
|
{
|
|
data->ratio_ = ratio_;
|
|
data->aml_ = aml_;
|
|
data->pml_ = pml_;
|
|
data->pmv_ = pmv_;
|
|
memcpy(data->multable_, multable_, sizeof(uint32) * 4 * 16);
|
|
}
|
|
|
|
void Chip::DataLoad(struct ChipData* data)
|
|
{
|
|
ratio_ = data->ratio_;
|
|
aml_ = data->aml_;
|
|
pml_ = data->pml_;
|
|
pmv_ = data->pmv_;
|
|
memcpy(multable_, data->multable_, sizeof(uint32) * 4 * 16);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Operator
|
|
//
|
|
bool FM::Operator::tablehasmade = false;
|
|
uint FM::Operator::sinetable[1024];
|
|
int32 FM::Operator::cltable[FM_CLENTS];
|
|
|
|
// 構築
|
|
FM::Operator::Operator()
|
|
: chip_(0)
|
|
{
|
|
if (!tablehasmade)
|
|
MakeTable();
|
|
|
|
// EG Part
|
|
ar_ = dr_ = sr_ = rr_ = key_scale_rate_ = 0;
|
|
ams_ = amtable[0][0];
|
|
mute_ = false;
|
|
keyon_ = false;
|
|
tl_out_ = false;
|
|
ssg_type_ = 0;
|
|
#if 1 // libOPNMIDI: experimental SSG-EG
|
|
inverted_ = false;
|
|
held_ = false;
|
|
#endif
|
|
|
|
// PG Part
|
|
multiple_ = 0;
|
|
detune_ = 0;
|
|
detune2_ = 0;
|
|
|
|
// LFO
|
|
ms_ = 0;
|
|
|
|
// Reset();
|
|
}
|
|
|
|
// 初期化
|
|
void FM::Operator::Reset()
|
|
{
|
|
// EG part
|
|
tl_ = tl_latch_ = 127;
|
|
ShiftPhase(off);
|
|
eg_count_ = 0;
|
|
eg_curve_count_ = 0;
|
|
#if 1 // libOPNMIDI: experimental SSG-EG
|
|
inverted_ = false;
|
|
held_ = false;
|
|
#else
|
|
ssg_phase_ = 0;
|
|
#endif
|
|
|
|
// PG part
|
|
pg_count_ = 0;
|
|
|
|
// OP part
|
|
out_ = out2_ = 0;
|
|
|
|
param_changed_ = true;
|
|
PARAMCHANGE(0);
|
|
}
|
|
|
|
void Operator::MakeTable()
|
|
{
|
|
// 対数テーブルの作成
|
|
assert(FM_CLENTS >= 256);
|
|
|
|
int* p = cltable;
|
|
int i;
|
|
for (i=0; i<256; i++)
|
|
{
|
|
int v = int(floor(pow(2., 13. - i / 256.)));
|
|
v = (v + 2) & ~3;
|
|
*p++ = v;
|
|
*p++ = -v;
|
|
}
|
|
while (p < cltable + FM_CLENTS)
|
|
{
|
|
//*p++ = p[-512] / 2;
|
|
*p = p[-512] / 2;
|
|
p++;
|
|
}
|
|
|
|
// for (i=0; i<13*256; i++)
|
|
// printf("%4d, %d, %d\n", i, cltable[i*2], cltable[i*2+1]);
|
|
|
|
// サインテーブルの作成
|
|
double log2 = log(2.);
|
|
for (i=0; i<FM_OPSINENTS/2; i++)
|
|
{
|
|
double r = (i * 2 + 1) * FM_PI / FM_OPSINENTS;
|
|
double q = -256 * log(sin(r)) / log2;
|
|
uint s = (int) (floor(q + 0.5)) + 1;
|
|
// printf("%d, %d\n", s, cltable[s * 2] / 8);
|
|
sinetable[i] = s * 2 ;
|
|
sinetable[FM_OPSINENTS / 2 + i] = s * 2 + 1;
|
|
}
|
|
|
|
::FM::MakeLFOTable();
|
|
|
|
tablehasmade = true;
|
|
}
|
|
|
|
|
|
|
|
inline void FM::Operator::SetDPBN(uint dp, uint bn)
|
|
{
|
|
dp_ = dp, bn_ = bn; param_changed_ = true;
|
|
PARAMCHANGE(1);
|
|
}
|
|
|
|
|
|
// 準備
|
|
void Operator::Prepare()
|
|
{
|
|
if (param_changed_)
|
|
{
|
|
param_changed_ = false;
|
|
// PG Part
|
|
pg_diff_ = (dp_ + dttable[detune_ + bn_]) * chip_->GetMulValue(detune2_, multiple_);
|
|
pg_diff_lfo_ = pg_diff_ >> 11;
|
|
|
|
// EG Part
|
|
key_scale_rate_ = bn_ >> (3-ks_);
|
|
tl_out_ = mute_ ? 0x3ff : tl_ * 8;
|
|
|
|
switch (eg_phase_)
|
|
{
|
|
case attack:
|
|
SetEGRate(static_cast<uint>(ar_ ? Min(63, ar_ + key_scale_rate_) : 0));
|
|
break;
|
|
case decay:
|
|
SetEGRate(static_cast<uint>(dr_ ? Min(63, dr_ + key_scale_rate_) : 0));
|
|
eg_level_on_next_phase_ = sl_ * 8;
|
|
break;
|
|
case sustain:
|
|
SetEGRate(static_cast<uint>(sr_ ? Min(63, sr_ + key_scale_rate_) : 0));
|
|
break;
|
|
case release:
|
|
SetEGRate(static_cast<uint>(Min(63, rr_ + key_scale_rate_)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// SSG-EG
|
|
inverted_ = false;
|
|
held_ = false;
|
|
if (ssg_type_ && (eg_phase_ != release))
|
|
{
|
|
#if 1 // libOPNMIDI: experimental SSG-EG
|
|
inverted_ = (ssg_type_ & 4) != 0;
|
|
inverted_ ^= (ssg_type_ & 2) && ar_ != 62; // try to match polarity with nuked OPN
|
|
#else
|
|
int m = static_cast<int>(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u));
|
|
|
|
assert(0 <= ssg_phase_ && ssg_phase_ <= 2);
|
|
const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_];
|
|
|
|
ssg_offset_ = table[0] * 0x200;
|
|
ssg_vector_ = table[1];
|
|
#endif
|
|
}
|
|
// LFO
|
|
ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0];
|
|
EGUpdate();
|
|
|
|
dbgopout_ = 0;
|
|
}
|
|
}
|
|
|
|
void Operator::DataSave(struct OperatorData* data)
|
|
{
|
|
data->out_ = out_;
|
|
data->out2_ = out2_;
|
|
data->in2_ = in2_;
|
|
data->dp_ = dp_;
|
|
data->detune_ = detune_;
|
|
data->detune2_ = detune2_;
|
|
data->multiple_ = multiple_;
|
|
data->pg_count_ = pg_count_;
|
|
data->pg_diff_ = pg_diff_;
|
|
data->pg_diff_lfo_ = pg_diff_lfo_;
|
|
data->type_ = type_;
|
|
data->bn_ = bn_;
|
|
data->eg_level_ = eg_level_;
|
|
data->eg_level_on_next_phase_ = eg_level_on_next_phase_;
|
|
data->eg_count_ = eg_count_;
|
|
data->eg_count_diff_ = eg_count_diff_;
|
|
data->eg_out_ = eg_out_;
|
|
data->tl_out_ = tl_out_;
|
|
data->eg_rate_ = eg_rate_;
|
|
data->eg_curve_count_ = eg_curve_count_;
|
|
#if 0 // libOPNMIDI: experimental SSG-EG
|
|
data->ssg_offset_ = ssg_offset_;
|
|
data->ssg_vector_ = ssg_vector_;
|
|
data->ssg_phase_ = ssg_phase_;
|
|
#endif
|
|
data->key_scale_rate_ = key_scale_rate_;
|
|
data->eg_phase_ = eg_phase_;
|
|
data->ms_ = ms_;
|
|
data->tl_ = tl_;
|
|
data->tl_latch_ = tl_latch_;
|
|
data->ar_ = ar_;
|
|
data->dr_ = dr_;
|
|
data->sr_ = sr_;
|
|
data->sl_ = sl_;
|
|
data->rr_ = rr_;
|
|
data->ks_ = ks_;
|
|
data->ssg_type_ = ssg_type_;
|
|
data->keyon_ = keyon_;
|
|
data->amon_ = amon_;
|
|
data->param_changed_ = param_changed_;
|
|
data->mute_ = mute_;
|
|
data->inverted_ = inverted_;
|
|
data->held_ = held_;
|
|
}
|
|
|
|
void Operator::DataLoad(struct OperatorData* data)
|
|
{
|
|
out_ = data->out_;
|
|
out2_ = data->out2_;
|
|
in2_ = data->in2_;
|
|
dp_ = data->dp_;
|
|
detune_ = data->detune_;
|
|
detune2_ = data->detune2_;
|
|
multiple_ = data->multiple_;
|
|
pg_count_ = data->pg_count_;
|
|
pg_diff_ = data->pg_diff_;
|
|
pg_diff_lfo_ = data->pg_diff_lfo_;
|
|
type_ = data->type_;
|
|
bn_ = data->bn_;
|
|
eg_level_ = data->eg_level_;
|
|
eg_level_on_next_phase_ = data->eg_level_on_next_phase_;
|
|
eg_count_ = data->eg_count_;
|
|
eg_count_diff_ = data->eg_count_diff_;
|
|
eg_out_ = data->eg_out_;
|
|
tl_out_ = data->tl_out_;
|
|
eg_rate_ = data->eg_rate_;
|
|
eg_curve_count_ = data->eg_curve_count_;
|
|
#if 0 // libOPNMIDI: experimental SSG-EG
|
|
ssg_offset_ = data->ssg_offset_;
|
|
ssg_vector_ = data->ssg_vector_;
|
|
ssg_phase_ = data->ssg_phase_;
|
|
#endif
|
|
key_scale_rate_ = data->key_scale_rate_;
|
|
eg_phase_ = data->eg_phase_;
|
|
ms_ = data->ms_;
|
|
tl_ = data->tl_;
|
|
tl_latch_ = data->tl_latch_;
|
|
ar_ = data->ar_;
|
|
dr_ = data->dr_;
|
|
sr_ = data->sr_;
|
|
sl_ = data->sl_;
|
|
rr_ = data->rr_;
|
|
ks_ = data->ks_;
|
|
ssg_type_ = data->ssg_type_;
|
|
keyon_ = data->keyon_;
|
|
amon_ = data->amon_;
|
|
param_changed_ = data->param_changed_;
|
|
mute_ = data->mute_;
|
|
inverted_ = data->inverted_;
|
|
held_ = data->held_;
|
|
ams_ = amtable[type_][amon_ ? (ms_ >> 4) & 3 : 0];
|
|
}
|
|
|
|
|
|
// envelop の eg_phase_ 変更
|
|
void Operator::ShiftPhase(EGPhase nextphase)
|
|
{
|
|
switch (nextphase)
|
|
{
|
|
case attack: // Attack Phase
|
|
tl_ = tl_latch_;
|
|
if (ssg_type_)
|
|
{
|
|
#if 0 // libOPNMIDI: experimental SSG-EG
|
|
ssg_phase_ = ssg_phase_ + 1;
|
|
if (ssg_phase_ > 2)
|
|
ssg_phase_ = 1;
|
|
|
|
int m = static_cast<int>(ar_ >= ((ssg_type_ == 8 || ssg_type_ == 12) ? 56u : 60u));
|
|
|
|
assert(0 <= ssg_phase_ && ssg_phase_ <= 2);
|
|
const int* table = ssgenvtable[ssg_type_ & 7][m][ssg_phase_];
|
|
|
|
ssg_offset_ = table[0] * 0x200;
|
|
ssg_vector_ = table[1];
|
|
#endif
|
|
}
|
|
if ((ar_ + key_scale_rate_) < 62)
|
|
{
|
|
SetEGRate(static_cast<uint>(ar_ ? Min(63, ar_ + key_scale_rate_) : 0));
|
|
eg_phase_ = attack;
|
|
break;
|
|
}
|
|
// fall through
|
|
case decay: // Decay Phase
|
|
if (sl_)
|
|
{
|
|
eg_level_ = 0;
|
|
eg_level_on_next_phase_ = ssg_type_ ? Min(sl_ * 8, 0x200) : sl_ * 8;
|
|
|
|
SetEGRate(static_cast<uint>(dr_ ? Min(63, dr_ + key_scale_rate_) : 0));
|
|
eg_phase_ = decay;
|
|
break;
|
|
}
|
|
// fall through
|
|
case sustain: // Sustain Phase
|
|
eg_level_ = sl_ * 8;
|
|
eg_level_on_next_phase_ = ssg_type_ ? 0x200 : 0x400;
|
|
|
|
SetEGRate(static_cast<uint>(sr_ ? Min(63, sr_ + key_scale_rate_) : 0));
|
|
eg_phase_ = sustain;
|
|
break;
|
|
|
|
case release: // Release Phase
|
|
inverted_ = false;
|
|
held_ = false;
|
|
#if 0 // libOPNMIDI: experimental SSG-EG
|
|
if (ssg_type_)
|
|
{
|
|
eg_level_ = eg_level_ * ssg_vector_ + ssg_offset_;
|
|
ssg_vector_ = 1;
|
|
ssg_offset_ = 0;
|
|
}
|
|
#endif
|
|
if (eg_phase_ == attack || (eg_level_ < FM_EG_BOTTOM)) //0x400/* && eg_phase_ != off*/))
|
|
{
|
|
eg_level_on_next_phase_ = 0x400;
|
|
SetEGRate(static_cast<uint>(Min(63, rr_ + key_scale_rate_)));
|
|
eg_phase_ = release;
|
|
break;
|
|
}
|
|
// fall through
|
|
case off: // off
|
|
default:
|
|
eg_level_ = FM_EG_BOTTOM;
|
|
eg_level_on_next_phase_ = FM_EG_BOTTOM;
|
|
EGUpdate();
|
|
SetEGRate(0);
|
|
eg_phase_ = off;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Block/F-Num
|
|
void Operator::SetFNum(uint f)
|
|
{
|
|
dp_ = (f & 2047) << ((f >> 11) & 7);
|
|
bn_ = notetable[(f >> 7) & 127];
|
|
param_changed_ = true;
|
|
PARAMCHANGE(2);
|
|
}
|
|
|
|
// 1サンプル合成
|
|
|
|
// ISample を envelop count (2π) に変換するシフト量
|
|
#define IS2EC_SHIFT ((20 + FM_PGBITS) - 13)
|
|
|
|
|
|
// 入力: s = 20+FM_PGBITS = 29
|
|
#define Sine(s) sinetable[((s) >> (20+FM_PGBITS-FM_OPSINBITS))&(FM_OPSINENTS-1)]
|
|
#define SINE(s) sinetable[(s) & (FM_OPSINENTS-1)]
|
|
|
|
inline FM::ISample Operator::LogToLin(uint a)
|
|
{
|
|
#if 1 // FM_CLENTS < 0xc00 // 400 for TL, 400 for ENV, 400 for LFO.
|
|
return (a < FM_CLENTS) ? cltable[a] : 0;
|
|
#else
|
|
return cltable[a];
|
|
#endif
|
|
}
|
|
|
|
inline void Operator::EGUpdate()
|
|
{
|
|
#if 1 // libOPNMIDI: experimental SSG-EG
|
|
int level = eg_level_;
|
|
level = (!inverted_) ? level : (512 - level) & 0x3ff;
|
|
eg_out_ = Min(tl_out_ + level, 0x3ff) << (1 + 2);
|
|
#else
|
|
if (!ssg_type_)
|
|
{
|
|
eg_out_ = Min(tl_out_ + eg_level_, 0x3ff) << (1 + 2);
|
|
}
|
|
else
|
|
{
|
|
eg_out_ = Min(tl_out_ + eg_level_ * ssg_vector_ + ssg_offset_, 0x3ff) << (1 + 2);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
inline void Operator::SetEGRate(uint rate)
|
|
{
|
|
eg_rate_ = rate;
|
|
eg_count_diff_ = decaytable2[rate / 4] * chip_->GetRatio();
|
|
}
|
|
|
|
// EG 計算
|
|
void FM::Operator::EGCalc()
|
|
{
|
|
eg_count_ = (2047 * 3) << FM_RATIOBITS; // ##この手抜きは再現性を低下させる
|
|
|
|
if (eg_phase_ == attack)
|
|
{
|
|
int c = attacktable[eg_rate_][eg_curve_count_ & 7];
|
|
if (c >= 0)
|
|
{
|
|
eg_level_ -= 1 + (eg_level_ >> c);
|
|
if (eg_level_ <= 0)
|
|
ShiftPhase(decay);
|
|
}
|
|
EGUpdate();
|
|
}
|
|
else
|
|
{
|
|
if (!ssg_type_)
|
|
{
|
|
eg_level_ += decaytable1[eg_rate_][eg_curve_count_ & 7];
|
|
if (eg_level_ >= eg_level_on_next_phase_)
|
|
ShiftPhase(EGPhase(eg_phase_+1));
|
|
EGUpdate();
|
|
}
|
|
else
|
|
{
|
|
if (!held_)
|
|
eg_level_ += 4 * decaytable1[eg_rate_][eg_curve_count_ & 7];
|
|
else
|
|
eg_level_ = (((ssg_type_ & 4) != 0) ^ ((ssg_type_ & 2) != 0)) ? 0 : 1024;;
|
|
EGUpdate(); // libOPNMIDI: experimental SSG-EG
|
|
if (eg_level_ >= eg_level_on_next_phase_)
|
|
{
|
|
switch (eg_phase_)
|
|
{
|
|
case decay:
|
|
ShiftPhase(sustain);
|
|
break;
|
|
case sustain:
|
|
#if 1 // libOPNMIDI: experimental SSG-EG
|
|
if (ssg_type_ & 1)
|
|
{
|
|
inverted_ = false;
|
|
held_ = true;
|
|
}
|
|
if (!held_)
|
|
{
|
|
inverted_ ^= (ssg_type_ & 2) && (ar_ == 62); // try to match polarity with nuked OPN
|
|
ShiftPhase(attack);
|
|
}
|
|
#else
|
|
ShiftPhase(attack);
|
|
#endif
|
|
break;
|
|
case release:
|
|
ShiftPhase(off);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
eg_curve_count_++;
|
|
}
|
|
|
|
inline void FM::Operator::EGStep()
|
|
{
|
|
eg_count_ -= eg_count_diff_;
|
|
|
|
// EG の変化は全スロットで同期しているという噂もある
|
|
if (eg_count_ <= 0)
|
|
EGCalc();
|
|
}
|
|
|
|
// PG 計算
|
|
// ret:2^(20+PGBITS) / cycle
|
|
inline uint32 FM::Operator::PGCalc()
|
|
{
|
|
uint32 ret = pg_count_;
|
|
pg_count_ += pg_diff_;
|
|
dbgpgout_ = ret;
|
|
return ret;
|
|
}
|
|
|
|
inline uint32 FM::Operator::PGCalcL()
|
|
{
|
|
uint32 ret = pg_count_;
|
|
pg_count_ += pg_diff_ + ((pg_diff_lfo_ * chip_->GetPMV()) >> 5);// & -(1 << (2+IS2EC_SHIFT)));
|
|
dbgpgout_ = ret;
|
|
return ret /* + pmv * pg_diff_;*/;
|
|
}
|
|
|
|
// OP 計算
|
|
// in: ISample (最大 8π)
|
|
inline FM::ISample FM::Operator::Calc(ISample in)
|
|
{
|
|
EGStep();
|
|
out2_ = out_;
|
|
|
|
int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS);
|
|
pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT));
|
|
out_ = LogToLin(eg_out_ + SINE(pgin));
|
|
|
|
dbgopout_ = out_;
|
|
return out_;
|
|
}
|
|
|
|
inline FM::ISample FM::Operator::CalcL(ISample in)
|
|
{
|
|
EGStep();
|
|
|
|
int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS);
|
|
pgin += in >> (20+FM_PGBITS-FM_OPSINBITS-(2+IS2EC_SHIFT));
|
|
out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]);
|
|
|
|
dbgopout_ = out_;
|
|
return out_;
|
|
}
|
|
|
|
inline FM::ISample FM::Operator::CalcN(uint noise)
|
|
{
|
|
EGStep();
|
|
|
|
int lv = Max(0, 0x3ff - (tl_out_ + eg_level_)) << 1;
|
|
|
|
// noise & 1 ? lv : -lv と等価
|
|
noise = (noise & 1) - 1;
|
|
out_ = (lv + noise) ^ noise;
|
|
|
|
dbgopout_ = out_;
|
|
return out_;
|
|
}
|
|
|
|
// OP (FB) 計算
|
|
// Self Feedback の変調最大 = 4π
|
|
inline FM::ISample FM::Operator::CalcFB(uint fb)
|
|
{
|
|
EGStep();
|
|
|
|
ISample in = out_ + out2_;
|
|
out2_ = out_;
|
|
|
|
int pgin = PGCalc() >> (20+FM_PGBITS-FM_OPSINBITS);
|
|
if (fb < 31)
|
|
{
|
|
pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS);
|
|
}
|
|
out_ = LogToLin(eg_out_ + SINE(pgin));
|
|
dbgopout_ = out2_;
|
|
|
|
return out2_;
|
|
}
|
|
|
|
inline FM::ISample FM::Operator::CalcFBL(uint fb)
|
|
{
|
|
EGStep();
|
|
|
|
ISample in = out_ + out2_;
|
|
out2_ = out_;
|
|
|
|
int pgin = PGCalcL() >> (20+FM_PGBITS-FM_OPSINBITS);
|
|
if (fb < 31)
|
|
{
|
|
pgin += ((in << (1 + IS2EC_SHIFT)) >> fb) >> (20+FM_PGBITS-FM_OPSINBITS);
|
|
}
|
|
|
|
out_ = LogToLin(eg_out_ + SINE(pgin) + ams_[chip_->GetAML()]);
|
|
dbgopout_ = out_;
|
|
|
|
return out_;
|
|
}
|
|
|
|
#undef Sine
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 4-op Channel
|
|
//
|
|
const uint8 Channel4::fbtable[8] = { 31, 7, 6, 5, 4, 3, 2, 1 };
|
|
int Channel4::kftable[64];
|
|
|
|
bool Channel4::tablehasmade = false;
|
|
|
|
|
|
Channel4::Channel4()
|
|
{
|
|
if (!tablehasmade)
|
|
MakeTable();
|
|
|
|
SetAlgorithm(0);
|
|
pms = pmtable[0][0];
|
|
}
|
|
|
|
void Channel4::MakeTable()
|
|
{
|
|
// 100/64 cent = 2^(i*100/64*1200)
|
|
for (int i=0; i<64; i++)
|
|
{
|
|
kftable[i] = int(0x10000 * pow(2., i / 768.) );
|
|
}
|
|
}
|
|
|
|
// リセット
|
|
void Channel4::Reset()
|
|
{
|
|
op[0].Reset();
|
|
op[1].Reset();
|
|
op[2].Reset();
|
|
op[3].Reset();
|
|
}
|
|
|
|
// Calc の用意
|
|
int Channel4::Prepare()
|
|
{
|
|
op[0].Prepare();
|
|
op[1].Prepare();
|
|
op[2].Prepare();
|
|
op[3].Prepare();
|
|
|
|
pms = pmtable[op[0].type_][op[0].ms_ & 7];
|
|
int key = (op[0].IsOn() | op[1].IsOn() | op[2].IsOn() | op[3].IsOn()) ? 1 : 0;
|
|
int lfo = op[0].ms_ & (op[0].amon_ | op[1].amon_ | op[2].amon_ | op[3].amon_ ? 0x37 : 7) ? 2 : 0;
|
|
return key | lfo;
|
|
}
|
|
|
|
// F-Number/BLOCK を設定
|
|
void Channel4::SetFNum(uint f)
|
|
{
|
|
for (int i=0; i<4; i++)
|
|
op[i].SetFNum(f);
|
|
}
|
|
|
|
// KC/KF を設定
|
|
void Channel4::SetKCKF(uint kc, uint kf)
|
|
{
|
|
const static uint kctable[16] =
|
|
{
|
|
5197, 5506, 5833, 6180, 6180, 6547, 6937, 7349,
|
|
7349, 7786, 8249, 8740, 8740, 9259, 9810, 10394,
|
|
};
|
|
|
|
int oct = 19 - ((kc >> 4) & 7);
|
|
|
|
//printf("%p", this);
|
|
uint kcv = kctable[kc & 0x0f];
|
|
kcv = (kcv + 2) / 4 * 4;
|
|
//printf(" %.4x", kcv);
|
|
uint dp = kcv * kftable[kf & 0x3f];
|
|
//printf(" %.4x %.4x %.8x", kcv, kftable[kf & 0x3f], dp >> oct);
|
|
dp >>= 16 + 3;
|
|
dp <<= 16 + 3;
|
|
dp >>= oct;
|
|
uint bn = (kc >> 2) & 31;
|
|
op[0].SetDPBN(dp, bn);
|
|
op[1].SetDPBN(dp, bn);
|
|
op[2].SetDPBN(dp, bn);
|
|
op[3].SetDPBN(dp, bn);
|
|
//printf(" %.8x\n", dp);
|
|
}
|
|
|
|
// キー制御
|
|
void Channel4::KeyControl(uint key)
|
|
{
|
|
if (key & 0x1) op[0].KeyOn(); else op[0].KeyOff();
|
|
if (key & 0x2) op[1].KeyOn(); else op[1].KeyOff();
|
|
if (key & 0x4) op[2].KeyOn(); else op[2].KeyOff();
|
|
if (key & 0x8) op[3].KeyOn(); else op[3].KeyOff();
|
|
}
|
|
|
|
// アルゴリズムを設定
|
|
void Channel4::SetAlgorithm(uint algo)
|
|
{
|
|
static const uint8 table1[8][6] =
|
|
{
|
|
{ 0, 1, 1, 2, 2, 3 }, { 1, 0, 0, 1, 1, 2 },
|
|
{ 1, 1, 1, 0, 0, 2 }, { 0, 1, 2, 1, 1, 2 },
|
|
{ 0, 1, 2, 2, 2, 1 }, { 0, 1, 0, 1, 0, 1 },
|
|
{ 0, 1, 2, 1, 2, 1 }, { 1, 0, 1, 0, 1, 0 },
|
|
};
|
|
|
|
in [0] = &buf[table1[algo][0]];
|
|
out[0] = &buf[table1[algo][1]];
|
|
in [1] = &buf[table1[algo][2]];
|
|
out[1] = &buf[table1[algo][3]];
|
|
in [2] = &buf[table1[algo][4]];
|
|
out[2] = &buf[table1[algo][5]];
|
|
|
|
op[0].ResetFB();
|
|
algo_ = algo;
|
|
}
|
|
|
|
// 合成
|
|
ISample Channel4::Calc()
|
|
{
|
|
int r;
|
|
switch (algo_)
|
|
{
|
|
case 0:
|
|
op[2].Calc(op[1].Out());
|
|
op[1].Calc(op[0].Out());
|
|
r = op[3].Calc(op[2].Out());
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 1:
|
|
op[2].Calc(op[0].Out() + op[1].Out());
|
|
op[1].Calc(0);
|
|
r = op[3].Calc(op[2].Out());
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 2:
|
|
op[2].Calc(op[1].Out());
|
|
op[1].Calc(0);
|
|
r = op[3].Calc(op[0].Out() + op[2].Out());
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 3:
|
|
op[2].Calc(0);
|
|
op[1].Calc(op[0].Out());
|
|
r = op[3].Calc(op[1].Out() + op[2].Out());
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 4:
|
|
op[2].Calc(0);
|
|
r = op[1].Calc(op[0].Out());
|
|
r += op[3].Calc(op[2].Out());
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 5:
|
|
r = op[2].Calc(op[0].Out());
|
|
r += op[1].Calc(op[0].Out());
|
|
r += op[3].Calc(op[0].Out());
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 6:
|
|
r = op[2].Calc(0);
|
|
r += op[1].Calc(op[0].Out());
|
|
r += op[3].Calc(0);
|
|
op[0].CalcFB(fb);
|
|
break;
|
|
case 7:
|
|
r = op[2].Calc(0);
|
|
r += op[1].Calc(0);
|
|
r += op[3].Calc(0);
|
|
r += op[0].CalcFB(fb);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
r = 0;
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// 合成
|
|
ISample Channel4::CalcL()
|
|
{
|
|
chip_->SetPMV(pms[chip_->GetPML()]);
|
|
|
|
int r;
|
|
switch (algo_)
|
|
{
|
|
case 0:
|
|
op[2].CalcL(op[1].Out());
|
|
op[1].CalcL(op[0].Out());
|
|
r = op[3].CalcL(op[2].Out());
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 1:
|
|
op[2].CalcL(op[0].Out() + op[1].Out());
|
|
op[1].CalcL(0);
|
|
r = op[3].CalcL(op[2].Out());
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 2:
|
|
op[2].CalcL(op[1].Out());
|
|
op[1].CalcL(0);
|
|
r = op[3].CalcL(op[0].Out() + op[2].Out());
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 3:
|
|
op[2].CalcL(0);
|
|
op[1].CalcL(op[0].Out());
|
|
r = op[3].CalcL(op[1].Out() + op[2].Out());
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 4:
|
|
op[2].CalcL(0);
|
|
r = op[1].CalcL(op[0].Out());
|
|
r += op[3].CalcL(op[2].Out());
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 5:
|
|
r = op[2].CalcL(op[0].Out());
|
|
r += op[1].CalcL(op[0].Out());
|
|
r += op[3].CalcL(op[0].Out());
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 6:
|
|
r = op[2].CalcL(0);
|
|
r += op[1].CalcL(op[0].Out());
|
|
r += op[3].CalcL(0);
|
|
op[0].CalcFBL(fb);
|
|
break;
|
|
case 7:
|
|
r = op[2].CalcL(0);
|
|
r += op[1].CalcL(0);
|
|
r += op[3].CalcL(0);
|
|
r += op[0].CalcFBL(fb);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
r = 0;
|
|
break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// 合成
|
|
ISample Channel4::CalcN(uint noise)
|
|
{
|
|
buf[1] = buf[2] = buf[3] = 0;
|
|
|
|
buf[0] = op[0].out_; op[0].CalcFB(fb);
|
|
*out[0] += op[1].Calc(*in[0]);
|
|
*out[1] += op[2].Calc(*in[1]);
|
|
int o = op[3].out_;
|
|
op[3].CalcN(noise);
|
|
return *out[2] + o;
|
|
}
|
|
|
|
// 合成
|
|
ISample Channel4::CalcLN(uint noise)
|
|
{
|
|
chip_->SetPMV(pms[chip_->GetPML()]);
|
|
buf[1] = buf[2] = buf[3] = 0;
|
|
|
|
buf[0] = op[0].out_; op[0].CalcFBL(fb);
|
|
*out[0] += op[1].CalcL(*in[0]);
|
|
*out[1] += op[2].CalcL(*in[1]);
|
|
int o = op[3].out_;
|
|
op[3].CalcN(noise);
|
|
return *out[2] + o;
|
|
}
|
|
|
|
void Channel4::DataSave(struct Channel4Data* data) {
|
|
data->fb = fb;
|
|
memcpy(data->buf, buf, sizeof(int) * 4);
|
|
data->algo_ = algo_;
|
|
for(int i = 0; i < 4; i++) {
|
|
op[i].DataSave(&data->op[i]);
|
|
}
|
|
}
|
|
|
|
void Channel4::DataLoad(struct Channel4Data* data) {
|
|
fb = data->fb;
|
|
memcpy(buf, data->buf, sizeof(int) * 4);
|
|
algo_ = data->algo_;
|
|
SetAlgorithm(algo_);
|
|
for(int i = 0; i < 4; i++) {
|
|
op[i].DataLoad(&data->op[i]);
|
|
}
|
|
pms = pmtable[op[0].type_][op[0].ms_ & 7];
|
|
}
|
|
} // namespace FM
|