| 1 | /* ScummVM - Graphic Adventure Engine |
| 2 | * |
| 3 | * ScummVM is the legal property of its developers, whose names |
| 4 | * are too numerous to list here. Please refer to the COPYRIGHT |
| 5 | * file distributed with this source distribution. |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU General Public License |
| 9 | * as published by the Free Software Foundation; either version 2 |
| 10 | * of the License, or (at your option) any later version. |
| 11 | |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with this program; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 20 | * |
| 21 | * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/trunk/engines/scumm/player_nes.h $ |
| 22 | * $Id: player_nes.h 27024 2007-05-30 21:56:52Z fingolfin $ |
| 23 | * |
| 24 | */ |
| 25 | |
| 26 | /* |
| 27 | * The PSG_HuC6280 class is based on the HuC6280 sound chip emulator |
| 28 | * by Charles MacDonald (E-mail: cgfm2@hotmail.com, WWW: http://cgfm2.emuviews.com) |
| 29 | * The implementation used here was taken from MESS (http://www.mess.org/) |
| 30 | * the Multiple Emulator Super System (sound/c6280.c). |
| 31 | * LFO and noise channel support have been removed (not used by Loom PCE). |
| 32 | */ |
| 33 | |
| 34 | #include <math.h> |
| 35 | #include "player_pce.h" |
| 36 | |
| 37 | namespace Scumm { |
| 38 | |
| 39 | // CPU and PSG use the same base clock but with a different divider |
| 40 | const double MASTER_CLOCK = 21477270.0; // ~21.48 MHz |
| 41 | const double CPU_CLOCK = MASTER_CLOCK / 3; // ~7.16 MHz |
| 42 | const double PSG_CLOCK = MASTER_CLOCK / 6; // ~3.58 MHz |
| 43 | const double TIMER_CLOCK = CPU_CLOCK / 1024; // ~6.9 kHz |
| 44 | |
| 45 | // The PSG update routine is originally triggered by the timer IRQ (not by VSYNC) |
| 46 | // approx. 120 times per second (TIML=0x39). But as just every second call is used |
| 47 | // to update the PSG we will call the update routine approx. 60 times per second. |
| 48 | const double UPDATE_FREQ = TIMER_CLOCK / (57 + 1) / 2; // ~60 Hz |
| 49 | |
| 50 | // $AFA5 |
| 51 | static const byte wave_table[7][32] = { |
| 52 | { // sine |
| 53 | 0x10, 0x19, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1E, 0x1D, 0x1C, 0x19, |
| 54 | 0x10, 0x05, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x05, |
| 55 | }, { // mw-shaped |
| 56 | 0x10, 0x1C, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, 0x1E, 0x1C, 0x1E, 0x1F, 0x1F, 0x1E, 0x1E, 0x1D, 0x1C, |
| 57 | 0x10, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, |
| 58 | }, { // square |
| 59 | 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, 0x1E, |
| 60 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 61 | }, { // triangle |
| 62 | 0x10, 0x0C, 0x08, 0x04, 0x01, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x1F, 0x1C, 0x18, 0x14, |
| 63 | 0x10, 0x0C, 0x08, 0x04, 0x01, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x1F, 0x1C, 0x18, 0x14, |
| 64 | }, { // saw-tooth |
| 65 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, |
| 66 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, |
| 67 | }, { // sigmoid |
| 68 | 0x07, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x1F, 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, |
| 69 | 0x08, 0x06, 0x05, 0x03, 0x02, 0x01, 0x00, 0x00, 0x0F, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x16, |
| 70 | }, { // MW-shaped |
| 71 | 0x1F, 0x1E, 0x1D, 0x1D, 0x1C, 0x1A, 0x17, 0x0F, 0x0F, 0x17, 0x1A, 0x1C, 0x1D, 0x1D, 0x1E, 0x1F, |
| 72 | 0x00, 0x01, 0x02, 0x02, 0x03, 0x05, 0x08, 0x0F, 0x0F, 0x08, 0x05, 0x03, 0x02, 0x02, 0x01, 0x00 |
| 73 | } |
| 74 | }; |
| 75 | |
| 76 | // AEBC |
| 77 | static const int control_offsets[14] = { |
| 78 | 0, 7, 20, 33, 46, 56, 75, 88, 116, 126, 136, 152, 165, 181 |
| 79 | }; |
| 80 | |
| 81 | // AED8 |
| 82 | static const byte control_data[205] = { |
| 83 | /* 0*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0xFF, |
| 84 | /* 7*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF0, 0x3A, 0x00, 0xFD, 0xFF, |
| 85 | /* 20*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x01, 0x00, 0xF0, 0x1D, 0x00, 0xF8, 0xFF, |
| 86 | /* 33*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x02, 0x00, 0xF0, 0x1E, 0x00, 0xFC, 0xFF, |
| 87 | /* 46*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x07, 0x00, 0xE0, 0xFF, |
| 88 | /* 56*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x01, 0x00, 0xD8, 0xF0, 0x00, 0xD8, 0x01, 0x00, 0x00, 0x04, 0x00, 0xF0, 0xFF, |
| 89 | /* 75*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x03, 0x00, 0xF8, 0x6E, 0x00, 0xFF, 0xFF, |
| 90 | /* 88*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x08, 0x00, 0xF0, 0xF0, 0x00, 0xD0, 0x01, 0x00, 0x00, 0x05, 0x00, 0xF0, 0xF0, 0x00, 0xB8, 0xE6, 0x80, 0xFF, 0xE6, 0x80, 0xFF, 0xFF, |
| 91 | /*116*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x05, 0x00, 0xD0, 0xFF, |
| 92 | /*126*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x04, 0x00, 0xF8, 0xFF, |
| 93 | /*136*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x03, 0x00, 0xF4, 0xE6, 0xC0, 0xFF, 0xE6, 0xC0, 0xFF, 0xFF, |
| 94 | /*152*/ 0xF0, 0x00, 0xD0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x10, 0x0E, 0x00, 0xFE, 0xFF, |
| 95 | /*165*/ 0xF0, 0x00, 0xA8, 0x01, 0x00, 0x00, 0x18, 0x00, 0x02, 0xE6, 0x80, 0xFE, 0xE6, 0xC0, 0xFF, 0xFF, |
| 96 | /*181*/ 0xF0, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x02, 0x00, 0xF8, 0x02, 0x00, 0x00, 0x02, 0x00, 0xF0, 0x01, 0x00, 0xF8, 0x02, 0x00, 0x08, 0xF1, 0x99, 0xAF |
| 97 | }; |
| 98 | |
| 99 | static const uint16 lookup_table[87] = { |
| 100 | 0x0D40, 0x0C80, 0x0BC0, 0x0B20, 0x0A80, 0x09E0, 0x0940, 0x08C0, |
| 101 | 0x0840, 0x07E0, 0x0760, 0x0700, 0x06A0, 0x0640, 0x05E0, 0x0590, |
| 102 | 0x0540, 0x04F0, 0x04A0, 0x0460, 0x0420, 0x03F0, 0x03B0, 0x0380, |
| 103 | 0x0350, 0x0320, 0x02F0, 0x02C8, 0x02A0, 0x0278, 0x0250, 0x0230, |
| 104 | 0x0210, 0x01F8, 0x01D8, 0x01C0, 0x01A8, 0x0190, 0x0178, 0x0164, |
| 105 | 0x0150, 0x013C, 0x0128, 0x0118, 0x0108, 0x00FC, 0x00EC, 0x00E0, |
| 106 | 0x00D4, 0x00C8, 0x00BC, 0x00B2, 0x00A8, 0x009E, 0x0094, 0x008C, |
| 107 | 0x0084, 0x007E, 0x0076, 0x0070, 0x006A, 0x0064, 0x005E, 0x0059, |
| 108 | 0x0054, 0x004F, 0x004A, 0x0046, 0x0042, 0x003F, 0x003B, 0x0038, |
| 109 | 0x0035, 0x0032, 0x0030, 0x002D, 0x002A, 0x0028, 0x0026, 0x0024, |
| 110 | 0x0022, 0x0020, 0x001E, 0x001C, 0x001B, 0x8E82, 0xB500 |
| 111 | }; |
| 112 | |
| 113 | // B27B |
| 114 | static const uint16 freq_offset[3] = { |
| 115 | 0, 2, 9 |
| 116 | }; |
| 117 | |
| 118 | static const uint16 freq_table[] = { |
| 119 | 0x0000, 0x0800, |
| 120 | 0xFFB0, 0xFFD1, 0xFFE8, 0xFFF1, 0x0005, 0x0000, 0x0800, |
| 121 | 0xFF9C, 0xFFD8, 0x0000, 0x000F, 0x0005, 0x0000, 0x0800 |
| 122 | }; |
| 123 | |
| 124 | static const int sound_table[13] = { |
| 125 | 0, 2, 3, 4, 5, 6, 7, 8, 9, 11, 1, 10, 11 |
| 126 | }; |
| 127 | |
| 128 | // 0xAE12 |
| 129 | // Note: |
| 130 | // - offsets relative to data_table |
| 131 | // - byte one of each sound was always 0x3F (= use all channels) -> removed from table |
| 132 | static const uint16 sounds[13][6] = { |
| 133 | { 481, 481, 481, 481, 481, 481 }, |
| 134 | { 395, 408, 467, 480, 480, 480 }, |
| 135 | { 85, 96, 109, 109, 109, 109 }, |
| 136 | { 110, 121, 134, 134, 134, 134 }, |
| 137 | { 135, 146, 159, 159, 159, 159 }, |
| 138 | { 160, 171, 184, 184, 184, 184 }, |
| 139 | { 185, 196, 209, 209, 209, 209 }, |
| 140 | { 210, 221, 234, 234, 234, 234 }, |
| 141 | { 235, 246, 259, 259, 259, 259 }, |
| 142 | { 260, 271, 284, 284, 284, 284 }, |
| 143 | { 285, 298, 311, 324, 335, 348 }, |
| 144 | { 349, 360, 361, 362, 373, 384 }, |
| 145 | { 0, 84, 84, 84, 84, 84 } // unused |
| 146 | }; |
| 147 | |
| 148 | // 0xB2A1 |
| 149 | static const byte data_table[482] = { |
| 150 | /* 0*/ 0xE2, 0x0A, 0xE1, 0x0D, 0xE6, 0xED, 0xE0, 0x0F, 0xE2, 0x00, 0xE1, 0x00, |
| 151 | 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2, |
| 152 | 0xB2, 0xE1, 0x03, 0xF2, 0xF2, 0xB2, 0xE1, 0x04, 0xF2, 0xF2, 0xB2, 0xE1, |
| 153 | 0x05, 0xF2, 0xF2, 0xB2, 0xE1, 0x06, 0xF2, 0xF2, 0xB2, 0xE1, 0x07, 0xF2, |
| 154 | 0xF2, 0xB2, 0xE1, 0x08, 0xF2, 0xF2, 0xB2, 0xE1, 0x09, 0xF2, 0xF2, 0xB2, |
| 155 | 0xE1, 0x0A, 0xF2, 0xF2, 0xB2, 0xE1, 0x0B, 0xF2, 0xF2, 0xB2, 0xE1, 0x0C, |
| 156 | 0xF2, 0xF2, 0xB2, 0xE1, 0x0D, 0xF2, 0xF2, 0xB2, 0xFF, 0xD1, 0x03, 0xF3, |
| 157 | /* 84*/ 0xFF, |
| 158 | |
| 159 | /* 85*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0x07, 0xFF, |
| 160 | /* 96*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x07, 0xFF, |
| 161 | /*109*/ 0xFF, |
| 162 | |
| 163 | /*110*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0x27, 0xFF, |
| 164 | /*121*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x27, 0xFF, |
| 165 | /*134*/ 0xFF, |
| 166 | |
| 167 | /*135*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0x47, 0xFF, |
| 168 | /*146*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x47, 0xFF, |
| 169 | /*159*/ 0xFF, |
| 170 | |
| 171 | /*160*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0x57, 0xFF, |
| 172 | /*171*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x57, 0xFF, |
| 173 | /*184*/ 0xFF, |
| 174 | |
| 175 | /*185*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0x77, 0xFF, |
| 176 | /*196*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x77, 0xFF, |
| 177 | /*209*/ 0xFF, |
| 178 | |
| 179 | /*210*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0x97, 0xFF, |
| 180 | /*221*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x97, 0xFF, |
| 181 | /*234*/ 0xFF, |
| 182 | |
| 183 | /*235*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD3, 0xB7, 0xFF, |
| 184 | /*246*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0xB7, 0xFF, |
| 185 | /*259*/ 0xFF, |
| 186 | |
| 187 | /*260*/ 0xE2, 0x0C, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD4, 0x07, 0xFF, |
| 188 | /*271*/ 0xE2, 0x06, 0xE1, 0x02, 0xE0, 0x0F, 0xE6, 0xED, 0xD5, 0xF0, 0x0C, 0x07, 0xFF, |
| 189 | /*284*/ 0xFF, |
| 190 | |
| 191 | /*285*/ 0xE2, 0x0B, 0xE1, 0x04, 0xE6, 0xED, 0xE0, 0x0A, 0xD0, 0xDB, 0x14, 0x0E, 0xFF, |
| 192 | /*298*/ 0xE2, 0x0B, 0xE1, 0x04, 0xE6, 0xED, 0xE0, 0x0A, 0xD0, 0xDB, 0x32, 0x1E, 0xFF, |
| 193 | /*311*/ 0xE2, 0x0B, 0xE1, 0x0B, 0xE6, 0xED, 0xE0, 0x0A, 0xD0, 0xDB, 0x50, 0x1E, 0xFF, |
| 194 | /*324*/ 0xE2, 0x0B, 0xE1, 0x0B, 0xE6, 0xED, 0xE0, 0x0A, 0xD0, 0x0E, 0xFF, |
| 195 | /*335*/ 0xE2, 0x0B, 0xE1, 0x02, 0xE6, 0xED, 0xE0, 0x0A, 0xD0, 0xDB, 0x0A, 0x0E, 0xFF, |
| 196 | /*348*/ 0xFF, |
| 197 | |
| 198 | /*349*/ 0xE2, 0x03, 0xE1, 0x01, 0xE6, 0xED, 0xE0, 0x06, 0xD6, 0x17, 0xFF, |
| 199 | /*360*/ 0xFF, |
| 200 | /*361*/ 0xFF, |
| 201 | /*362*/ 0xE2, 0x04, 0xE1, 0x04, 0xE6, 0xED, 0xE0, 0x06, 0xD5, 0xA7, 0xFF, |
| 202 | /*373*/ 0xE2, 0x03, 0xE1, 0x06, 0xE6, 0xED, 0xE0, 0x06, 0xD6, 0x37, 0xFF, |
| 203 | /*384*/ 0xE2, 0x04, 0xE1, 0x06, 0xE6, 0xED, 0xE0, 0x06, 0xD3, 0x87, 0xFF, |
| 204 | |
| 205 | /*395*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x0B, 0xE8, 0x0B, 0xFF, |
| 206 | /*408*/ 0xE2, 0x0C, 0xE1, 0x03, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x00, |
| 207 | 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, |
| 208 | 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, |
| 209 | 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, |
| 210 | 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF, |
| 211 | /*467*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x1B, 0xE8, 0x1B, 0xFF, |
| 212 | /*480*/ 0xFF, |
| 213 | |
| 214 | /*481*/ 0xFF |
| 215 | }; |
| 216 | |
| 217 | |
| 218 | /* |
| 219 | * PSG_HuC6280 |
| 220 | */ |
| 221 | |
| 222 | class PSG_HuC6280 { |
| 223 | private: |
| 224 | typedef struct { |
| 225 | uint16 frequency; |
| 226 | uint8 control; |
| 227 | uint8 balance; |
| 228 | uint8 waveform[32]; |
| 229 | uint8 index; |
| 230 | int16 dda; |
| 231 | uint32 counter; |
| 232 | } channel_t; |
| 233 | |
| 234 | double _clock; |
| 235 | double _rate; |
| 236 | uint8 _select; |
| 237 | uint8 _balance; |
| 238 | channel_t _channel[8]; |
| 239 | int16 _volumeTable[32]; |
| 240 | uint32 _noiseFreqTable[32]; |
| 241 | uint32 _waveFreqTable[4096]; |
| 242 | |
| 243 | public: |
| 244 | void init(); |
| 245 | void reset(); |
| 246 | void write(int offset, byte data); |
| 247 | void update(int16* samples, int sampleCnt); |
| 248 | |
| 249 | PSG_HuC6280(double clock, double samplerate); |
| 250 | }; |
| 251 | |
| 252 | PSG_HuC6280::PSG_HuC6280(double clock, double samplerate) { |
| 253 | _clock = clock; |
| 254 | _rate = samplerate; |
| 255 | |
| 256 | // Initialize PSG_HuC6280 emulator |
| 257 | init(); |
| 258 | } |
| 259 | |
| 260 | void PSG_HuC6280::init() { |
| 261 | int i; |
| 262 | double step; |
| 263 | |
| 264 | // Loudest volume level for table |
| 265 | double level = 65535.0 / 6.0 / 32.0; |
| 266 | |
| 267 | // Clear context |
| 268 | reset(); |
| 269 | |
| 270 | // Make waveform frequency table |
| 271 | for(i = 0; i < 4096; i++) { |
| 272 | step = ((_clock / _rate) * 4096) / (i+1); |
| 273 | _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step; |
| 274 | } |
| 275 | |
| 276 | // Make noise frequency table |
| 277 | for(i = 0; i < 32; i++) { |
| 278 | step = ((_clock / _rate) * 32) / (i+1); |
| 279 | _noiseFreqTable[i] = (uint32)step; |
| 280 | } |
| 281 | |
| 282 | // Make volume table |
| 283 | // PSG_HuC6280 has 48dB volume range spread over 32 steps |
| 284 | step = 48.0 / 32.0; |
| 285 | for(i = 0; i < 31; i++) { |
| 286 | _volumeTable[i] = (uint16)level; |
| 287 | level /= pow(10.0, step / 20.0); |
| 288 | } |
| 289 | _volumeTable[31] = 0; |
| 290 | } |
| 291 | |
| 292 | void PSG_HuC6280::reset() { |
| 293 | _select = 0; |
| 294 | _balance = 0xFF; |
| 295 | memset(_channel, 0, sizeof(_channel)); |
| 296 | memset(_volumeTable, 0, sizeof(_volumeTable)); |
| 297 | memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable)); |
| 298 | memset(_waveFreqTable, 0, sizeof(_waveFreqTable)); |
| 299 | } |
| 300 | |
| 301 | void PSG_HuC6280::write(int offset, byte data) { |
| 302 | channel_t *chan = &_channel[_select]; |
| 303 | |
| 304 | switch(offset & 0x0F) { |
| 305 | case 0x00: // Channel select |
| 306 | _select = data & 0x07; |
| 307 | break; |
| 308 | |
| 309 | case 0x01: // Global balance |
| 310 | _balance = data; |
| 311 | break; |
| 312 | |
| 313 | case 0x02: // Channel frequency (LSB) |
| 314 | chan->frequency = (chan->frequency & 0x0F00) | data; |
| 315 | chan->frequency &= 0x0FFF; |
| 316 | break; |
| 317 | |
| 318 | case 0x03: // Channel frequency (MSB) |
| 319 | chan->frequency = (chan->frequency & 0x00FF) | (data << 8); |
| 320 | chan->frequency &= 0x0FFF; |
| 321 | break; |
| 322 | |
| 323 | case 0x04: // Channel control (key-on, DDA mode, volume) |
| 324 | // 1-to-0 transition of DDA bit resets waveform index |
| 325 | if((chan->control & 0x40) && ((data & 0x40) == 0)) { |
| 326 | chan->index = 0; |
| 327 | } |
| 328 | chan->control = data; |
| 329 | break; |
| 330 | |
| 331 | case 0x05: // Channel balance |
| 332 | chan->balance = data; |
| 333 | break; |
| 334 | |
| 335 | case 0x06: // Channel waveform data |
| 336 | switch(chan->control & 0xC0) { |
| 337 | case 0x00: |
| 338 | chan->waveform[chan->index & 0x1F] = data & 0x1F; |
| 339 | chan->index = (chan->index + 1) & 0x1F; |
| 340 | break; |
| 341 | |
| 342 | case 0x40: |
| 343 | break; |
| 344 | |
| 345 | case 0x80: |
| 346 | chan->waveform[chan->index & 0x1F] = data & 0x1F; |
| 347 | chan->index = (chan->index + 1) & 0x1F; |
| 348 | break; |
| 349 | |
| 350 | case 0xC0: |
| 351 | chan->dda = data & 0x1F; |
| 352 | break; |
| 353 | } |
| 354 | |
| 355 | break; |
| 356 | |
| 357 | case 0x07: // Noise control (enable, frequency) |
| 358 | case 0x08: // LFO frequency |
| 359 | case 0x09: // LFO control (enable, mode) |
| 360 | break; |
| 361 | |
| 362 | default: |
| 363 | break; |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | void PSG_HuC6280::update(int16* samples, int sampleCnt) { |
| 368 | static const int scale_tab[] = { |
| 369 | 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, |
| 370 | 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F |
| 371 | }; |
| 372 | int ch; |
| 373 | int i; |
| 374 | |
| 375 | int lmal = (_balance >> 4) & 0x0F; |
| 376 | int rmal = (_balance >> 0) & 0x0F; |
| 377 | int vll, vlr; |
| 378 | |
| 379 | lmal = scale_tab[lmal]; |
| 380 | rmal = scale_tab[rmal]; |
| 381 | |
| 382 | // Clear buffer |
| 383 | memset(samples, 0, 2 * sampleCnt * sizeof(int16)); |
| 384 | |
| 385 | for(ch = 0; ch < 6; ch++) { |
| 386 | // Only look at enabled channels |
| 387 | if(_channel[ch].control & 0x80) { |
| 388 | int lal = (_channel[ch].balance >> 4) & 0x0F; |
| 389 | int ral = (_channel[ch].balance >> 0) & 0x0F; |
| 390 | int al = _channel[ch].control & 0x1F; |
| 391 | |
| 392 | lal = scale_tab[lal]; |
| 393 | ral = scale_tab[ral]; |
| 394 | |
| 395 | // Calculate volume just as the patent says |
| 396 | vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal); |
| 397 | if(vll > 0x1F) vll = 0x1F; |
| 398 | |
| 399 | vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal); |
| 400 | if(vlr > 0x1F) vlr = 0x1F; |
| 401 | |
| 402 | vll = _volumeTable[vll]; |
| 403 | vlr = _volumeTable[vlr]; |
| 404 | |
| 405 | // Check channel mode |
| 406 | if(_channel[ch].control & 0x40) { |
| 407 | /* DDA mode */ |
| 408 | for(i = 0; i < sampleCnt; i++) { |
| 409 | samples[2*i] += (int16)(vll * (_channel[ch].dda - 16)); |
| 410 | samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16)); |
| 411 | } |
| 412 | } else { |
| 413 | /* Waveform mode */ |
| 414 | uint32 step = _waveFreqTable[_channel[ch].frequency]; |
| 415 | for(i = 0; i < sampleCnt; i += 1) { |
| 416 | int offset; |
| 417 | int16 data; |
| 418 | offset = (_channel[ch].counter >> 12) & 0x1F; |
| 419 | _channel[ch].counter += step; |
| 420 | _channel[ch].counter &= 0x1FFFF; |
| 421 | data = _channel[ch].waveform[offset]; |
| 422 | samples[2*i] += (int16)(vll * (data - 16)); |
| 423 | samples[2*i + 1] += (int16)(vlr * (data - 16)); |
| 424 | } |
| 425 | } |
| 426 | } |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | |
| 431 | /* |
| 432 | * Player_PCE |
| 433 | */ |
| 434 | |
| 435 | void Player_PCE::PSG_Write(int reg, byte data) { |
| 436 | _psg->write(reg, data); |
| 437 | } |
| 438 | |
| 439 | void Player_PCE::setupWaveform(byte bank) { |
| 440 | const byte *ptr = wave_table[bank]; |
| 441 | PSG_Write(4, 0x40); |
| 442 | PSG_Write(4, 0x00); |
| 443 | for (int i = 0; i < 32; ++i) { |
| 444 | PSG_Write(6, ptr[i]); |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | // A541 |
| 449 | void Player_PCE::procA541(channel_t *channel) { |
| 450 | channel->soundDataPtr = NULL; |
| 451 | channel->controlVecShort10 = 0; |
| 452 | |
| 453 | channel->controlVecShort03 = 0; |
| 454 | channel->controlVecShort06 = 0; |
| 455 | channel->controlVec8 = 0; |
| 456 | channel->controlVec9 = 0; |
| 457 | channel->controlVec10 = 0; |
| 458 | channel->soundUpdateCounter = 0; |
| 459 | channel->controlVec18 = 0; |
| 460 | channel->controlVec19 = 0; |
| 461 | channel->controlVec23 = false; |
| 462 | channel->controlVec24 = false; |
| 463 | channel->controlVec21 = 0; |
| 464 | |
| 465 | channel->waveformCtrl = 0x80; |
| 466 | } |
| 467 | |
| 468 | // A592 |
| 469 | void Player_PCE::startSound(int sound) { |
| 470 | channel_t *channel; |
| 471 | const uint16 *ptr = sounds[sound_table[sound]]; |
| 472 | |
| 473 | for (int i = 0; i < 6; ++i) { |
| 474 | channel = &channels[i]; |
| 475 | procA541(channel); |
| 476 | |
| 477 | channel->controlVec24 = true; |
| 478 | channel->waveformCtrl = 0; |
| 479 | channel->controlVec0 = 0; |
| 480 | channel->controlVec19 = 0; |
| 481 | channel->controlVec18 = 0; |
| 482 | channel->soundDataPtr = &data_table[*ptr++]; |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | // A64B |
| 487 | void Player_PCE::updateSound() { |
| 488 | for (int i = 0; i < 12; i++) { |
| 489 | channel_t *channel = &channels[i]; |
| 490 | bool cond = true; |
| 491 | if (i < 6) { |
| 492 | channel->controlVec21 ^= 0xFF; |
| 493 | if (!channel->controlVec21) |
| 494 | cond = false; |
| 495 | } |
| 496 | if (cond) { |
| 497 | processSoundData(channel); |
| 498 | procAB7F(channel); |
| 499 | procAC24(channel); |
| 500 | channel->controlVec11 = (channel->controlVecShort10 >> 11) | 0x80; |
| 501 | channel->balance = channel->balance2; |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | for (int i = 0; i < 6; ++i) { |
| 506 | procA731(&channels[i]); |
| 507 | } |
| 508 | } |
| 509 | |
| 510 | int Player_PCE::readBuffer(int16 *buffer, const int numSamples) { |
| 511 | int sampleCopyCnt; |
| 512 | int samplesLeft = numSamples; |
| 513 | |
| 514 | _mutex.lock(); |
| 515 | |
| 516 | while (true) { |
| 517 | // copy samples to output buffer |
| 518 | sampleCopyCnt = (samplesLeft < _sampleBufferCnt) ? samplesLeft : _sampleBufferCnt; |
| 519 | if (sampleCopyCnt > 0) { |
| 520 | memcpy(buffer, _sampleBuffer, sampleCopyCnt * sizeof(int16)); |
| 521 | buffer += sampleCopyCnt; |
| 522 | samplesLeft -= sampleCopyCnt; |
| 523 | _sampleBufferCnt -= sampleCopyCnt; |
| 524 | } |
| 525 | |
| 526 | if (samplesLeft == 0) |
| 527 | break; |
| 528 | |
| 529 | // retrieve samples for one timer period |
| 530 | updateSound(); |
| 531 | _psg->update(_sampleBuffer, _samplesPerPeriod / 2); |
| 532 | _sampleBufferCnt = _samplesPerPeriod; |
| 533 | } |
| 534 | |
| 535 | // copy remaining samples to the front of the buffer |
| 536 | if (_sampleBufferCnt > 0) { |
| 537 | memmove(&_sampleBuffer[0], |
| 538 | &_sampleBuffer[_samplesPerPeriod - _sampleBufferCnt], |
| 539 | _sampleBufferCnt * sizeof(int16)); |
| 540 | } |
| 541 | |
| 542 | _mutex.unlock(); |
| 543 | |
| 544 | return numSamples; |
| 545 | } |
| 546 | |
| 547 | void Player_PCE::procA731(channel_t *channel) { |
| 548 | PSG_Write(0, channel->id); |
| 549 | PSG_Write(2, channel->freq & 0xFF); |
| 550 | PSG_Write(3, (channel->freq >> 8) & 0xFF); |
| 551 | |
| 552 | int tmp = channel->controlVec11; |
| 553 | if ((channel->controlVec11 & 0xC0) == 0x80) { |
| 554 | tmp = channel->controlVec11 & 0x1F; |
| 555 | if (tmp != 0) { |
| 556 | tmp -= channel->controlVec0; |
| 557 | if (tmp >= 0) { |
| 558 | tmp |= 0x80; |
| 559 | } else { |
| 560 | tmp = 0; |
| 561 | } |
| 562 | } |
| 563 | } |
| 564 | |
| 565 | PSG_Write(5, channel->balance); |
| 566 | if ((channel->waveformCtrl & 0x80) == 0) { |
| 567 | channel->waveformCtrl |= 0x80; |
| 568 | PSG_Write(0, channel->id); |
| 569 | setupWaveform(channel->waveformCtrl & 0x7F); |
| 570 | } |
| 571 | |
| 572 | PSG_Write(4, tmp); |
| 573 | } |
| 574 | |
| 575 | // A793 |
| 576 | void Player_PCE::processSoundData(channel_t *channel) { |
| 577 | channel->soundUpdateCounter--; |
| 578 | if (channel->soundUpdateCounter > 0) { |
| 579 | return; |
| 580 | } |
| 581 | |
| 582 | while (true) { |
| 583 | const byte *ptr = channel->soundDataPtr; |
| 584 | byte value = (ptr ? *ptr++ : 0xFF); |
| 585 | if (value < 0xD0) { |
| 586 | int mult = (value & 0x0F) + 1; |
| 587 | channel->soundUpdateCounter = mult * channel->controlVec1; |
| 588 | value >>= 4; |
| 589 | procAA62(channel, value); |
| 590 | channel->soundDataPtr = ptr; |
| 591 | return; |
| 592 | } |
| 593 | |
| 594 | // jump_table (A7F7) |
| 595 | switch (value - 0xD0) { |
| 596 | case 0: /*A85A*/ |
| 597 | case 1: /*A85D*/ |
| 598 | case 2: /*A861*/ |
| 599 | case 3: /*A865*/ |
| 600 | case 4: /*A869*/ |
| 601 | case 5: /*A86D*/ |
| 602 | case 6: /*A871*/ |
| 603 | channel->controlVec2 = (value - 0xD0) * 12; |
| 604 | break; |
| 605 | case 11: /*A8A8*/ |
| 606 | channel->controlVecShort06 = (int8)*ptr++; |
| 607 | break; |
| 608 | case 16: /*A8C2*/ |
| 609 | channel->controlVec1 = *ptr++; |
| 610 | break; |
| 611 | case 17: /*A8CA*/ |
| 612 | channel->waveformCtrl = *ptr++; |
| 613 | break; |
| 614 | case 18: /*A8D2*/ |
| 615 | channel->controlVec10 = *ptr++; |
| 616 | break; |
| 617 | case 22: /*A8F2*/ |
| 618 | value = *ptr; |
| 619 | channel->balance = value; |
| 620 | channel->balance2 = value; |
| 621 | ptr++; |
| 622 | break; |
| 623 | case 24: /*A905*/ |
| 624 | channel->controlVec23 = true; |
| 625 | break; |
| 626 | case 32: /*A921*/ |
| 627 | *ptr++; |
| 628 | break; |
| 629 | case 47: |
| 630 | channel->controlVec24 = false; |
| 631 | channel->controlVec10 &= 0x7F; |
| 632 | channel->controlVecShort10 &= 0x00FF; |
| 633 | return; |
| 634 | default: |
| 635 | // unused -> ignore |
| 636 | break; |
| 637 | } |
| 638 | |
| 639 | channel->soundDataPtr = ptr; |
| 640 | } |
| 641 | } |
| 642 | |
| 643 | void Player_PCE::procAA62(channel_t *channel, int a) { |
| 644 | procACEA(channel, a); |
| 645 | if (channel->controlVec23) { |
| 646 | channel->controlVec23 = false; |
| 647 | return; |
| 648 | } |
| 649 | |
| 650 | channel->controlVec18 = 0; |
| 651 | |
| 652 | channel->controlVec10 |= 0x80; |
| 653 | int y = channel->controlVec10 & 0x7F; |
| 654 | channel->controlBufferPos = &control_data[control_offsets[y]]; |
| 655 | channel->controlVec5 = 0; |
| 656 | } |
| 657 | |
| 658 | void Player_PCE::procAB7F(channel_t *channel) { |
| 659 | uint16 freqValue = channel->controlVecShort02; |
| 660 | channel->controlVecShort02 += channel->controlVecShort03; |
| 661 | |
| 662 | int pos = freq_offset[channel->controlVec19] + channel->controlVec18; |
| 663 | freqValue += freq_table[pos]; |
| 664 | if (freq_table[pos + 1] != 0x0800) { |
| 665 | channel->controlVec18++; |
| 666 | } |
| 667 | freqValue += channel->controlVecShort06; |
| 668 | |
| 669 | channel->freq = freqValue; |
| 670 | } |
| 671 | |
| 672 | void Player_PCE::procAC24(channel_t *channel) { |
| 673 | if ((channel->controlVec10 & 0x80) == 0) |
| 674 | return; |
| 675 | |
| 676 | if (channel->controlVec5 == 0) { |
| 677 | const byte *ctrlPtr = channel->controlBufferPos; |
| 678 | byte value = *ctrlPtr++; |
| 679 | while (value >= 0xF0) { |
| 680 | if (value == 0xF0) { |
| 681 | channel->controlVecShort10 = READ_LE_UINT16(ctrlPtr); |
| 682 | ctrlPtr += 2; |
| 683 | } else if (value == 0xFF) { |
| 684 | channel->controlVec10 &= 0x7F; |
| 685 | return; |
| 686 | } else { |
| 687 | // unused |
| 688 | } |
| 689 | value = *ctrlPtr++; |
| 690 | } |
| 691 | channel->controlVec5 = value; |
| 692 | channel->controlVecShort09 = READ_LE_UINT16(ctrlPtr); |
| 693 | ctrlPtr += 2; |
| 694 | channel->controlBufferPos = ctrlPtr; |
| 695 | } |
| 696 | |
| 697 | channel->controlVecShort10 += channel->controlVecShort09; |
| 698 | channel->controlVec5--; |
| 699 | } |
| 700 | |
| 701 | void Player_PCE::procACEA(channel_t *channel, int a) { |
| 702 | int x = a + |
| 703 | channel->controlVec2 + |
| 704 | channel->controlVec8 + |
| 705 | channel->controlVec9; |
| 706 | channel->controlVecShort02 = lookup_table[x]; |
| 707 | } |
| 708 | |
| 709 | Player_PCE::Player_PCE(ScummEngine *scumm, Audio::Mixer *mixer) { |
| 710 | for (int i = 0; i < 12; ++i) { |
| 711 | memset(&channels[i], 0, sizeof(channel_t)); |
| 712 | channels[i].id = i; |
| 713 | } |
| 714 | |
| 715 | _mixer = mixer; |
| 716 | _sample_rate = _mixer->getOutputRate(); |
| 717 | _vm = scumm; |
| 718 | |
| 719 | _samplesPerPeriod = 2 * (int)(_sample_rate / UPDATE_FREQ); |
| 720 | _sampleBuffer = new int16[_samplesPerPeriod]; |
| 721 | _sampleBufferCnt = 0; |
| 722 | |
| 723 | _psg = new PSG_HuC6280(PSG_CLOCK, _sample_rate); |
| 724 | |
| 725 | _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); |
| 726 | } |
| 727 | |
| 728 | Player_PCE::~Player_PCE() { |
| 729 | _mixer->stopHandle(_soundHandle); |
| 730 | delete[] _sampleBuffer; |
| 731 | delete _psg; |
| 732 | } |
| 733 | |
| 734 | void Player_PCE::stopSound(int nr) { |
| 735 | // TODO: implement |
| 736 | } |
| 737 | |
| 738 | void Player_PCE::stopAllSounds() { |
| 739 | // TODO: implement |
| 740 | } |
| 741 | |
| 742 | int Player_PCE::getSoundStatus(int nr) const { |
| 743 | // TODO: status for each sound |
| 744 | for (int i = 0; i < 6; ++i) { |
| 745 | if (channels[i].controlVec24) |
| 746 | return 1; |
| 747 | } |
| 748 | return 0; |
| 749 | } |
| 750 | |
| 751 | int Player_PCE::getMusicTimer() const { |
| 752 | return 0; |
| 753 | } |
| 754 | |
| 755 | } // End of namespace Scumm |