fruitjam-doom/opl/emu8950.c
2022-03-07 12:44:30 -06:00

2028 lines
No EOL
65 KiB
C

/**
* emu8950 v1.1.0
* https://github.com/digital-sound-antiques/emu8950
* Copyright (C) 2001-2020 Mitsutaka Okazaki
* Copyright (C) 2021-2022 Graham Sanderson
*
* SPDX-License-Identifier: MIT
*/
#if USE_EMU8950_OPL
#include "emu8950.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define SAMPLE_BUF_SIZE 1024
#ifndef INLINE
#if defined(_MSC_VER)
#define INLINE __inline
#elif defined(__GNUC__)
#define INLINE __inline__
#else
#define INLINE inline
#endif
#endif
#define _PI_ 3.14159265358979323846264338327950288
/* dynamic range of envelope output */
#if !EMU8950_NO_FLOAT
#define EG_STEP 0.1875
#else
#define EG_STEPx16 3
#endif
/* dynamic range of total level */
#define TL_STEP 0.75
#define TL_BITS 6
/* dynamic range of sustine level */
#define SL_STEP 3.0
#define SL_BITS 4
/* damper speed before key-on. key-scale affects. */
#define DAMPER_RATE 12
#define TL2EG(tl) ((tl) << 2)
/* clang-format off */
/* exp_table[255-x] = round((exp2((double)x / 256.0) - 1) * 1024) */
static uint16_t exp_table[256] = {
1018, 1013, 1007, 1002, 996, 991, 986, 980, 975, 969, 964, 959, 953, 948, 942, 937,
932, 927, 921, 916, 911, 906, 900, 895, 890, 885, 880, 874, 869, 864, 859, 854,
849, 844, 839, 834, 829, 824, 819, 814, 809, 804, 799, 794, 789, 784, 779, 774,
770, 765, 760, 755, 750, 745, 741, 736, 731, 726, 722, 717, 712, 708, 703, 698,
693, 689, 684, 680, 675, 670, 666, 661, 657, 652, 648, 643, 639, 634, 630, 625,
621, 616, 612, 607, 603, 599, 594, 590, 585, 581, 577, 572, 568, 564, 560, 555,
551, 547, 542, 538, 534, 530, 526, 521, 517, 513, 509, 505, 501, 496, 492, 488,
484, 480, 476, 472, 468, 464, 460, 456, 452, 448, 444, 440, 436, 432, 428, 424,
420, 416, 412, 409, 405, 401, 397, 393, 389, 385, 382, 378, 374, 370, 367, 363,
359, 355, 352, 348, 344, 340, 337, 333, 329, 326, 322, 318, 315, 311, 308, 304,
300, 297, 293, 290, 286, 283, 279, 276, 272, 268, 265, 262, 258, 255, 251, 248,
244, 241, 237, 234, 231, 227, 224, 220, 217, 214, 210, 207, 204, 200, 197, 194,
190, 187, 184, 181, 177, 174, 171, 168, 164, 161, 158, 155, 152, 148, 145, 142,
139, 136, 133, 130, 126, 123, 120, 117, 114, 111, 108, 105, 102, 99, 96, 93,
90, 87, 84, 81, 78, 75, 72, 69, 66, 63, 60, 57, 54, 51, 48, 45,
42, 40, 37, 34, 31, 28, 25, 22, 20, 17, 14, 11, 8, 6, 3, 0,
};
/* logsin_table[x] = round(-log2(sin((x + 0.5) * PI / (PG_WIDTH / 4) / 2)) * 256) */
#if !EMU8950_NO_WAVE_TABLE_MAP
#define LOGSIN_TABLE_SIZE PG_WIDTH / 4
#else
#define LOGSIN_TABLE_SIZE PG_WIDTH / 2
#endif
static uint16_t logsin_table[LOGSIN_TABLE_SIZE] = {
2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869,
846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609,
598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461,
453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358,
352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280,
276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219,
215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169,
167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129,
127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96,
94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69,
67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47,
46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30,
29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17,
16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7,
7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
#if EMU8950_NO_WAVE_TABLE_MAP
// double the table size to include second 1/4 cycle
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2,
2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7,
7, 8, 8, 9, 9, 10, 10, 11, 12, 12, 13, 13, 14, 15, 15, 16,
17, 17, 18, 19, 20, 20, 21, 22, 23, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 45, 46,
47, 48, 49, 51, 52, 53, 55, 56, 57, 59, 60, 62, 63, 64, 66, 67,
69, 70, 72, 74, 75, 77, 78, 80, 82, 83, 85, 87, 89, 91, 92, 94,
96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 125, 127,
129, 131, 134, 136, 138, 141, 143, 146, 148, 151, 153, 156, 159, 161, 164, 167,
169, 172, 175, 178, 181, 184, 187, 190, 193, 196, 199, 202, 205, 209, 212, 215,
219, 222, 226, 229, 233, 236, 240, 244, 248, 251, 255, 259, 263, 267, 271, 276,
280, 284, 289, 293, 297, 302, 307, 311, 316, 321, 326, 331, 336, 341, 347, 352,
358, 363, 369, 375, 380, 386, 392, 399, 405, 411, 418, 425, 432, 439, 446, 453,
461, 468, 476, 484, 492, 501, 509, 518, 527, 536, 546, 556, 566, 576, 587, 598,
609, 621, 633, 646, 659, 672, 687, 701, 717, 732, 749, 767, 785, 804, 825, 846,
869, 894, 920, 949, 979, 1013, 1050, 1091, 1137, 1190, 1252, 1326, 1419, 1543, 1731, 2137,
#endif
};
/* clang-format on */
/* amplitude lfo table */
/* The following envelop pattern is verified on real YM2413. */
/* each element repeates 64 cycles */
static uint8_t am_table[210] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, //
2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, //
4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, //
6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, //
8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, //
10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, //
12, 12, 12, 12, 12, 12, 12, 12, //
13, 13, 13, //
12, 12, 12, 12, 12, 12, 12, 12, //
11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, //
9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, //
7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, //
5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, //
3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, //
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
#if !EMU8950_SLOT_RENDER
#if !EMU8950_NO_WAVE_TABLE_MAP
static uint16_t wave_table_map[4][PG_WIDTH];
#else
// we start with
// _ _
// / \/ \ which is abs(sine) wave
//
static uint16_t wav_or_table_lookup[4][4] = {
{0, 0, 0x8000, 0x8000}, // .. negate second half
{0, 0, 0x0fff, 0x0fff}, // .. attenuate second half
{0, 0, 0x0000, 0x0000}, // .. leave second half alone
{0, 0xfff, 0, 0xfff}, // .. attenuate 1 and 3
};
#endif
/* offset to fnum, rough approximation of 14 cents depth. */
static int8_t pm_table[8][PM_PG_WIDTH] = {
{0, 0, 0, 0, 0, 0, 0, 0}, // fnum = 000xxxxx
{0, 0, 1, 0, 0, 0, -1, 0}, // fnum = 001xxxxx
{0, 1, 2, 1, 0, -1, -2, -1}, // fnum = 010xxxxx
{0, 1, 3, 1, 0, -1, -3, -1}, // fnum = 011xxxxx
{0, 2, 4, 2, 0, -2, -4, -2}, // fnum = 100xxxxx
{0, 2, 5, 2, 0, -2, -5, -2}, // fnum = 101xxxxx
{0, 3, 6, 3, 0, -3, -6, -3}, // fnum = 110xxxxx
{0, 3, 7, 3, 0, -3, -7, -3}, // fnum = 111xxxxx
};
/* envelope decay increment step table */
static uint8_t eg_step_tables[4][8] = {
{0, 1, 0, 1, 0, 1, 0, 1},
{0, 1, 0, 1, 1, 1, 0, 1},
{0, 1, 1, 1, 0, 1, 1, 1},
{0, 1, 1, 1, 1, 1, 1, 1},
};
static uint8_t eg_step_tables_fast[4][8] = {
{1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 2, 1, 1, 1, 2},
{1, 2, 1, 2, 1, 2, 1, 2},
{1, 2, 2, 2, 1, 2, 2, 2},
};
static uint32_t ml_table[16] = {1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2,
8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2};
#endif
#if !EMU8950_NO_TLL
#if !EMU8950_NO_FLOAT
#define dB2(x) ((x)*2)
static double kl_table[16] = {dB2(0.000), dB2(9.000), dB2(12.000), dB2(13.875), dB2(15.000), dB2(16.125),
dB2(16.875), dB2(17.625), dB2(18.000), dB2(18.750), dB2(19.125), dB2(19.500),
dB2(19.875), dB2(20.250), dB2(20.625), dB2(21.000)};
#else
#define dB2x16(x) ((uint16_t)((x)*32))
static int16_t kl_tablex16[16] = {dB2x16(0.000), dB2x16(9.000), dB2x16(12.000), dB2x16(13.875), dB2x16(15.000),
dB2x16(16.125),
dB2x16(16.875), dB2x16(17.625), dB2x16(18.000), dB2x16(18.750), dB2x16(19.125),
dB2x16(19.500),
dB2x16(19.875), dB2x16(20.250), dB2x16(20.625), dB2x16(21.000)};
#endif
#endif
#if !EMU8950_NO_TLL
static uint32_t tll_table[8 * 16][1 << TL_BITS][4];
#endif
static int32_t rks_table[2][32][2];
#define min(i, j) (((i) < (j)) ? (i) : (j))
#define max(i, j) (((i) > (j)) ? (i) : (j))
/***************************************************
Internal Sample Rate Converter
****************************************************/
/* Note: to disable internal rate converter, set clock/72 to output sampling rate. */
/*
* LW is truncate length of sinc(x) calculation.
* Lower LW is faster, higher LW results better quality.
* LW must be a non-zero positive even number, no upper limit.
* LW=16 or greater is recommended when upsampling.
* LW=8 is practically okay for downsampling.
*/
#define LW 16
#if !EMU8950_NO_RATECONV
/* resolution of sinc(x) table. sinc(x) where 0.0<=x<1.0 corresponds to sinc_table[0...SINC_RESO-1] */
#define SINC_RESO 256
#define SINC_AMP_BITS 12
// double hamming(double x) { return 0.54 - 0.46 * cos(2 * PI * x); }
static double blackman(double x) { return 0.42 - 0.5 * cos(2 * _PI_ * x) + 0.08 * cos(4 * _PI_ * x); }
static double sinc(double x) { return (x == 0.0 ? 1.0 : sin(_PI_ * x) / (_PI_ * x)); }
static double windowed_sinc(double x) { return blackman(0.5 + 0.5 * x / (LW / 2)) * sinc(x); }
/* f_inp: input frequency. f_out: output frequencey, ch: number of channels */
OPL_RateConv *OPL_RateConv_new(double f_inp, double f_out, int ch) {
OPL_RateConv *conv = malloc(sizeof(OPL_RateConv));
int i;
conv->ch = ch;
conv->f_ratio = f_inp / f_out;
conv->buf = malloc(sizeof(void *) * ch);
for (i = 0; i < ch; i++) {
conv->buf[i] = malloc(sizeof(conv->buf[0][0]) * LW);
}
/* create sinc_table for positive 0 <= x < LW/2 */
conv->sinc_table = malloc(sizeof(conv->sinc_table[0]) * SINC_RESO * LW / 2);
for (i = 0; i < SINC_RESO * LW / 2; i++) {
const double x = (double) i / SINC_RESO;
if (f_out < f_inp) {
/* for downsampling */
conv->sinc_table[i] = (int16_t) ((1 << SINC_AMP_BITS) * windowed_sinc(x / conv->f_ratio) / conv->f_ratio);
} else {
/* for upsampling */
conv->sinc_table[i] = (int16_t) ((1 << SINC_AMP_BITS) * windowed_sinc(x));
}
}
return conv;
}
static INLINE int16_t lookup_sinc_table(int16_t *table, double x) {
int16_t index = (int16_t) (x * SINC_RESO);
if (index < 0)
index = -index;
return table[min(SINC_RESO * LW / 2 - 1, index)];
}
void OPL_RateConv_reset(OPL_RateConv *conv) {
int i;
conv->timer = 0;
for (i = 0; i < conv->ch; i++) {
memset(conv->buf[i], 0, sizeof(conv->buf[i][0]) * LW);
}
}
/* put original data to this converter at f_inp. */
void OPL_RateConv_putData(OPL_RateConv *conv, int ch, int16_t data) {
int16_t *buf = conv->buf[ch];
int i;
for (i = 0; i < LW - 1; i++) {
buf[i] = buf[i + 1];
}
buf[LW - 1] = data;
}
/* get resampled data from this converter at f_out. */
/* this function must be called f_out / f_inp times per one putData call. */
int16_t OPL_RateConv_getData(OPL_RateConv *conv, int ch) {
int16_t *buf = conv->buf[ch];
int32_t sum = 0;
int k;
double dn;
conv->timer += conv->f_ratio;
dn = conv->timer - floor(conv->timer);
conv->timer = dn;
for (k = 0; k < LW; k++) {
double x = ((double) k - (LW / 2 - 1)) - dn;
sum += buf[k] * lookup_sinc_table(conv->sinc_table, x);
}
return sum >> SINC_AMP_BITS;
}
void OPL_RateConv_delete(OPL_RateConv *conv) {
int i;
for (i = 0; i < conv->ch; i++) {
free(conv->buf[i]);
}
free(conv->buf);
free(conv->sinc_table);
free(conv);
}
#endif
/***************************************************
Create tables
****************************************************/
static void makeSinTable(void) {
#if !EMU8950_NO_WAVE_TABLE_MAP
int x;
for (x = 0; x < PG_WIDTH; x++) {
if (x < PG_WIDTH / 4) {
wave_table_map[0][x] = logsin_table[x];
} else if (x < PG_WIDTH / 2) {
wave_table_map[0][x] = logsin_table[PG_WIDTH / 2 - x - 1];
} else {
wave_table_map[0][x] = 0x8000 | wave_table_map[0][PG_WIDTH - x - 1];
}
}
for (x = 0; x < PG_WIDTH; x++) {
if (x < PG_WIDTH / 2) {
wave_table_map[1][x] = wave_table_map[0][x];
} else {
wave_table_map[1][x] = 0xfff;
}
}
for (x = 0; x < PG_WIDTH; x++) {
if (x < PG_WIDTH / 2) {
wave_table_map[2][x] = wave_table_map[0][x];
} else {
wave_table_map[2][x] = wave_table_map[0][x - PG_WIDTH / 2];
}
}
for (x = 0; x < PG_WIDTH; x++) {
if (x < PG_WIDTH / 4) {
wave_table_map[3][x] = wave_table_map[0][x];
} else if (x < PG_WIDTH / 2) {
wave_table_map[3][x] = 0xfff;
} else if (x < PG_WIDTH * 3 / 4) {
wave_table_map[3][x] = wave_table_map[0][x - PG_WIDTH / 2];
} else {
wave_table_map[3][x] = 0xfff;
}
}
#endif
}
static void makeTllTable(void) {
#if !EMU8950_NO_TLL
int32_t tmp;
int32_t fnum, block, TL, KL, kx;
for (fnum = 0; fnum < 16; fnum++) {
for (block = 0; block < 8; block++) {
for (TL = 0; TL < 64; TL++) {
for (KL = 0; KL < 4; KL++) {
kx = ((KL & 1) << 1) | ((KL >> 1) & 1);
if (KL == 0) {
tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL);
} else {
#if !EMU8950_NO_FLOAT
tmp = (int32_t)(kl_table[fnum] - dB2(3.000) * (7 - block));
if (tmp <= 0)
tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL);
else
tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - kx)) / EG_STEP) + TL2EG(TL);
#else
tmp = (int32_t)(kl_tablex16[fnum] - dB2x16(3.000) * (7 - block));
if (tmp <= 0)
tll_table[(block << 4) | fnum][TL][KL] = TL2EG(TL);
else
tll_table[(block << 4) | fnum][TL][KL] = (uint32_t)((tmp >> (3 - kx)) / EG_STEPx16) + TL2EG(TL);
#endif
}
}
}
}
}
#endif
}
static void makeRksTable(void) {
int fnum8, fnum9, blk;
int blk_fnum98;
for (fnum8 = 0; fnum8 < 2; fnum8++)
for (fnum9 = 0; fnum9 < 2; fnum9++)
for (blk = 0; blk < 8; blk++) {
blk_fnum98 = (blk << 2) | (fnum9 << 1) | fnum8;
rks_table[0][blk_fnum98][1] = (blk << 1) + fnum9;
rks_table[0][blk_fnum98][0] = blk >> 1;
rks_table[1][blk_fnum98][1] = (blk << 1) + (fnum9 & fnum8);
rks_table[1][blk_fnum98][0] = blk >> 1;
}
}
static uint8_t table_initialized = 0;
static void initializeTables() {
makeTllTable();
makeRksTable();
makeSinTable();
table_initialized = 1;
}
/*********************************************************
Synthesizing
*********************************************************/
#define SLOT_BD1 12
#define SLOT_BD2 13
#define SLOT_HH 14
#define SLOT_SD 15
#define SLOT_TOM 16
#define SLOT_CYM 17
/* utility macros */
#define MOD(o, x) (&(o)->slot[(x) << 1])
#define CAR(o, x) (&(o)->slot[((x) << 1) | 1])
#define BIT(s, b) (((s) >> (b)) & 1)
#if OPL_DEBUG
static void _debug_print_patch(OPL_SLOT *slot) {
OPL_PATCH *p = slot->patch;
printf("[slot#%d am:%d pm:%d eg:%d kr:%d ml:%d kl:%d tl:%d ws:%d fb:%d A:%d D:%d S:%d R:%d]\n", slot->number, //
p->AM, p->PM, p->EG, p->KR, p->ML, //
p->KL, p->TL, p->WS, p->FB, //
p->AR, p->DR, p->SL, p->RR);
}
static char *_debug_eg_state_name(OPL_SLOT *slot) {
switch (slot->eg_state) {
case ATTACK:
return "attack";
case DECAY:
return "decay";
case SUSTAIN:
return "sustain";
case RELEASE:
return "release";
case DAMP:
return "damp";
default:
return "unknown";
}
}
static INLINE void _debug_print_slot_info(OPL_SLOT *slot) {
char *name = _debug_eg_state_name(slot);
_debug_print_patch(slot);
printf("[slot#%d state:%s fnum:%03x rate:%d-%d]\n", slot->number, name, slot->blk_fnum, slot->eg_rate_h,
slot->eg_rate_l);
fflush(stdout);
}
#endif
enum SLOT_UPDATE_FLAG
{
UPDATE_WS = 1,
UPDATE_TLL = 2,
UPDATE_RKS = 4,
UPDATE_EG = 8,
UPDATE_ALL = 255,
};
static INLINE void request_update(OPL_SLOT *slot, int flag) {
slot->update_requests |= flag;
}
static INLINE int get_parameter_rate(OPL_SLOT *slot) {
switch (slot->eg_state) {
case ATTACK:
return slot->patch->AR;
case DECAY:
return slot->patch->DR;
case SUSTAIN:
return slot->patch->EG ? 0 : slot->patch->RR;
case RELEASE:
return slot->patch->RR;
default:
return 0;
}
}
static void commit_slot_update(OPL_SLOT *slot, uint8_t notesel) {
if (slot->update_requests & UPDATE_WS) {
#if !EMU8950_NO_WAVE_TABLE_MAP
slot->wave_table = wave_table_map[slot->patch->WS & 3];
#else
#if !EMU8950_SLOT_RENDER
slot->wav_or_table = wav_or_table_lookup[slot->patch->WS & 3];
#endif
#endif
}
if (slot->update_requests & UPDATE_TLL) {
#if !EMU8950_NO_TLL
if ((slot->type & 1) == 0) {
slot->tll = tll_table[slot->blk_fnum >> 6][slot->patch->TL][slot->patch->KL];
} else {
slot->tll = tll_table[slot->blk_fnum >> 6][slot->patch->TL][slot->patch->KL];
}
#else
static const uint8_t kslrom4[16] = {
0 * 4, 32 * 4, 40 * 4, 45 * 4, 48 * 4, 51 * 4, 53 * 4, 55 * 4, 56 * 4, 58 * 4, 59 * 4, 60 * 4, 61 * 4,
62 * 4, 63 * 4, 255
};
int fnum = (slot->blk_fnum >> 6) & 15;
int block = (slot->blk_fnum >> 10);
int16_t ksl = kslrom4[fnum] - ((0x08 - block) << 5);
if (ksl < 0) {
slot->tll = slot->patch->TL4;
} else {
slot->tll = slot->patch->TL4 + (ksl >> slot->patch->KL_SHIFT);
}
#endif
}
if (slot->update_requests & UPDATE_RKS) {
slot->rks = rks_table[notesel][slot->blk_fnum >> 8][slot->patch->KR];
}
if (slot->update_requests & (UPDATE_RKS | UPDATE_EG)) {
int p_rate = get_parameter_rate(slot);
if (p_rate == 0) {
slot->eg_shift = 0;
slot->eg_rate_h = 0;
slot->eg_rate_l = 0;
} else {
slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2));
slot->eg_rate_l = slot->rks & 3;
if (slot->eg_state == ATTACK) {
slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
} else {
slot->eg_shift = (slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
}
}
}
#if OPL_DEBUG
if (slot->last_eg_state != slot->eg_state) {
_debug_print_slot_info(slot);
slot->last_eg_state = slot->eg_state;
}
#endif
slot->update_requests = 0;
}
#if !EMU8950_SLOT_RENDER
static void commit_slot_update_eg_only(OPL_SLOT *slot, uint8_t notesel) {
assert(slot->update_requests == UPDATE_EG);
int p_rate = get_parameter_rate(slot);
if (p_rate == 0) {
slot->eg_shift = 0;
slot->eg_rate_h = 0;
slot->eg_rate_l = 0;
} else {
slot->eg_rate_h = min(15, p_rate + (slot->rks >> 2));
slot->eg_rate_l = slot->rks & 3;
if (slot->eg_state == ATTACK) {
slot->eg_shift = (0 < slot->eg_rate_h && slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
} else {
slot->eg_shift = (slot->eg_rate_h < 12) ? (12 - slot->eg_rate_h) : 0;
}
}
slot->update_requests = 0;
}
#endif
static void reset_slot(OPL_SLOT *slot, int number) {
slot->patch = &(slot->__patch);
memset(slot->patch, 0, sizeof(OPL_PATCH));
slot->number = number;
#if !EMU8950_NO_PERCUSSION_MODE
slot->type = number % 2;
#endif
// slot->pg_keep = 0;
#if !EMU8950_NO_WAVE_TABLE_MAP
slot->wave_table = wave_table_map[0];
#else
#if !EMU8950_SLOT_RENDER
slot->wav_or_table = wav_or_table_lookup[0];
#endif
#endif
// slot->pg_phase = 0;
// slot->output[0] = 0;
// slot->output[1] = 0;
slot->eg_state = RELEASE;
// slot->eg_shift = 0;
// slot->rks = 0;
// slot->tll = 0;
// slot->blk_fnum = 0;
// slot->blk = 0;
// slot->fnum = 0;
// slot->pg_out = 0;
slot->eg_out = EG_MUTE;
}
#if !EMU8950_NO_PERCUSSION_MODE
#define slot_pg_keep(slot) slot->pg_keep
#define opl_perc_mode(opl) opl->perc_mode
#else
#define slot_pg_keep(slot) 0
#define opl_perc_mode(opl) 0
#endif
static INLINE void slotOn(OPL *opl, int i) {
OPL_SLOT *slot = &opl->slot[i];
if (min(15, slot->patch->AR + (slot->rks >> 2)) == 15) {
slot->eg_state = DECAY;
slot->eg_out = 0;
} else {
slot->eg_state = ATTACK;
}
if (!slot_pg_keep(slot)) {
slot->pg_phase = 0;
}
request_update(slot, UPDATE_EG);
}
static INLINE void slotOff(OPL *opl, int i) {
OPL_SLOT *slot = &opl->slot[i];
slot->eg_state = RELEASE;
request_update(slot, UPDATE_EG);
}
static INLINE void update_key_status(OPL *opl) {
const uint8_t r14 = opl->reg[0xbd];
const uint8_t perc_mode = BIT(r14, 5);
uint32_t new_slot_key_status = 0;
uint32_t updated_status;
int ch;
#if !EMU8950_NO_TIMER
if (opl->csm_mode && opl->csm_key_count) {
new_slot_key_status = 0x3ffff;
}
#endif
for (ch = 0; ch < 9; ch++)
if (opl->reg[0xB0 + ch] & 0x20)
new_slot_key_status |= 3 << (ch * 2);
if (perc_mode) {
if (r14 & 0x10)
new_slot_key_status |= 3 << SLOT_BD1;
if (r14 & 0x01)
new_slot_key_status |= 1 << SLOT_HH;
if (r14 & 0x08)
new_slot_key_status |= 1 << SLOT_SD;
if (r14 & 0x04)
new_slot_key_status |= 1 << SLOT_TOM;
if (r14 & 0x02)
new_slot_key_status |= 1 << SLOT_CYM;
}
updated_status = opl->slot_key_status ^ new_slot_key_status;
if (updated_status) {
int i;
for (i = 0; i < 18; i++)
if (BIT(updated_status, i)) {
if (BIT(new_slot_key_status, i)) {
slotOn(opl, i);
} else {
slotOff(opl, i);
}
}
}
opl->slot_key_status = new_slot_key_status;
}
/* set f-Nnmber ( fnum : 10bit ) */
static INLINE void set_fnumber(OPL *opl, int ch, int fnum) {
OPL_SLOT *car = CAR(opl, ch);
OPL_SLOT *mod = MOD(opl, ch);
car->fnum = fnum;
car->blk_fnum = (car->blk_fnum & 0x1c00) | (fnum & 0x3ff);
mod->fnum = fnum;
mod->blk_fnum = (mod->blk_fnum & 0x1c00) | (fnum & 0x3ff);
request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL);
request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL);
}
/* set block data (blk : 3bit ) */
static INLINE void set_block(OPL *opl, int ch, int blk) {
OPL_SLOT *car = CAR(opl, ch);
OPL_SLOT *mod = MOD(opl, ch);
car->blk = blk;
car->blk_fnum = ((blk & 7) << 10) | (car->blk_fnum & 0x3ff);
mod->blk = blk;
mod->blk_fnum = ((blk & 7) << 10) | (mod->blk_fnum & 0x3ff);
request_update(car, UPDATE_EG | UPDATE_RKS | UPDATE_TLL);
request_update(mod, UPDATE_EG | UPDATE_RKS | UPDATE_TLL);
}
static INLINE void update_perc_mode(OPL *opl) {
#if !EMU8950_NO_PERCUSSION_MODE
const uint8_t new_perc_mode = (opl->reg[0xbd] >> 5) & 1;
if (opl->perc_mode != new_perc_mode) {
if (new_perc_mode) {
opl->slot[SLOT_HH].type = 3;
opl->slot[SLOT_HH].pg_keep = 1;
opl->slot[SLOT_SD].type = 3;
opl->slot[SLOT_TOM].type = 3;
opl->slot[SLOT_CYM].type = 3;
opl->slot[SLOT_CYM].pg_keep = 1;
} else {
opl->slot[SLOT_HH].type = 0;
opl->slot[SLOT_HH].pg_keep = 0;
opl->slot[SLOT_SD].type = 1;
opl->slot[SLOT_TOM].type = 0;
opl->slot[SLOT_CYM].type = 1;
opl->slot[SLOT_CYM].pg_keep = 0;
}
}
opl->perc_mode = new_perc_mode;
#else
assert(!((opl->reg[0xbd] >> 5) & 1)); // new_perc_mode should be 0
#endif
}
#if !EMU8950_LINEAR
static INLINE void update_ampm(OPL *opl) {
#if !EMU8950_NO_TEST_FLAG
const uint32_t pm_inc = (opl_test_flag(opl) & 8) ? opl->pm_dphase << 10 : opl->pm_dphase;
const uint32_t am_inc = opl_test_flag(opl) ? 64 : 1;
if (opl_test_flag(opl) & 2) {
opl->pm_phase = 0;
opl->am_phase = 0;
} else {
opl->pm_phase = (opl->pm_phase + pm_inc) & (PM_DP_WIDTH - 1);
opl->am_phase += am_inc;
}
opl->lfo_am = am_table[(opl->am_phase >> 6) % sizeof(am_table)] >> (opl->am_mode ? 0 : 2);
#else
opl->pm_phase = (opl->pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
opl->am_phase_index++;
if (opl->am_phase_index == sizeof(am_table)) opl->am_phase_index = 0;
opl->lfo_am = am_table[opl->am_phase_index] >> (opl->am_mode ? 0 : 2);
#endif
}
static void update_noise(OPL *opl, int cycle) {
#if !EMU8950_SIMPLER_NOISE
int i;
for (i = 0; i < cycle; i++) {
if (opl->noise & 1) {
opl->noise ^= 0x800200;
}
opl->noise >>= 1;
}
#endif
}
static int noise_bit(OPL *opl) {
#if !EMU8950_SIMPLER_NOISE
return opl->noise & 1;
#else
if (opl->noise & 1) {
opl->noise ^= 0x800200;
opl->noise >>= 1;
return 1;
}
opl->noise >>= 1;
return 0;
#endif
}
static void update_short_noise(OPL *opl) {
const uint32_t pg_hh = opl->slot[SLOT_HH].pg_out;
const uint32_t pg_cym = opl->slot[SLOT_CYM].pg_out;
const uint8_t h_bit2 = BIT(pg_hh, PG_BITS - 8);
const uint8_t h_bit7 = BIT(pg_hh, PG_BITS - 3);
const uint8_t h_bit3 = BIT(pg_hh, PG_BITS - 7);
const uint8_t c_bit3 = BIT(pg_cym, PG_BITS - 7);
const uint8_t c_bit5 = BIT(pg_cym, PG_BITS - 5);
opl->short_noise = (h_bit2 ^ h_bit7) | (h_bit3 ^ c_bit5) | (c_bit3 ^ c_bit5);
}
#endif
#if !EMU8950_SLOT_RENDER
static INLINE void calc_phase(OPL_SLOT *slot, int32_t pm_phase, uint8_t pm_mode, uint8_t reset) {
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (pm_mode ? 0 : 1);
}
if (reset) {
slot->pg_phase = 0;
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
slot->pg_out = slot->pg_phase >> DP_BASE_BITS;
}
static INLINE uint8_t lookup_attack_step(OPL_SLOT *slot, uint32_t counter) {
int index = (counter >> slot->eg_shift) & 7;
switch (slot->eg_rate_h) {
case 13:
return eg_step_tables_fast[slot->eg_rate_l][index];
case 14:
return eg_step_tables_fast[slot->eg_rate_l][index] << 1;
case 0:
case 15:
return 0;
default:
return eg_step_tables[slot->eg_rate_l][index];
}
}
static INLINE uint8_t lookup_decay_step(OPL_SLOT *slot, uint32_t counter) {
int index = (counter >> slot->eg_shift) & 7;
switch (slot->eg_rate_h) {
case 0:
return 0;
case 13:
return eg_step_tables_fast[slot->eg_rate_l][index];
case 14:
return eg_step_tables_fast[slot->eg_rate_l][index] << 1;
case 15:
return 4;
default:
return eg_step_tables[slot->eg_rate_l][index];
}
}
static INLINE void calc_envelope(OPL_SLOT *slot, uint16_t eg_counter, uint8_t test) {
uint16_t mask = (1 << slot->eg_shift) - 1;
uint8_t step;
if (slot->eg_state == ATTACK) {
if (0 < slot->eg_out && slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
step = lookup_attack_step(slot, eg_counter);
slot->eg_out += (~slot->eg_out * step) >> 3;
}
} else {
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
}
}
switch (slot->eg_state) {
case ATTACK:
if (slot->eg_out == 0) {
slot->eg_state = DECAY;
request_update(slot, UPDATE_EG);
}
break;
case DECAY:
if ((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL) {
slot->eg_state = SUSTAIN;
request_update(slot, UPDATE_EG);
}
break;
case SUSTAIN:
case RELEASE:
default:
break;
}
if (test) {
slot->eg_out = 0;
}
}
#endif
#if !EMU8950_LINEAR
static void update_slots(OPL *opl) {
int i;
opl->eg_counter++;
for (i = 0; i < 18; i++) {
OPL_SLOT *slot = &opl->slot[i];
if (slot->update_requests) {
commit_slot_update(slot, opl->notesel);
}
calc_envelope(slot, opl->eg_counter, opl_test_flag(opl) & 1);
calc_phase(slot, opl->pm_phase, opl->pm_mode, opl_test_flag(opl) & 4);
}
}
#endif
/* input: 0..8191 output: -4095..4095 */
static int16_t lookup_exp_table(int16_t i) {
/* from andete's expressoin */
int16_t t = (exp_table[(i & 0xffu)] + 1024);
int16_t res = t >> ((i & 0x7f00) >> 8);
#if EMU8950_LINEAR_NEG_NOT_NOT
return ((i & 0x8000) ? -res : res) << 1;
#else
return ((i & 0x8000) ? ~res : res) << 1;
#endif
}
static INLINE int16_t to_linear(uint16_t h, OPL_SLOT *slot, int16_t am) {
uint16_t att;
if (slot->eg_out >= EG_MAX) {
return 0;
}
att = min(EG_MUTE, (slot->eg_out + slot->tll + am)) << 3;
return lookup_exp_table(h + att);
}
#define LOGSIN_MASK (PG_WIDTH/4 - 1)
#define LOGSIN_MASK2 (PG_WIDTH/2 - 1)
//static INLINE uint16_t get_wave_table(OPL_SLOT *slot, uint32_t index) {
static uint16_t get_wave_table(OPL_SLOT *slot, uint32_t index) {
#if !EMU8950_NO_WAVE_TABLE_MAP
return slot->wave_table[index];
#else
#if !EMU8950_SLOT_RENDER
return slot->wav_or_table[(index >> (PG_BITS - 2))&3] | logsin_table[(index & LOGSIN_MASK2)];
#else
assert(0);
return 0;
#endif
#if 0
switch (((index >> (PG_BITS - 4))&0xc) | (slot->patch->WS & 3)) {
case 0b0000:
case 0b0001:
case 0b0010:
case 0b0011:
case 0b1010:
case 0b1011:
return logsin_table[index & LOGSIN_MASK];
case 0b0100:
case 0b0101:
case 0b0110:
case 0b1110:
return logsin_table[LOGSIN_MASK - (index & LOGSIN_MASK)];
case 0b1000:
return 0x8000 | logsin_table[index & LOGSIN_MASK];
case 0b1100:
return 0x8000 | logsin_table[LOGSIN_MASK - (index & LOGSIN_MASK)];
default:
return 0xfff;
}
#endif
#endif
}
static INLINE uint16_t get_wave_table_wrap(OPL_SLOT *slot, uint32_t index) {
#if !EMU8950_NO_WAVE_TABLE_MAP
return get_wave_table(slot, index & (PG_WIDTH - 1));
#else
return get_wave_table(slot, index);
#endif
}
static INLINE int16_t calc_slot_car(OPL *opl, int ch, int16_t fm) {
OPL_SLOT *slot = CAR(opl, ch);
uint8_t am = slot->patch->AM ? opl->lfo_am : 0;
slot->output[1] = slot->output[0];
slot->output[0] = to_linear(get_wave_table_wrap(slot, slot->pg_out + 2 * (fm >> 1)), slot, am);
return slot->output[0];
}
static INLINE int16_t calc_slot_mod(OPL *opl, int ch) {
OPL_SLOT *slot = MOD(opl, ch);
int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0;
uint8_t am = slot->patch->AM ? opl->lfo_am : 0;
slot->output[1] = slot->output[0];
slot->output[0] = to_linear(get_wave_table_wrap(slot, slot->pg_out + fm), slot, am);
return slot->output[0];
}
/* Specify phase offset directly based on 10-bit (1024-length) sine table */
#define _PD(phase) ((PG_BITS < 10) ? (phase >> (10 - PG_BITS)) : (phase << (PG_BITS - 10)))
#if !EMU8950_NO_PERCUSSION_MODE
static INLINE int16_t calc_slot_tom(OPL *opl) {
OPL_SLOT *slot = &(opl->slot[SLOT_TOM]);
return to_linear(get_wave_table(slot, slot->pg_out), slot, 0);
}
static INLINE int16_t calc_slot_snare(OPL *opl) {
OPL_SLOT *slot = &(opl->slot[SLOT_SD]);
uint32_t phase;
if (BIT(opl->slot[SLOT_HH].pg_out, PG_BITS - 2))
phase = noise_bit(opl) ? _PD(0x300) : _PD(0x200);
else
phase = noise_bit(opl) ? _PD(0x0) : _PD(0x100);
return to_linear(get_wave_table(slot, phase), slot, 0);
}
static INLINE int16_t calc_slot_cym(OPL *opl) {
OPL_SLOT *slot = &(opl->slot[SLOT_CYM]);
uint32_t phase = opl->short_noise ? _PD(0x300) : _PD(0x100);
return to_linear(get_wave_table(slot, phase), slot, 0);
}
static INLINE int16_t calc_slot_hat(OPL *opl) {
OPL_SLOT *slot = &(opl->slot[SLOT_HH]);
uint32_t phase;
if (opl->short_noise)
phase = noise_bit(opl) ? _PD(0x2d0) : _PD(0x234);
else
phase = noise_bit(opl) ? _PD(0x34) : _PD(0xd0);
return to_linear(get_wave_table(slot, phase), slot, 0);
}
#endif
#define _MO(x) (-(x) >> 1)
#define _RO(x) (x)
static INLINE int16_t calc_fm(OPL *opl, int ch) {
if (opl->ch_alg[ch]) {
return calc_slot_car(opl, ch, 0) + calc_slot_mod(opl, ch);
}
return calc_slot_car(opl, ch, calc_slot_mod(opl, ch));
}
#if !EMU8950_NO_TIMER
static void latch_timer1(OPL *opl) { opl->timer1_counter = opl->reg[0x02] << 2; }
static void latch_timer2(OPL *opl) { opl->timer2_counter = opl->reg[0x03] << 4; }
static void csm_key_on(OPL *opl) {
opl->csm_key_count = 1;
update_key_status(opl);
}
static void csm_key_off(OPL *opl) {
opl->csm_key_count = 0;
update_key_status(opl);
}
static void update_timer(OPL *opl) {
if (opl->csm_mode && 0 < opl->csm_key_count) {
csm_key_off(opl);
}
if (opl->reg[0x04] & 0x01) {
opl->timer1_counter++;
if (opl->timer1_counter >> 10) {
opl->status |= 0x40; // timer1 overflow
if (opl->csm_mode) {
csm_key_on(opl);
}
if (opl->timer1_func) {
opl->timer1_func(opl->timer1_user_data);
}
latch_timer1(opl);
}
}
if (opl->reg[0x04] & 0x02) {
opl->timer2_counter++;
if (opl->timer2_counter >> 12) {
opl->status |= 0x20; // timer2 overflow
if (opl->timer2_func) {
opl->timer2_func(opl->timer2_user_data);
}
latch_timer2(opl);
}
}
}
#endif
#if !EMU8950_LINEAR
static void update_output(OPL *opl) {
int16_t *out;
int i;
#if !EMU8950_NO_TIMER
update_timer(opl);
#endif
// generate amplitude modulation same for all channels
// need am_phase and lfo_am
update_ampm(opl);
#if EMU8950_SHORT_NOISE_UPDATE_CHECK
if (opl->mask & (OPL_MASK_CYM | OPL_MASK_HH))
update_short_noise(opl);
#else
update_short_noise(opl);
#endif
update_slots(opl);
out = opl->ch_out;
/* CH1-6 */
for (i = 0; i < 6; i++) {
if (!(opl->mask & OPL_MASK_CH(i))) {
out[i] = _MO(calc_fm(opl, i));
}
}
/* CH7 */
if (!opl_perc_mode(opl)) {
if (!(opl->mask & OPL_MASK_CH(6))) {
out[6] = _MO(calc_fm(opl, 6));
}
} else {
if (!(opl->mask & OPL_MASK_BD)) {
out[9] = _RO(calc_fm(opl, 6));
}
}
update_noise(opl, 14);
/* CH8 */
if (!opl_perc_mode(opl)) {
if (!(opl->mask & OPL_MASK_CH(7))) {
out[7] = _MO(calc_fm(opl, 7));
}
} else {
if (!(opl->mask & OPL_MASK_HH)) {
out[10] = _RO(calc_slot_hat(opl));
}
if (!(opl->mask & OPL_MASK_SD)) {
out[11] = _RO(calc_slot_snare(opl));
}
}
update_noise(opl, 2);
/* CH9 */
if (!opl_perc_mode(opl)) {
if (!(opl->mask & OPL_MASK_CH(8))) {
out[8] = _MO(calc_fm(opl, 8));
}
} else {
if (!(opl->mask & OPL_MASK_TOM)) {
out[12] = _RO(calc_slot_tom(opl));
}
if (!(opl->mask & OPL_MASK_CYM)) {
out[13] = _RO(calc_slot_cym(opl));
}
}
update_noise(opl, 2);
}
INLINE static void mix_output(OPL *opl) {
int16_t out = 0;
int i;
for (i = 0; i < 15; i++) {
out += opl->ch_out[i];
}
#if !EMU8950_NO_RATECONV
if (opl->conv) {
OPL_RateConv_putData(opl->conv, 0, out);
} else {
opl->mix_out[0] = out;
}
#else
opl->mix_out[0] = out;
#endif
}
INLINE static int16_t mix_output_raw(OPL *opl) {
int32_t out = 0;
#if !EMU8950_NO_PERCUSSION_MODE
for (int i = 0; i < 15; i++) {
out += opl->ch_out[i];
}
#else
for (int i = 0; i < 9; i++) {
out += opl->ch_out[i];
}
#endif
return out;
}
#endif
/***********************************************************
External Interfaces
***********************************************************/
OPL *OPL_new(uint32_t clk, uint32_t rate) {
OPL *opl;
if (!table_initialized) {
initializeTables();
}
opl = (OPL *) calloc(sizeof(OPL), 1);
if (opl == NULL)
return NULL;
opl->clk = clk;
opl->rate = rate;
// opl->mask = 0;
#if !EMU8950_NO_RATECONV
// opl->conv = NULL;
#endif
// opl->mix_out[0] = 0;
// opl->mix_out[1] = 0;
#if !EMU8950_NO_TIMER
// opl->timer1_func = NULL;
// opl->timer1_user_data = NULL;
// opl->timer2_func = NULL;
// opl->timer2_user_data = NULL;
#endif
OPL_reset(opl);
return opl;
}
void OPL_delete(OPL *opl) {
#if !EMU8950_NO_RATECONV
if (opl->conv) {
OPL_RateConv_delete(opl->conv);
opl->conv = NULL;
}
#endif
free(opl);
}
static void reset_rate_conversion_params(OPL *opl) {
#if !EMU8950_NO_RATECONV
const double f_out = opl->rate;
const double f_inp = opl->clk / 72;
opl->out_time = 0;
opl->out_step = ((uint32_t) f_inp) << 8;
opl->inp_step = ((uint32_t) f_out) << 8;
if (opl->conv) {
OPL_RateConv_delete(opl->conv);
opl->conv = NULL;
}
if (floor(f_inp) != f_out && floor(f_inp + 0.5) != f_out) {
opl->conv = OPL_RateConv_new(f_inp, f_out, 2);
}
if (opl->conv) {
OPL_RateConv_reset(opl->conv);
}
#endif
}
void OPL_reset(OPL *opl) {
int i;
if (!opl)
return;
#if EMU8950_NO_RATECONV
// no useful fields to preserve
memset(opl, 0, sizeof(*opl));
#else
// some fields are not reset
opl->adr = 0;
opl->notesel = 0;
opl->status = 0;
opl->csm_mode = 0;
opl->csm_key_count = 0;
opl->timer1_counter = 0;
opl->timer2_counter = 0;
opl->pm_phase = 0;
opl->am_phase = 0;
opl->mask = 0;
opl->perc_mode = 0;
opl->slot_key_status = 0;
opl->eg_counter = 0;
#endif
#if !EMU8950_NO_PERCUSSION_MODE
opl->noise = 1;
#endif
reset_rate_conversion_params(opl);
for (i = 0; i < 18; i++) {
reset_slot(&opl->slot[i], i);
}
// for (i = 0; i < 9; i++) {
// opl->ch_alg[i] = 0;
// }
// for (i = 0; i < 0x100; i++) {
// opl->reg[i] = 0;
// }
opl->reg[0x04] = 0x18; // MASK_EOS | MASK_BUF_RDY
opl->pm_dphase = PM_DPHASE;
// for (i = 0; i < 15; i++) {
// opl->ch_out[i] = 0;
// }
}
void OPL_setRate(OPL *opl, uint32_t rate) {
opl->rate = rate;
reset_rate_conversion_params(opl);
}
void OPL_setQuality(OPL *opl, uint8_t q) {}
void OPL_setPan(OPL *opl, uint32_t ch, uint8_t pan) { opl->pan[ch & 15] = pan; }
#if !EMU8950_LINEAR
int16_t OPL_calc(OPL *opl) {
while (opl->out_step > opl->out_time) {
opl->out_time += opl->inp_step;
update_output(opl);
mix_output(opl);
}
opl->out_time -= opl->out_step;
#if !EMU8950_NO_RATECONV
if (opl->conv) {
opl->mix_out[0] = OPL_RateConv_getData(opl->conv, 0);
}
#endif
return opl->mix_out[0];
}
void OPL_calc_buffer(OPL *opl, int16_t *buffer, uint32_t nsamples) {
assert(opl->out_step == opl->inp_step);
for (unsigned i = 0; i < nsamples; i++) {
update_output(opl);
buffer[i] = mix_output_raw(opl);
}
}
#endif
#if PICO_ON_DEVICE
#include "hardware/gpio.h"
#endif
#if !LIB_PICO_PLATFORM
#define __not_in_flash_func(x) x
#endif
#if EMU8950_LINEAR
// these return number of samples rendered (can early out due to silence on the slot - not necessarily the channel)
uint32_t slot_mod_linear(OPL *opl, OPL_SLOT *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase);
uint32_t slot_car_linear_alg0(OPL *opl, OPL_SLOT *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase);
uint32_t slot_car_linear_alg1(OPL *opl, OPL_SLOT *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase);
#if DUMPO
int hack_ch;
#endif
#if !EMU8950_SLOT_RENDER
uint32_t __not_in_flash_func(slot_mod_linear)(OPL *opl, OPL_SLOT *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
uint32_t s = 0;
uint32_t nsamples_bak = nsamples;
if (slot->eg_state == ATTACK) {
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (0 < slot->eg_out && slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
uint8_t step = lookup_attack_step(slot, eg_counter);
slot->eg_out += (~slot->eg_out * step) >> 3;
}
if (slot->eg_out == 0) {
slot->eg_state = DECAY;
// todo collapse
request_update(slot, UPDATE_EG);
commit_slot_update_eg_only(slot, opl->notesel);
nsamples = s;
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0;
slot->output[1] = slot->output[0];
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
opl->mod_buffer[s] = slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out + fm), slot, am);
}
}
if (slot->eg_state == DECAY) {
nsamples = nsamples_bak;
// todo if note ends we can stop early
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
// todo check for decay to zero
}
if ((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL) {
slot->eg_state = SUSTAIN;
request_update(slot, UPDATE_EG);
commit_slot_update_eg_only(slot, opl->notesel);
nsamples = s;
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0;
slot->output[1] = slot->output[0];
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
opl->mod_buffer[s] = slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out + fm), slot, am);
}
}
if (slot->eg_state == SUSTAIN || slot->eg_state == RELEASE) {
nsamples = nsamples_bak;
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
int16_t fm = slot->patch->FB > 0 ? (slot->output[1] + slot->output[0]) >> (9 - slot->patch->FB) : 0;
slot->output[1] = slot->output[0];
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
opl->mod_buffer[s] = slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out + fm), slot, am);
}
}
return nsamples;
}
uint32_t __not_in_flash_func(slot_car_linear_alg1)(OPL *opl, OPL_SLOT *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
uint32_t s = 0;
uint32_t nsamples_bak = nsamples;
if (slot->eg_state == ATTACK) {
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (0 < slot->eg_out && slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
uint8_t step = lookup_attack_step(slot, eg_counter);
slot->eg_out += (~slot->eg_out * step) >> 3;
}
if (slot->eg_out == 0) {
slot->eg_state = DECAY;
// todo collapse
request_update(slot, UPDATE_EG);
commit_slot_update_eg_only(slot, opl->notesel);
nsamples = s;
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
slot->output[1] = slot->output[0];
slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out), slot, am);
opl->buffer[s] += slot->output[0] + opl->mod_buffer[s];
}
}
if (slot->eg_state == DECAY) {
nsamples = nsamples_bak;
// todo if note ends we can stop early
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
// todo check for decay to zero
}
if ((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL) {
slot->eg_state = SUSTAIN;
request_update(slot, UPDATE_EG);
commit_slot_update_eg_only(slot, opl->notesel);
nsamples = s;
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
slot->output[1] = slot->output[0];
slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out), slot, am);
opl->buffer[s] += slot->output[0] + opl->mod_buffer[s];
}
}
if (slot->eg_state == SUSTAIN || slot->eg_state == RELEASE) {
nsamples = nsamples_bak;
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
slot->output[1] = slot->output[0];
slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out), slot, am);
opl->buffer[s] += slot->output[0] + opl->mod_buffer[s];
}
}
return nsamples;
}
uint32_t __not_in_flash_func(slot_car_linear_alg0)(OPL *opl, OPL_SLOT *slot, uint32_t nsamples, uint32_t eg_counter, uint32_t pm_phase) {
uint32_t s = 0;
uint32_t nsamples_bak = nsamples;
if (slot->eg_state == ATTACK) {
for (; s < nsamples; s++) {
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (0 < slot->eg_out && slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
uint8_t step = lookup_attack_step(slot, eg_counter);
slot->eg_out += (~slot->eg_out * step) >> 3;
}
if (slot->eg_out == 0) {
slot->eg_state = DECAY;
// todo collapse
request_update(slot, UPDATE_EG);
commit_slot_update_eg_only(slot, opl->notesel);
nsamples = s;
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
slot->output[1] = slot->output[0];
// todo is this masking realy necessary; i doubt it. .. seems to be always even anyway
// int32_t fm = 2 * (opl->mod_buffer[s] >> 1);
int32_t fm = opl->mod_buffer[s];
slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out + fm), slot, am);
opl->buffer[s] += slot->output[0];
}
}
if (slot->eg_state == DECAY) {
nsamples = nsamples_bak;
// todo if note ends we can stop early
for (; s < nsamples; s++) {
if (hack_ch == 0 && s == 12) {
breako();
}
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
// todo check for decay to zero
}
if ((slot->patch->SL != 15) && (slot->eg_out >> 4) == slot->patch->SL) {
slot->eg_state = SUSTAIN;
request_update(slot, UPDATE_EG);
commit_slot_update_eg_only(slot, opl->notesel);
nsamples = s;
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
slot->output[1] = slot->output[0];
// todo is this masking realy necessary; i doubt it. .. seems to be always even anyway
// int32_t fm = 2 * (opl->mod_buffer[s] >> 1);
int32_t fm = opl->mod_buffer[s];
slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out + fm), slot, am);
opl->buffer[s] += slot->output[0];
}
}
if (slot->eg_state == SUSTAIN || slot->eg_state == RELEASE) {
nsamples = nsamples_bak;
for (; s < nsamples; s++) {
if (hack_ch == 0 && s == 12) {
breako();
}
eg_counter++;
pm_phase = (pm_phase + opl->pm_dphase) & (PM_DP_WIDTH - 1);
uint16_t mask = (1 << slot->eg_shift) - 1;
if (slot->eg_rate_h > 0 && (eg_counter & mask) == 0) {
slot->eg_out = min(EG_MUTE, slot->eg_out + lookup_decay_step(slot, eg_counter));
}
int8_t pm = 0;
if (slot->patch->PM) {
pm = pm_table[(slot->fnum >> 7) & 7][pm_phase >> (PM_DP_BITS - PM_PG_BITS)];
pm >>= (opl->pm_mode ? 0 : 1);
}
slot->pg_phase += (((slot->fnum & 0x3ff) + pm) * ml_table[slot->patch->ML]) << slot->blk >> 1;
slot->pg_phase &= (DP_WIDTH - 1);
uint32_t pg_out = slot->pg_phase >> DP_BASE_BITS;
uint8_t am = slot->patch->AM ? opl->lfo_am_buffer[s] : 0;
slot->output[1] = slot->output[0];
// todo is this masking realy necessary; i doubt it. .. seems to be always even anyway
// int32_t fm = 2 * (opl->mod_buffer[s] >> 1);
int32_t fm = opl->mod_buffer[s];
slot->output[0] = to_linear(get_wave_table_wrap(slot, pg_out + fm), slot, am);
opl->buffer[s] += slot->output[0];
}
}
return nsamples;
}
#endif
static_assert(EMU8950_NO_PERCUSSION_MODE, "");
// this produces stereo
void OPL_calc_buffer_linear(OPL *opl, int32_t *buffer, uint32_t nsamples) {
int i;
#if EMU8950_SLOT_RENDER
// kind of a nit pick, but so cheap - saves a bug every 24 hours due to an optimization
// (we require that incrementing eg_counter is never zero during the rendering loop)
opl->eg_counter = (opl->eg_counter & 0x3fffffffu) | 0x80000000u;
static uint8_t lfo_am_buffer_lsl3[SAMPLE_BUF_SIZE];
assert(nsamples <= sizeof(lfo_am_buffer_lsl3));
opl->lfo_am_buffer_lsl3 = lfo_am_buffer_lsl3;
#else
static uint8_t lfo_am_buffer[SAMPLE_BUF_SIZE];
assert(nsamples <= sizeof(lfo_am_buffer));
opl->lfo_am_buffer = lfo_am_buffer;
#endif
static int16_t mod_buffer[SAMPLE_BUF_SIZE];
opl->mod_buffer = mod_buffer;
opl->buffer = buffer;
// todo achievable by memcpy
for(uint32_t s = 0; s<nsamples; s++) {
// generate amplitude modulation same for all channels
// need am_phase and lfo_am
opl->am_phase_index++;
if (opl->am_phase_index == sizeof(am_table)) opl->am_phase_index = 0;
// todo this is a candidate for remove simply because it is not super noticeable without
#if EMU8950_SLOT_RENDER
// note <<3 still fits within 8 bits
lfo_am_buffer_lsl3[s] = (am_table[opl->am_phase_index] >> (opl->am_mode ? 0 : 2)) << 3;
#else
lfo_am_buffer[s] = (am_table[opl->am_phase_index] >> (opl->am_mode ? 0 : 2));
#endif
buffer[s]=0;
}
for (i = 0; i < 18; i++) {
OPL_SLOT *slot = &opl->slot[i];
int ch = i >> 1;
#if DUMPO
hack_ch = ch;
if (ch == 8 && bc == 1) {
printf("HRMPH\n");
}
#endif
if (slot->update_requests) {
commit_slot_update(slot, opl->notesel);
}
#if EMU8950_SLOT_RENDER
slot->lfo_am_buffer_lsl3 = opl->lfo_am_buffer_lsl3;
slot->pm_mode = opl->pm_mode;
slot->mod_buffer = opl->mod_buffer;
#endif
if (!(i & 1)) {
// ---- MOD SLOT ----
if ((slot+1)->eg_out >= EG_MUTE && (slot+1)->eg_state != ATTACK && !opl->ch_alg[ch]) {
#if DUMPO
memset(slot_output[i], 0, nsamples*2);
memset(slot_output[i+1], 0, nsamples*2);
#endif
i++;
continue;
}
uint32_t s_mod;
#if EMU8950_LINEAR_SKIP // todo consider disabling as almost unnecessary with EMU8950_LINEAR_END_OF_NOTE_OPTIMIZATION
if (slot->eg_out >= EG_MUTE && slot->eg_state != ATTACK) {
s_mod = 0;
} else
#endif
{
// printf("MOD %d\n", i);
// if (bc == 7 && i == 14) {
// printf("WAM\n");
// }
s_mod = slot_mod_linear(opl, slot, nsamples, opl->eg_counter, opl->pm_phase);
}
if (s_mod != nsamples) {
memset(opl->mod_buffer + s_mod, 0, (nsamples - s_mod) * 2);
}
#if DUMPO
memcpy(slot_output[i], opl->mod_buffer, nsamples * 2);
#endif
} else {
#if EMU8950_LINEAR_SKIP
#if EMU8950_SLOT_RENDER
slot->buffer = opl->buffer;
#endif
uint32_t s_alg;
#if EMU8950_LINEAR_SKIP // todo consider disabling as almost unnecessary with EMU8950_LINEAR_END_OF_NOTE_OPTIMIZATION
if (slot->eg_out >= EG_MUTE && slot->eg_state != ATTACK) {
s_alg = 0;
} else
#endif
#if DUMPO
memcpy(opl_buffer_bak, opl->buffer, nsamples * 4);
memset(opl->buffer, 0, nsamples * 4);
#endif
{
if (opl->ch_alg[ch]) {
s_alg = slot_car_linear_alg1(opl, slot, nsamples, opl->eg_counter, opl->pm_phase);
} else {
s_alg = slot_car_linear_alg0(opl, slot, nsamples, opl->eg_counter, opl->pm_phase);
}
}
if (s_alg != nsamples) {
if (opl->ch_alg[ch]) {
for (uint32_t s = s_alg; s < nsamples; s++) {
opl->buffer[s] += opl->mod_buffer[s];
}
}
}
#if DUMPO
for(uint s = 0; s < nsamples; s++) {
slot_output[i][s] = opl->buffer[s];
opl->buffer[s] += opl_buffer_bak[s];
}
#endif
#endif
}
}
opl->pm_phase = (opl->pm_phase + opl->pm_dphase * nsamples) & (PM_DP_WIDTH - 1);
opl->eg_counter += nsamples;
}
#endif
void OPL_calc_buffer_stereo(OPL *opl, int32_t *buffer, uint32_t nsamples) {
assert(opl->out_step == opl->inp_step);
#if DUMPO
bc++;
#endif
#if !EMU8950_LINEAR
for (unsigned i = 0; i < nsamples; i++) {
update_output(opl);
uint16_t raw = mix_output_raw(opl);
#if DUMPO
printf("SND %d %d %08x : ", bc, i, raw);
for (int i = 0; i < 9; i++) {
printf("%04x ", (uint16_t) opl->ch_out[i]);
}
printf("\n");
#endif
buffer[i] = (raw << 16u) | raw;
}
#else
OPL_calc_buffer_linear(opl, buffer, nsamples);
// if (0)
for (unsigned i = 0; i < nsamples; i++) {
#if DUMPO
uint16_t raw = _MO(buffer[i]);
printf("SND %d %d %08x : ", bc, i, raw);
for (int j = 0; j < 18; j++) {
printf("%04x ", (uint16_t) slot_output[j][i]);
}
printf("\n");
#else
uint16_t raw = buffer[i] >> 1; // _MO()
#endif
// todo clamp?
buffer[i] = (raw << 16u) | raw;
}
#endif
}
void OPL_writeReg(OPL *opl, uint32_t reg, uint8_t data) {
// printf("WR %04x %2x\n", reg, data);
int32_t s, c;
static int32_t stbl[32] = {0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1,
12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
reg = reg & 0xff;
if ((reg == 0x04) && (data & 0x80)) {
// IRQ RESET
opl->status = 0;
opl->reg[0x04] &= 0x7f;
return;
}
opl->reg[reg] = data;
if (reg == 0x01) {
#if !EMU8950_NO_TEST_FLAG
opl->test_flag = data;
#endif
} else if (reg == 0x04) {
if (data & 0x01) {
#if !EMU8950_NO_TIMER
latch_timer1(opl);
#else
printf("WARNING TIMER1 LATCH\n");
#endif
}
if (data & 0x02) {
#if !EMU8950_NO_TIMER
latch_timer2(opl);
#else
printf("WARNING TIMER2 LATCH\n");
#endif
}
} else if (0x07 <= reg && reg <= 0x12) {
if (reg == 0x08) {
#if !EMU8950_NO_TIMER
opl->csm_mode = (data >> 7) & 1;
#else
if ((data >> 7) & 1) {
printf("WARNING SET CSM MODE\n");
}
#endif
opl->notesel = (data >> 6) & 1;
}
} else if (0x20 <= reg && reg < 0x40) {
s = stbl[reg - 0x20];
if (s >= 0) {
opl->slot[s].patch->AM = (data >> 7) & 1;
opl->slot[s].patch->PM = (data >> 6) & 1;
opl->slot[s].patch->EG = (data >> 5) & 1;
opl->slot[s].patch->KR = (data >> 4) & 1;
opl->slot[s].patch->ML = (data) & 15;
request_update(&(opl->slot[s]), UPDATE_ALL);
}
} else if (0x40 <= reg && reg < 0x60) {
s = stbl[reg - 0x40];
if (s >= 0) {
#if !EMU8950_NO_TLL
opl->slot[s].patch->TL = (data)&63;
opl->slot[s].patch->KL = (data >> 6) & 3;
#else
opl->slot[s].patch->TL4 = data << 2;
static const uint8_t kslshift[4] = {
8, 1, 2, 0
};
opl->slot[s].patch->KL_SHIFT = kslshift[(data >> 6) & 3];
#endif
request_update(&(opl->slot[s]), UPDATE_ALL);
}
} else if (0x60 <= reg && reg < 0x80) {
s = stbl[reg - 0x60];
if (s >= 0) {
opl->slot[s].patch->AR = (data >> 4) & 15;
opl->slot[s].patch->DR = (data) & 15;
request_update(&(opl->slot[s]), UPDATE_EG);
}
} else if (0x80 <= reg && reg < 0xa0) {
s = stbl[reg - 0x80];
if (s >= 0) {
opl->slot[s].patch->SL = (data >> 4) & 15;
opl->slot[s].patch->RR = (data) & 15;
request_update(&(opl->slot[s]), UPDATE_EG);
}
} else if (0xa0 <= reg && reg < 0xa9) {
c = reg - 0xa0;
set_fnumber(opl, c, data + ((opl->reg[reg + 0x10] & 3) << 8));
} else if (0xb0 <= reg && reg < 0xb9) {
c = reg - 0xb0;
set_fnumber(opl, c, ((data & 3) << 8) + opl->reg[reg - 0x10]);
set_block(opl, c, (data >> 2) & 7);
update_key_status(opl);
} else if (0xc0 <= reg && reg < 0xc9) {
c = reg - 0xc0;
opl->slot[c * 2].patch->FB = (data >> 1) & 7;
opl->ch_alg[c] = data & 1;
} else if (reg == 0xbd) {
update_perc_mode(opl);
update_key_status(opl);
opl->am_mode = (data >> 7) & 1;
opl->pm_mode = (data >> 6) & 1;
} else if (0xe0 <= reg && reg < 0x100) {
if (opl->reg[0x01] & 0x20) {
s = stbl[reg - 0xe0];
if (s >= 0) {
opl->slot[s].patch->WS = data & 3;
request_update(&(opl->slot[s]), UPDATE_WS);
}
}
}
}
#endif