const OPM_PG_BASE_TABLE = [
1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1308, 1309, 1310, 1311, 1313, 1314, 1315, 1316,
1318, 1319, 1320, 1321, 1322, 1323, 1324, 1325, 1327, 1328, 1329, 1330, 1332, 1333, 1334, 1335,
1337, 1338, 1339, 1340, 1341, 1342, 1343, 1344, 1346, 1347, 1348, 1349, 1351, 1352, 1353, 1354,
1356, 1357, 1358, 1359, 1361, 1362, 1363, 1364, 1366, 1367, 1368, 1369, 1371, 1372, 1373, 1374,
1376, 1377, 1378, 1379, 1381, 1382, 1383, 1384, 1386, 1387, 1388, 1389, 1391, 1392, 1393, 1394,
1396, 1397, 1398, 1399, 1401, 1402, 1403, 1404, 1406, 1407, 1408, 1409, 1411, 1412, 1413, 1414,
1416, 1417, 1418, 1419, 1421, 1422, 1423, 1424, 1426, 1427, 1429, 1430, 1431, 1432, 1434, 1435,
1437, 1438, 1439, 1440, 1442, 1443, 1444, 1445, 1447, 1448, 1449, 1450, 1452, 1453, 1454, 1455,
1458, 1459, 1460, 1461, 1463, 1464, 1465, 1466, 1468, 1469, 1471, 1472, 1473, 1474, 1476, 1477,
1479, 1480, 1481, 1482, 1484, 1485, 1486, 1487, 1489, 1490, 1492, 1493, 1494, 1495, 1497, 1498,
1501, 1502, 1503, 1504, 1506, 1507, 1509, 1510, 1512, 1513, 1514, 1515, 1517, 1518, 1520, 1521,
1523, 1524, 1525, 1526, 1528, 1529, 1531, 1532, 1534, 1535, 1536, 1537, 1539, 1540, 1542, 1543,
1545, 1546, 1547, 1548, 1550, 1551, 1553, 1554, 1556, 1557, 1558, 1559, 1561, 1562, 1564, 1565,
1567, 1568, 1569, 1570, 1572, 1573, 1575, 1576, 1578, 1579, 1580, 1581, 1583, 1584, 1586, 1587,
1590, 1591, 1592, 1593, 1595, 1596, 1598, 1599, 1601, 1602, 1604, 1605, 1607, 1608, 1609, 1610,
1613, 1614, 1615, 1616, 1618, 1619, 1621, 1622, 1624, 1625, 1627, 1628, 1630, 1631, 1632, 1633,
1637, 1638, 1639, 1640, 1642, 1643, 1645, 1646, 1648, 1649, 1651, 1652, 1654, 1655, 1656, 1657,
1660, 1661, 1663, 1664, 1666, 1667, 1669, 1670, 1672, 1673, 1675, 1676, 1678, 1679, 1681, 1682,
1685, 1686, 1688, 1689, 1691, 1692, 1694, 1695, 1697, 1698, 1700, 1701, 1703, 1704, 1706, 1707,
1709, 1710, 1712, 1713, 1715, 1716, 1718, 1719, 1721, 1722, 1724, 1725, 1727, 1728, 1730, 1731,
1734, 1735, 1737, 1738, 1740, 1741, 1743, 1744, 1746, 1748, 1749, 1751, 1752, 1754, 1755, 1757,
1759, 1760, 1762, 1763, 1765, 1766, 1768, 1769, 1771, 1773, 1774, 1776, 1777, 1779, 1780, 1782,
1785, 1786, 1788, 1789, 1791, 1793, 1794, 1796, 1798, 1799, 1801, 1802, 1804, 1806, 1807, 1809,
1811, 1812, 1814, 1815, 1817, 1819, 1820, 1822, 1824, 1825, 1827, 1828, 1830, 1832, 1833, 1835,
1837, 1838, 1840, 1841, 1843, 1845, 1846, 1848, 1850, 1851, 1853, 1854, 1856, 1858, 1859, 1861,
1864, 1865, 1867, 1868, 1870, 1872, 1873, 1875, 1877, 1879, 1880, 1882, 1884, 1885, 1887, 1888,
1891, 1892, 1894, 1895, 1897, 1899, 1900, 1902, 1904, 1906, 1907, 1909, 1911, 1912, 1914, 1915,
1918, 1919, 1921, 1923, 1925, 1926, 1928, 1930, 1932, 1933, 1935, 1937, 1939, 1940, 1942, 1944,
1946, 1947, 1949, 1951, 1953, 1954, 1956, 1958, 1960, 1961, 1963, 1965, 1967, 1968, 1970, 1972,
1975, 1976, 1978, 1980, 1982, 1983, 1985, 1987, 1989, 1990, 1992, 1994, 1996, 1997, 1999, 2001,
2003, 2004, 2006, 2008, 2010, 2011, 2013, 2015, 2017, 2019, 2021, 2022, 2024, 2026, 2028, 2029,
2032, 2033, 2035, 2037, 2039, 2041, 2043, 2044, 2047, 2048, 2050, 2052, 2054, 2056, 2058, 2059,
2062, 2063, 2065, 2067, 2069, 2071, 2073, 2074, 2077, 2078, 2080, 2082, 2084, 2086, 2088, 2089,
2092, 2093, 2095, 2097, 2099, 2101, 2103, 2104, 2107, 2108, 2110, 2112, 2114, 2116, 2118, 2119,
2122, 2123, 2125, 2127, 2129, 2131, 2133, 2134, 2137, 2139, 2141, 2142, 2145, 2146, 2148, 2150,
2153, 2154, 2156, 2158, 2160, 2162, 2164, 2165, 2168, 2170, 2172, 2173, 2176, 2177, 2179, 2181,
2185, 2186, 2188, 2190, 2192, 2194, 2196, 2197, 2200, 2202, 2204, 2205, 2208, 2209, 2211, 2213,
2216, 2218, 2220, 2222, 2223, 2226, 2227, 2230, 2232, 2234, 2236, 2238, 2239, 2242, 2243, 2246,
2249, 2251, 2253, 2255, 2256, 2259, 2260, 2263, 2265, 2267, 2269, 2271, 2272, 2275, 2276, 2279,
2281, 2283, 2285, 2287, 2288, 2291, 2292, 2295, 2297, 2299, 2301, 2303, 2304, 2307, 2308, 2311,
2315, 2317, 2319, 2321, 2322, 2325, 2326, 2329, 2331, 2333, 2335, 2337, 2338, 2341, 2342, 2345,
2348, 2350, 2352, 2354, 2355, 2358, 2359, 2362, 2364, 2366, 2368, 2370, 2371, 2374, 2375, 2378,
2382, 2384, 2386, 2388, 2389, 2392, 2393, 2396, 2398, 2400, 2402, 2404, 2407, 2410, 2411, 2414,
2417, 2419, 2421, 2423, 2424, 2427, 2428, 2431, 2433, 2435, 2437, 2439, 2442, 2445, 2446, 2449,
2452, 2454, 2456, 2458, 2459, 2462, 2463, 2466, 2468, 2470, 2472, 2474, 2477, 2480, 2481, 2484,
2488, 2490, 2492, 2494, 2495, 2498, 2499, 2502, 2504, 2506, 2508, 2510, 2513, 2516, 2517, 2520,
2524, 2526, 2528, 2530, 2531, 2534, 2535, 2538, 2540, 2542, 2544, 2546, 2549, 2552, 2553, 2556,
2561, 2563, 2565, 2567, 2568, 2571, 2572, 2575, 2577, 2579, 2581, 2583, 2586, 2589, 2590, 2593
];
const OPMStage_ATTACK = 0;
const OPMStage_DECAY = 1;
const OPMStage_SUSTAIN = 2;
const OPMStage_RELEASE = 3;
const OPMStage_SILENCE = 4;
const OPM_EG_SHIFT_TABLE = [
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,
11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8,
7 , 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4,
3 , 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 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,
0 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
];
const OPM_EG_TABLE_X = [
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x01010101, 0x01011101, 0x01110111, 0x01111111, 0x01010101, 0x01011101, 0x01110111, 0x01111111,
0x01010101, 0x01011101, 0x01110111, 0x01111111, 0x01010101, 0x01011101, 0x01110111, 0x01111111,
0x01010101, 0x01011101, 0x01110111, 0x01111111, 0x01010101, 0x01011101, 0x01110111, 0x01111111,
0x01010101, 0x01011101, 0x01110111, 0x01111111, 0x01010101, 0x01011101, 0x01110111, 0x01111111,
0x01010101, 0x01011101, 0x01110111, 0x01111111, 0x01010101, 0x01011101, 0x01110111, 0x01111111,
0x01010101, 0x01011101, 0x01110111, 0x01111111, 0x01010101, 0x01011101, 0x01110111, 0x01111111,
0x11111111, 0x11121112, 0x12121212, 0x12221222, 0x22222222, 0x22242224, 0x24242424, 0x24442444,
0x44444444, 0x44484448, 0x48484848, 0x48884888, 0x88888888, 0x88888888, 0x88888888, 0x88888888,
0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888,
0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888,
0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888,
0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888, 0x88888888
];
const OPM_DT1_BASE_TABLE = [
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, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16,
2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22
];
const OPM_LFO_NOISE_BASE = [
0xff, 0xee, 0xd3, 0x80, 0x58, 0xda, 0x7f, 0x94, 0x9e, 0xe3, 0xfa, 0x00, 0x4d, 0xfa, 0xff, 0x6a,
0x7a, 0xde, 0x49, 0xf6, 0x00, 0x33, 0xbb, 0x63, 0x91, 0x60, 0x51, 0xff, 0x00, 0xd8, 0x7f, 0xde,
0xdc, 0x73, 0x21, 0x85, 0xb2, 0x9c, 0x5d, 0x24, 0xcd, 0x91, 0x9e, 0x76, 0x7f, 0x20, 0xfb, 0xf3,
0x00, 0xa6, 0x3e, 0x42, 0x27, 0x69, 0xae, 0x33, 0x45, 0x44, 0x11, 0x41, 0x72, 0x73, 0xdf, 0xa2,
0x32, 0xbd, 0x7e, 0xa8, 0x13, 0xeb, 0xd3, 0x15, 0xdd, 0xfb, 0xc9, 0x9d, 0x61, 0x2f, 0xbe, 0x9d,
0x23, 0x65, 0x51, 0x6a, 0x84, 0xf9, 0xc9, 0xd7, 0x23, 0xbf, 0x65, 0x19, 0xdc, 0x03, 0xf3, 0x24,
0x33, 0xb6, 0x1e, 0x57, 0x5c, 0xac, 0x25, 0x89, 0x4d, 0xc5, 0x9c, 0x99, 0x15, 0x07, 0xcf, 0xba,
0xc5, 0x9b, 0x15, 0x4d, 0x8d, 0x2a, 0x1e, 0x1f, 0xea, 0x2b, 0x2f, 0x64, 0xa9, 0x50, 0x3d, 0xab,
0x50, 0x77, 0xe9, 0xc0, 0xac, 0x6d, 0x3f, 0xca, 0xcf, 0x71, 0x7d, 0x80, 0xa6, 0xfd, 0xff, 0xb5,
0xbd, 0x6f, 0x24, 0x7b, 0x00, 0x99, 0x5d, 0xb1, 0x48, 0xb0, 0x28, 0x7f, 0x80, 0xec, 0xbf, 0x6f,
0x6e, 0x39, 0x90, 0x42, 0xd9, 0x4e, 0x2e, 0x12, 0x66, 0xc8, 0xcf, 0x3b, 0x3f, 0x10, 0x7d, 0x79,
0x00, 0xd3, 0x1f, 0x21, 0x93, 0x34, 0xd7, 0x19, 0x22, 0xa2, 0x08, 0x20, 0xb9, 0xb9, 0xef, 0x51,
0x99, 0xde, 0xbf, 0xd4, 0x09, 0x75, 0xe9, 0x8a, 0xee, 0xfd, 0xe4, 0x4e, 0x30, 0x17, 0xdf, 0xce,
0x11, 0xb2, 0x28, 0x35, 0xc2, 0x7c, 0x64, 0xeb, 0x91, 0x5f, 0x32, 0x0c, 0x6e, 0x00, 0xf9, 0x92,
0x19, 0xdb, 0x8f, 0xab, 0xae, 0xd6, 0x12, 0xc4, 0x26, 0x62, 0xce, 0xcc, 0x0a, 0x03, 0xe7, 0xdd,
0xe2, 0x4d, 0x8a, 0xa6, 0x46, 0x95, 0x0f, 0x8f, 0xf5, 0x15, 0x97, 0x32, 0xd4, 0x28, 0x1e, 0x55
];
let opmLFOAMTable;
let opmLFOPMTable;
let opmTLTable;
let opmSinTable;
let opmFreqTable;
let opmDT1FreqTable;
let opmNoiseTable;
function opmInit () {
opmLFOAMTable = new Int32Array (256 * 4);
opmLFOPMTable = new Int32Array (256 * 4);
for (let i = 0; i < 256; i++) {
opmLFOAMTable[i] = 255 - i;
opmLFOPMTable[i] = i < 128 ? i : i - 255;
opmLFOAMTable[256 + i] = i < 128 ? 255 : 0;
opmLFOPMTable[256 + i] = i < 128 ? 128 : -128;
opmLFOAMTable[512 + i] = i < 128 ? 255 - i * 2 : i * 2 - 256;
opmLFOPMTable[512 + i] = i < 64 ? i * 2 : i < 128 ? 255 - i * 2 : i < 192 ? 256 - i * 2 : i * 2 - 511;
opmLFOAMTable[768 + i] = OPM_LFO_NOISE_BASE[i];
opmLFOPMTable[768 + i] = OPM_LFO_NOISE_BASE[i] - 128;
}
opmTLTable = new Int32Array (OPM_TL_TABLE_SIZE);
for (let x = 0; x < OPM_TL_BLOCK_SIZE; x += 2) {
let t = ((trunc (pow (2.0, 16.0 + (x + 2) / -(OPM_TL_SIZE >> 1))) + 16) & -32) >> 3;
opmTLTable[x] = t;
opmTLTable[x + 1] = -t;
for (let i = 1; i < 13; i++) {
opmTLTable[x + OPM_TL_BLOCK_SIZE * i + 1] = -(opmTLTable[x + OPM_TL_BLOCK_SIZE * i] = t >> i);
}
}
opmSinTable = new Int32Array (OPM_SIN_TABLE_SIZE);
for (let i = 0; i < OPM_SIN_TABLE_SIZE; i++) {
opmSinTable[i] = ((trunc ((-OPM_TL_SIZE >> 1) / LN2 *
log (abs (sin (PI / OPM_SIN_TABLE_SIZE * (i * 2 + 1))))
) + 1) & ~1) +
(i < OPM_SIN_TABLE_SIZE >> 1 ? 0 : 1);
}
opmFreqTable = new Int32Array (768 * 11);
for (let i = 0; i < 768; i++) {
let t = OPM_PG_BASE_TABLE[i];
opmFreqTable[768 * 1 + i] = t >> 2 << ( OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 2 + i] = t >> 1 << ( OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 3 + i] = t << ( OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 4 + i] = t << (1 + OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 5 + i] = t << (2 + OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 6 + i] = t << (3 + OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 7 + i] = t << (4 + OPM_PHASE_SHIFT - 10);
opmFreqTable[768 * 8 + i] = t << (5 + OPM_PHASE_SHIFT - 10);
}
{
let t = opmFreqTable[768 * 1 + 0];
for (let i = 768 * 0; i < 768 * 1; i++) {
opmFreqTable[i] = t;
}
t = opmFreqTable[768 * 9 - 1];
for (let i = 768 * 9; i < 768 * 11; i++) {
opmFreqTable[i] = t;
}
}
opmDT1FreqTable = new Int32Array (256);
for (let i = 0; i < 128; i++) {
let t = OPM_DT1_BASE_TABLE[i] << (OPM_SIN_BITS + OPM_PHASE_SHIFT - 20);
opmDT1FreqTable[i] = t;
opmDT1FreqTable[128 + i] = -t;
}
opmNoiseTable = new Int32Array (32);
for (let i = 0; i < 32; i++) {
opmNoiseTable[i] = 2048 / (32 - i + ((i + 1) >> 5)) << 6;
}
opmPortArray = new Array (OPM_PORT_COUNT);
for (let pn = 0; pn < OPM_PORT_COUNT; pn++) {
let port = opmPortArray[pn] = {
op$PortNumber: pn,
op$CLKA: 0,
op$CLKB: 0,
op$IntervalA: 64 / OPM_OSC_FREQ * (1024 - 0),
op$IntervalB: 1024 / OPM_OSC_FREQ * (256 - 0),
op$ClockA: Infinity,
op$ClockB: Infinity,
op$ISTA: 0,
op$ISTB: 0,
op$IRQENA: false,
op$IRQENB: false,
op$CSMOn: false,
op$BusyEndTime: 0,
op$EGCounter: 1,
op$EGTimer: 3,
op$LFOActive: true,
op$LFOWaveIndex: 0,
op$WAVE256: 0,
op$LFOAMValue: opmLFOAMTable[0],
op$LFOPMValue: opmLFOPMTable[0],
op$LFOCounterMinor: 1 << 18,
op$LFOPeriodMinor: 1 << 18,
op$LFOCounterMajor: 0,
op$LFOPeriodMajor: 16,
op$AMD: 0,
op$PMD: 0,
op$LFOAMOutput: 0,
op$LFOPMOutput: 0,
op$NoiseRegister: 0,
op$NoisePhase: 0,
op$NoiseFrequency: opmNoiseTable[0],
op$CSMRequest: 0,
op$Joint1Box: new Int32Array ([0]),
op$Joint2Box: new Int32Array ([0]),
op$Joint3Box: new Int32Array ([0]),
op$Joint4Box: new Int32Array ([0]),
op$Slot: new Array (OPM_SLOT_COUNT),
op$Channel: new Array (OPM_CHANNEL_COUNT),
op$CT1: 0,
op$CT2: 0
};
for (let sn = 0; sn < OPM_SLOT_COUNT; sn++) {
let slot = port.op$Slot[sn] = {
sl$SlotNumber: sn,
sl$Port: port,
sl$Channel: null,
sl$Phase: 0,
sl$Freq: 0,
sl$Detune1Freq: 0,
sl$Multiply: 0,
sl$Detune1Page: 0,
sl$Detune2Depth: 0,
sl$AMSMask: 0,
sl$TotalLevel: 0,
sl$Volume: OPM_TL_SIZE - 1,
sl$KeyStatus: 0,
sl$KeyScale: 0,
sl$Stage: OPMStage_SILENCE,
sl$AttackRate: 0,
sl$AttackShift: 0,
sl$Attack3: 0,
sl$Attack4: 0,
sl$DecayRate: 0,
sl$DecayShift: 0,
sl$Decay3: 0,
sl$Decay4: 0,
sl$DecayLevel: 0,
sl$SustainRate: 0,
sl$SustainShift: 0,
sl$Sustain3: 0,
sl$Sustain4: 0,
sl$ReleaseRate: 0,
sl$ReleaseShift: 0,
sl$Release3: 0,
sl$Release4: 0,
sl$TransitionMask: 1
};
}
for (let cn = 0; cn < OPM_CHANNEL_COUNT; cn++) {
let cnlo = cn & 7;
let cnhi = cn >> 3;
let m1 = port.op$Slot[(cnhi << 5) + cnlo];
let m2 = port.op$Slot[(cnhi << 5) + 8 + cnlo];
let c1 = port.op$Slot[(cnhi << 5) + 16 + cnlo];
let c2 = port.op$Slot[(cnhi << 5) + 24 + cnlo];
let channel = port.op$Channel[cn] = {
ch$ChannelNumber: cn,
ch$Port: port,
ch$M1: m1,
ch$C1: c1,
ch$M2: m2,
ch$C2: c2,
ch$KC: 0,
ch$KF: 0,
ch$KeyIndex: 0,
ch$OutputBox: new Int32Array ([0]),
ch$fbOutputHandle: port.op$Joint1Box,
ch$fbInputValue: 0,
ch$fbPreviousValue: 0,
ch$fbScale: 0,
ch$bfInputHandle: port.op$Joint2Box,
ch$bfInputValue: 0,
ch$bfOutputHandle: port.op$Joint3Box,
ch$LeftMask: 0,
ch$RightMask: 0,
ch$NoiseOn: false,
ch$c1InputHandle: port.op$Joint1Box,
ch$c1OutputHandle: port.op$Joint2Box,
ch$m2InputHandle: port.op$Joint3Box,
ch$m2OutputHandle: port.op$Joint4Box,
ch$c2InputHandle: port.op$Joint4Box,
ch$FLCON: 0,
ch$SLOT: 0,
ch$SYNC: false,
ch$PAN: 0,
ch$AMS: 0,
ch$ShiftAMS: 0,
ch$PMS: 0,
ch$ShiftPMS: 0
};
m1.sl$Channel = channel;
m2.sl$Channel = channel;
c1.sl$Channel = channel;
c2.sl$Channel = channel;
chReset (channel);
}
opmSet (port, 0x1b, 0);
opmSet (port, 0x18, 0);
for (let cnhi = 0; cnhi < (OPM_CHANNEL_COUNT >> 3); cnhi++) {
for (let a = 0x20; a <= 0xff; a++) {
opmSet (port, (cnhi << 8) + a, 0);
}
}
}
}
function slReset (slot) {
slot.sl$Phase = 0;
slot.sl$Freq = 0;
slot.sl$Detune1Freq = 0;
slot.sl$Multiply = 0;
slot.sl$Detune1Page = 0;
slot.sl$Detune2Depth = 0;
slot.sl$AMSMask = 0;
slot.sl$TotalLevel = 0;
slot.sl$Volume = OPM_TL_SIZE - 1;
slot.sl$KeyStatus = 0;
slot.sl$KeyScale = 0;
slot.sl$Stage = OPMStage_SILENCE;
slot.sl$AttackRate = 0;
slot.sl$AttackShift = 0;
slot.sl$Attack3 = 0;
slot.sl$Attack4 = 0;
slot.sl$DecayRate = 0;
slot.sl$DecayShift = 0;
slot.sl$Decay3 = 0;
slot.sl$Decay4 = 0;
slot.sl$DecayLevel = 0;
slot.sl$SustainRate = 0;
slot.sl$SustainShift = 0;
slot.sl$Sustain3 = 0;
slot.sl$Sustain4 = 0;
slot.sl$ReleaseRate = 0;
slot.sl$ReleaseShift = 0;
slot.sl$Release3 = 0;
slot.sl$Release4 = 0;
slot.sl$TransitionMask = 1;
}
function chReset (channel) {
let port = channel.ch$Port;
slReset (channel.ch$M1);
slReset (channel.ch$C1);
slReset (channel.ch$M2);
slReset (channel.ch$C2);
channel.ch$KC = 0;
channel.ch$KF = 0;
channel.ch$KeyIndex = 768;
channel.ch$OutputBox[0] = 0;
channel.ch$fbOutputHandle = port.op$Joint1Box;
channel.ch$fbInputValue = 0;
channel.ch$fbPreviousValue = 0;
channel.ch$fbScale = 0;
channel.ch$bfInputHandle = port.op$Joint2Box;
channel.ch$bfInputValue = 0;
channel.ch$bfOutputHandle = port.op$Joint3Box;
channel.ch$LeftMask = 0;
channel.ch$RightMask = 0;
channel.ch$NoiseOn = false;
channel.ch$c1OutputHandle = port.op$Joint2Box;
channel.ch$m2OutputHandle = port.op$Joint4Box;
channel.ch$c2InputHandle = port.op$Joint4Box;
channel.ch$FLCON = 0;
channel.ch$SLOT = 0;
channel.ch$SYNC = true;
channel.ch$PAN = 0;
channel.ch$AMS = 0;
channel.ch$ShiftAMS = 0;
channel.ch$PMS = 0;
channel.ch$ShiftPMS = 0;
}
function opmRead (port) {
return (commandTime < port.op$BusyEndTime ? 0x80 : 0x00) | port.op$ISTB | port.op$ISTA;
}
function opmSet (port, a, d) {
port.op$BusyEndTime = commandTime + 68 / OPM_OSC_FREQ;
let cnhi = (a >> 8) & ((OPM_CHANNEL_COUNT >> 3) - 1);
a &= 0xff;
d &= 0xff;
if (a < 0x20) {
switch (a) {
case 0x01:
port.op$LFOActive = (d & 2) == 0;
if (!port.op$LFOActive) {
port.op$LFOWaveIndex = 0;
port.op$LFOAMValue = opmLFOAMTable[port.op$WAVE256];
port.op$LFOPMValue = opmLFOPMTable[port.op$WAVE256];
}
break;
case 0x08:
chKeyOnOff (port.op$Channel[(cnhi << 3) + (d & 7)], d >> 3);
break;
case 0x0f:
port.op$Channel[7].ch$NoiseOn = d < 0;
port.op$NoiseFrequency = opmNoiseTable[d & 31];
break;
case 0x10:
port.op$CLKA = d << 2 | (port.op$CLKA & 0x03);
port.op$IntervalA = 64 / OPM_OSC_FREQ * (1024 - port.op$CLKA);
if (isFinite (port.op$ClockA)) {
port.op$ClockA = commandTime + port.op$IntervalA;
removeCommand ("opminta", port.op$PortNumber);
addCommand ({
t: port.op$ClockA,
c: "opminta",
p: port.op$PortNumber
});
}
break;
case 0x11:
port.op$CLKA = (port.op$CLKA & 0x3fc) | (d & 0x03);
port.op$IntervalA = 64 / OPM_OSC_FREQ * (1024 - port.op$CLKA);
if (isFinite (port.op$ClockA)) {
port.op$ClockA = commandTime + port.op$IntervalA;
removeCommand ("opminta", port.op$PortNumber);
addCommand ({
t: port.op$ClockA,
c: "opminta",
p: port.op$PortNumber
});
}
break;
case 0x12:
port.op$CLKB = d;
port.op$IntervalB = 1024 / OPM_OSC_FREQ * (256 - port.op$CLKB);
if (isFinite (port.op$ClockB)) {
port.op$ClockB = commandTime + port.op$IntervalB;
removeCommand ("opmintb", port.op$PortNumber);
addCommand ({
t: port.op$ClockB,
c: "opmintb",
p: port.op$PortNumber
});
}
break;
case 0x14:
if ((d & OPM_LOADA) != 0) {
if (!isFinite (port.op$ClockA)) {
if ((port.op$ISTA | port.op$ISTB) != 0) {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
} else {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq1",
p: port.op$PortNumber
});
}
port.op$ClockA = commandTime + port.op$IntervalA;
removeCommand ("opminta", port.op$PortNumber);
addCommand ({
t: port.op$ClockA,
c: "opminta",
p: port.op$PortNumber
});
}
} else {
if (isFinite (port.op$ClockA)) {
if ((port.op$ISTA | port.op$ISTB) != 0) {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
} else {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq1",
p: port.op$PortNumber
});
}
port.op$ClockA = Infinity;
removeCommand ("opminta", port.op$PortNumber);
addCommand ({
t: port.op$ClockA,
c: "opminta",
p: port.op$PortNumber
});
}
}
if ((d & OPM_LOADB) != 0) {
if (!isFinite (port.op$ClockB)) {
if ((port.op$ISTA | port.op$ISTB) != 0) {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
} else {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq1",
p: port.op$PortNumber
});
}
port.op$ClockB = commandTime + port.op$IntervalB;
removeCommand ("opmintb", port.op$PortNumber);
addCommand ({
t: port.op$ClockB,
c: "opmintb",
p: port.op$PortNumber
});
}
} else {
if (isFinite (port.op$ClockB)) {
if ((port.op$ISTA | port.op$ISTB) != 0) {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
} else {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq1",
p: port.op$PortNumber
});
}
port.op$ClockB = Infinity;
removeCommand ("opmintb", port.op$PortNumber);
addCommand ({
t: port.op$ClockB,
c: "opmintb",
p: port.op$PortNumber
});
}
}
port.op$IRQENA = (d & OPM_IRQENA) != 0;
port.op$IRQENB = (d & OPM_IRQENB) != 0;
if ((d & OPM_RESETA) != 0) {
port.op$ISTA = 0;
if (port.op$ISTB != 0) {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
} else {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq1",
p: port.op$PortNumber
});
}
}
if ((d & OPM_RESETB) != 0) {
port.op$ISTB = 0;
if (port.op$ISTA != 0) {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
} else {
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq1",
p: port.op$PortNumber
});
}
}
port.op$CSMOn = d < 0;
break;
case 0x18:
port.op$LFOPeriodMajor = 16 + (d & 15);
port.op$LFOCounterMinor = port.op$LFOPeriodMinor = 1 << (18 - (d >> 4));
break;
case 0x19:
if ((d & 0x80) == 0) {
port.op$AMD = d & 127;
} else {
port.op$PMD = d & 127;
}
break;
case 0x1b:
port.op$WAVE256 = (d & 3) << 8;
port.op$LFOAMValue = opmLFOAMTable[port.op$WAVE256 + port.op$LFOWaveIndex];
port.op$LFOPMValue = opmLFOPMTable[port.op$WAVE256 + port.op$LFOWaveIndex];
let ct1 = d >> 7 & 1;
if (port.op$CT1 != ct1) {
port.op$CT1 = ct1;
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmct1" + ct1,
p: port.op$PortNumber
});
}
let ct2 = d >> 6 & 1;
if (port.op$CT2 != ct2) {
port.op$CT2 = ct2;
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmct2" + ct2,
p: port.op$PortNumber
});
}
break;
}
} else if (a < 0x40) {
let channel = port.op$Channel[(cnhi << 3) + (a & 7)];
switch (a >> 3) {
case 0x20 >> 3:
chSetFLCON (channel, d);
chSetPAN (channel, d >> 6);
break;
case 0x28 >> 3:
chSetKC (channel, d);
break;
case 0x30 >> 3:
chSetKF (channel, d >> 2);
break;
case 0x38 >> 3:
chSetAMS (channel, d);
chSetPMS (channel, d >> 4);
break;
}
} else {
let slot = port.op$Slot[(cnhi << 5) + (a & 31)];
switch (a >> 5) {
case 0x40 >> 5:
slSetMUL (slot, d);
slSetDT1 (slot, d >> 4);
break;
case 0x60 >> 5:
slSetTL (slot, d);
break;
case 0x80 >> 5:
slSetAR (slot, d);
slSetKS (slot, d >> 6);
break;
case 0xa0 >> 5:
slSetD1R (slot, d);
slSetAMSEN (slot, d >> 7);
break;
case 0xc0 >> 5:
slSetD2R (slot, d);
slSetDT2 (slot, d >> 6);
break;
case 0xe0 >> 5:
slSetRR (slot, d);
slSetD1L (slot, d >> 4);
break;
}
}
}
function opmIntA (port) {
port.op$ClockA += port.op$IntervalA;
addCommand ({
t: port.op$ClockA,
c: "opminta",
p: port.op$PortNumber
});
if (port.op$IRQENA) {
port.op$ISTA = OPM_ISTA;
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
}
if (port.op$CSMOn) {
port.op$CSMRequest = 1;
}
}
function opmIntB (port) {
port.op$ClockB += port.op$IntervalB;
addCommand ({
t: port.op$ClockB,
c: "opmintb",
p: port.op$PortNumber
});
if (port.op$IRQENB) {
port.op$ISTB = OPM_ISTB;
opmpcmProcessor.port.postMessage ({
t: commandTime,
c: "opmirq0",
p: port.op$PortNumber
});
}
if (port.op$CSMOn) {
port.op$CSMRequest = 1;
}
}
function chKeyOnOff (channel, mask) {
let port = channel.ch$Port;
if (mask != 0) {
channel.ch$SLOT = mask;
channel.ch$SYNC = port.op$LFOWaveIndex == 0;
}
if ((mask & 0x01) != 0) {
slKeyOn (channel.ch$M1, 1);
} else {
slKeyOff (channel.ch$M1, ~1);
}
if ((mask & 0x02) != 0) {
slKeyOn (channel.ch$C1, 1);
} else {
slKeyOff (channel.ch$C1, ~1);
}
if ((mask & 0x04) != 0) {
slKeyOn (channel.ch$M2, 1);
} else {
slKeyOff (channel.ch$M2, ~1);
}
if ((mask & 0x08) != 0) {
slKeyOn (channel.ch$C2, 1);
} else {
slKeyOff (channel.ch$C2, ~1);
}
}
function chSetFLCON (channel, v) {
let port = channel.ch$Port;
v &= 63;
channel.ch$FLCON = v;
switch (v & 7) {
case 0:
channel.ch$fbOutputHandle = port.op$Joint1Box;
channel.ch$c1OutputHandle = channel.ch$bfInputHandle = port.op$Joint2Box;
channel.ch$bfOutputHandle = port.op$Joint3Box;
channel.ch$m2OutputHandle = channel.ch$c2InputHandle = port.op$Joint4Box;
break;
case 1:
channel.ch$fbOutputHandle = channel.ch$c1OutputHandle = channel.ch$bfInputHandle = port.op$Joint2Box;
channel.ch$bfOutputHandle = port.op$Joint3Box;
channel.ch$m2OutputHandle = channel.ch$c2InputHandle = port.op$Joint4Box;
break;
case 2:
channel.ch$c1OutputHandle = channel.ch$bfInputHandle = port.op$Joint2Box;
channel.ch$bfOutputHandle = port.op$Joint3Box;
channel.ch$fbOutputHandle = channel.ch$m2OutputHandle = channel.ch$c2InputHandle = port.op$Joint4Box;
break;
case 3:
channel.ch$fbOutputHandle = port.op$Joint1Box;
channel.ch$c1OutputHandle = channel.ch$bfInputHandle = port.op$Joint2Box;
channel.ch$bfOutputHandle = channel.ch$m2OutputHandle = channel.ch$c2InputHandle = port.op$Joint4Box;
break;
case 4:
channel.ch$fbOutputHandle = port.op$Joint1Box;
channel.ch$bfOutputHandle = port.op$Joint2Box;
channel.ch$m2OutputHandle = channel.ch$c2InputHandle = port.op$Joint4Box;
channel.ch$c1OutputHandle = channel.ch$OutputBox;
break;
case 5:
channel.ch$fbOutputHandle = channel.ch$bfInputHandle = channel.ch$c2InputHandle = port.op$Joint1Box;
channel.ch$bfOutputHandle = port.op$Joint3Box;
channel.ch$c1OutputHandle = channel.ch$m2OutputHandle = channel.ch$OutputBox;
break;
case 6: