/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "engines/engine.h" #include "scumm/player_v2.h" #include "scumm/scumm.h" #include "sound/mididrv.h" #include "sound/mixer.h" namespace Scumm { #define FREQ_HZ 236 // Don't change! #define FIXP_SHIFT 16 #define MAX_OUTPUT 0x7fff #define NG_PRESET 0x0f35 /* noise generator preset */ #define FB_WNOISE 0x12000 /* feedback for white noise */ #define FB_PNOISE 0x08000 /* feedback for periodic noise */ // CMS/Gameblaster Emulation taken from DosBox #define LEFT 0x00 #define RIGHT 0x01 #define MAX_OUTPUT 0x7fff #define MIN_OUTPUT -0x8000 //#define CMS_BUFFER_SIZE 128 #define CMS_RATE 22050 #define PROCESS_ATTACK 1 #define PROCESS_RELEASE 2 #define PROCESS_SUSTAIN 3 #define PROCESS_DECAY 4 #define PROCESS_VIBRATO 5 /* this structure defines a channel */ struct saa1099_channel { int frequency; /* frequency (0x00..0xff) */ int freq_enable; /* frequency enable */ int noise_enable; /* noise enable */ int octave; /* octave (0x00..0x07) */ int amplitude[2]; /* amplitude (0x00..0x0f) */ int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ /* vars to simulate the square wave */ double counter; double freq; int level; }; /* this structure defines a noise channel */ struct saa1099_noise { /* vars to simulate the noise generator output */ double counter; double freq; int level; /* noise polynomal shifter */ }; /* this structure defines a SAA1099 chip */ struct SAA1099 { int stream; /* our stream */ int noise_params[2]; /* noise generators parameters */ int env_enable[2]; /* envelope generators enable */ int env_reverse_right[2]; /* envelope reversed for right channel */ int env_mode[2]; /* envelope generators mode */ int env_bits[2]; /* non zero = 3 bits resolution */ int env_clock[2]; /* envelope clock mode (non-zero external) */ int env_step[2]; /* current envelope step */ int all_ch_enable; /* all channels enable */ int sync_state; /* sync all channels */ int selected_reg; /* selected register */ struct saa1099_channel channels[6]; /* channels */ struct saa1099_noise noise[2]; /* noise generators */ }; static byte envelope[8][64] = { /* zero amplitude */ { 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* maximum amplitude */ {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, /* single decay */ {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 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 }, /* repetitive decay */ {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, /* single triangular */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 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 }, /* repetitive triangular */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, /* single attack */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 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 }, /* repetitive attack */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } }; static int amplitude_lookup[16] = { 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 }; class CMSEmulator { public: CMSEmulator(uint32 sampleRate) { _sampleRate = sampleRate; memset(_saa1099, 0, sizeof(SAA1099)*2); } ~CMSEmulator() { } void portWrite(int port, int val); void readBuffer(int16 *buffer, const int numSamples); private: uint32 _sampleRate; SAA1099 _saa1099[2]; void envelope(int chip, int ch); void update(int chip, int16 *buffer, int length); void portWriteIntern(int chip, int offset, int data); }; void CMSEmulator::portWrite(int port, int val) { switch (port) { case 0x220: portWriteIntern(0, 1, val); break; case 0x221: _saa1099[0].selected_reg = val & 0x1f; if (_saa1099[0].selected_reg == 0x18 || _saa1099[0].selected_reg == 0x19) { /* clock the envelope channels */ if (_saa1099[0].env_clock[0]) envelope(0, 0); if (_saa1099[0].env_clock[1]) envelope(0, 1); } break; case 0x222: portWriteIntern(1, 1, val); break; case 0x223: _saa1099[1].selected_reg = val & 0x1f; if (_saa1099[1].selected_reg == 0x18 || _saa1099[1].selected_reg == 0x19) { /* clock the envelope channels */ if (_saa1099[1].env_clock[0]) envelope(1, 0); if (_saa1099[1].env_clock[1]) envelope(1, 1); } break; default: warning("CMSEmulator got port: 0x%X", port); break; } } void CMSEmulator::readBuffer(int16 *buffer, const int numSamples) { update(0, &buffer[0], numSamples); update(1, &buffer[0], numSamples); } void CMSEmulator::envelope(int chip, int ch) { SAA1099 *saa = &_saa1099[chip]; if (saa->env_enable[ch]) { int step, mode, mask; mode = saa->env_mode[ch]; /* step from 0..63 and then loop in steps 32..63 */ step = saa->env_step[ch] = ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); mask = 15; if (saa->env_bits[ch]) mask &= ~1; /* 3 bit resolution, mask LSB */ saa->channels[ch*3+0].envelope[ LEFT] = saa->channels[ch*3+1].envelope[ LEFT] = saa->channels[ch*3+2].envelope[ LEFT] = Scumm::envelope[mode][step] & mask; if (saa->env_reverse_right[ch] & 0x01) { saa->channels[ch*3+0].envelope[RIGHT] = saa->channels[ch*3+1].envelope[RIGHT] = saa->channels[ch*3+2].envelope[RIGHT] = (15 - Scumm::envelope[mode][step]) & mask; } else { saa->channels[ch*3+0].envelope[RIGHT] = saa->channels[ch*3+1].envelope[RIGHT] = saa->channels[ch*3+2].envelope[RIGHT] = Scumm::envelope[mode][step] & mask; } } else { /* envelope mode off, set all envelope factors to 16 */ saa->channels[ch*3+0].envelope[ LEFT] = saa->channels[ch*3+1].envelope[ LEFT] = saa->channels[ch*3+2].envelope[ LEFT] = saa->channels[ch*3+0].envelope[RIGHT] = saa->channels[ch*3+1].envelope[RIGHT] = saa->channels[ch*3+2].envelope[RIGHT] = 16; } } void CMSEmulator::update(int chip, int16 *buffer, int length) { struct SAA1099 *saa = &_saa1099[chip]; int j, ch; /* if the channels are disabled we're done */ if (!saa->all_ch_enable) { /* init output data */ if (chip == 0) { memset(buffer, 0, sizeof(int16)*length*2); } return; } if (chip == 0) { memset(buffer, 0, sizeof(int16)*length*2); } for (ch = 0; ch < 2; ch++) { switch (saa->noise_params[ch]) { case 0: saa->noise[ch].freq = 31250.0 * 2; break; case 1: saa->noise[ch].freq = 15625.0 * 2; break; case 2: saa->noise[ch].freq = 7812.5 * 2; break; case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; } } /* fill all data needed */ for (j = 0; j < length; ++j) { int output_l = 0, output_r = 0; /* for each channel */ for (ch = 0; ch < 6; ch++) { if (saa->channels[ch].freq == 0.0) saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / (511.0 - (double)saa->channels[ch].frequency); /* check the actual position in the square wave */ saa->channels[ch].counter -= saa->channels[ch].freq; while (saa->channels[ch].counter < 0) { /* calculate new frequency now after the half wave is updated */ saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / (511.0 - (double)saa->channels[ch].frequency); saa->channels[ch].counter += _sampleRate; saa->channels[ch].level ^= 1; /* eventually clock the envelope counters */ if (ch == 1 && saa->env_clock[0] == 0) envelope(chip, 0); if (ch == 4 && saa->env_clock[1] == 0) envelope(chip, 1); } /* if the noise is enabled */ if (saa->channels[ch].noise_enable) { /* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */ if (saa->noise[ch/3].level & 1) { /* subtract to avoid overflows, also use only half amplitude */ output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2; output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2; } } /* if the square wave is enabled */ if (saa->channels[ch].freq_enable) { /* if the channel level is high */ if (saa->channels[ch].level & 1) { output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; } } } for (ch = 0; ch < 2; ch++) { /* check the actual position in noise generator */ saa->noise[ch].counter -= saa->noise[ch].freq; while (saa->noise[ch].counter < 0) { saa->noise[ch].counter += _sampleRate; if( ((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; else saa->noise[ch].level <<= 1; } } /* write sound data to the buffer */ buffer[j*2] += output_l / 6; buffer[j*2+1] += output_r / 6; } } void CMSEmulator::portWriteIntern(int chip, int offset, int data) { SAA1099 *saa = &_saa1099[chip]; int reg = saa->selected_reg; int ch; switch (reg) { /* channel i amplitude */ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: ch = reg & 7; saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; break; /* channel i frequency */ case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: ch = reg & 7; saa->channels[ch].frequency = data & 0xff; break; /* channel i octave */ case 0x10: case 0x11: case 0x12: ch = (reg - 0x10) << 1; saa->channels[ch + 0].octave = data & 0x07; saa->channels[ch + 1].octave = (data >> 4) & 0x07; break; /* channel i frequency enable */ case 0x14: saa->channels[0].freq_enable = data & 0x01; saa->channels[1].freq_enable = data & 0x02; saa->channels[2].freq_enable = data & 0x04; saa->channels[3].freq_enable = data & 0x08; saa->channels[4].freq_enable = data & 0x10; saa->channels[5].freq_enable = data & 0x20; break; /* channel i noise enable */ case 0x15: saa->channels[0].noise_enable = data & 0x01; saa->channels[1].noise_enable = data & 0x02; saa->channels[2].noise_enable = data & 0x04; saa->channels[3].noise_enable = data & 0x08; saa->channels[4].noise_enable = data & 0x10; saa->channels[5].noise_enable = data & 0x20; break; /* noise generators parameters */ case 0x16: saa->noise_params[0] = data & 0x03; saa->noise_params[1] = (data >> 4) & 0x03; break; /* envelope generators parameters */ case 0x18: case 0x19: ch = reg - 0x18; saa->env_reverse_right[ch] = data & 0x01; saa->env_mode[ch] = (data >> 1) & 0x07; saa->env_bits[ch] = data & 0x10; saa->env_clock[ch] = data & 0x20; saa->env_enable[ch] = data & 0x80; /* reset the envelope */ saa->env_step[ch] = 0; break; /* channels enable & reset generators */ case 0x1c: saa->all_ch_enable = data & 0x01; saa->sync_state = data & 0x02; if (data & 0x02) { int i; /* Synch & Reset generators */ for (i = 0; i < 6; i++) { saa->channels[i].level = 0; saa->channels[i].counter = 0.0; } } break; default: /* Error! */ error("CMS Unkown write to reg %x with %x",reg, data); } } #pragma mark - #pragma mark - Player_V2CMS #pragma mark - const uint8 note_lengths[] = { 0, 0, 0, 2, 0, 3, 4, 5, 6, 8, 9, 12, 16, 18, 24, 32, 36, 48, 64, 72, 96 }; static const uint16 hull_offsets[] = { 0, 12, 24, 36, 48, 60, 72, 88, 104, 120, 136, 256, 152, 164, 180 }; static const int16 hulls[] = { // hull 0 3, -1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, // hull 1 (staccato) 3, -1, 0, 32, 0, -1, 0, 0, 0, -1, 0, 0, // hull 2 (legato) 3, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // hull 3 (staccatissimo) 3, -1, 0, 2, 0, -1, 0, 0, 0, -1, 0, 0, // hull 4 3, -1, 0, 6, 0, -1, 0, 0, 0, -1, 0, 0, // hull 5 3, -1, 0, 16, 0, -1, 0, 0, 0, -1, 0, 0, // hull 6 (int16) 60000, -1, -1000, 20, 0, 0, 0, 0, (int16) 40000, -1, -5000, 5, 0, -1, 0, 0, // hull 7 (int16) 50000, -1, 0, 8, 30000, -1, 0, 0, 28000, -1, -5000, 5, 0, -1, 0, 0, // hull 8 (int16) 60000, -1, -2000, 16, 0, 0, 0, 0, 28000, -1, -6000, 5, 0, -1, 0, 0, // hull 9 (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0, (int16) 40000, -1, -2000, 10, 0, -1, 0, 0, // hull 10 (int16) 60000, -1, 0, 4, -2000, 8, 0, 0, (int16) 40000, -1, -6000, 5, 0, -1, 0, 0, // hull 12 0, -1, 150, 340, -150, 340, 0, -1, 0, -1, 0, 0, // hull 13 == 164 20000, -1, 4000, 7, 1000, 15, 0, 0, (int16) 35000, -1, -2000, 15, 0, -1, 0, 0, // hull 14 == 180 (int16) 35000, -1, 500, 20, 0, 0, 0, 0, (int16) 45000, -1, -500, 60, 0, -1, 0, 0, // hull misc = 196 (int16) 44000, -1, -4400, 10, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 53000, -1, -5300, 10, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 63000, -1, -6300, 10, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 44000, -1, -1375, 32, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 53000, -1, -1656, 32, 0, -1, 0, 0, 0, -1, 0, 0, // hull 11 == 256 (int16) 63000, -1, -1968, 32, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 44000, -1, - 733, 60, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 53000, -1, - 883, 60, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 63000, -1, -1050, 60, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 44000, -1, - 488, 90, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 53000, -1, - 588, 90, 0, -1, 0, 0, 0, -1, 0, 0, (int16) 63000, -1, - 700, 90, 0, -1, 0, 0, 0, -1, 0, 0 }; static const uint16 freqmod_lengths[] = { 0x1000, 0x1000, 0x20, 0x2000, 0x1000 }; static const uint16 freqmod_offsets[] = { 0, 0x100, 0x200, 0x302, 0x202 }; static const int8 freqmod_table[0x502] = { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 59, 62, 65, 67, 70, 73, 75, 78, 80, 82, 85, 87, 89, 91, 94, 96, 98, 100, 102, 103, 105, 107, 108, 110, 112, 113, 114, 116, 117, 118, 119, 120, 121, 122, 123, 123, 124, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 124, 123, 123, 122, 121, 120, 119, 118, 117, 116, 114, 113, 112, 110, 108, 107, 105, 103, 102, 100, 98, 96, 94, 91, 89, 87, 85, 82, 80, 78, 75, 73, 70, 67, 65, 62, 59, 57, 54, 51, 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0, -3, -6, -9, -12, -15, -18, -21, -24, -27, -30, -33, -36, -39, -42, -45, -48, -51, -54, -57, -59, -62, -65, -67, -70, -73, -75, -78, -80, -82, -85, -87, -89, -91, -94, -96, -98,-100,-102,-103, -105,-107,-108,-110,-112,-113,-114,-116, -117,-118,-119,-120,-121,-122,-123,-123, -124,-125,-125,-126,-126,-126,-126,-126, -126,-126,-126,-126,-126,-126,-125,-125, -124,-123,-123,-122,-121,-120,-119,-118, -117,-116,-114,-113,-112,-110,-108,-107, -105,-103,-102,-100, -98, -96, -94, -91, -89, -87, -85, -82, -80, -78, -75, -73, -70, -67, -65, -62, -59, -57, -54, -51, -48, -45, -42, -39, -36, -33, -30, -27, -24, -21, -18, -15, -12, -9, -6, -3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, -128,-127,-126,-125,-124,-123,-122,-121, -120,-119,-118,-117,-116,-115,-114,-113, -112,-111,-110,-109,-108,-107,-106,-105, -104,-103,-102,-101,-100, -99, -98, -97, -96, -95, -94, -93, -92, -91, -90, -89, -88, -87, -86, -85, -84, -83, -82, -81, -80, -79, -78, -77, -76, -75, -74, -73, -72, -71, -70, -69, -68, -67, -66, -65, -64, -63, -62, -61, -60, -59, -58, -57, -56, -55, -54, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, -120, 120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, -120,-120,-120,-120,-120,-120,-120,-120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 41, 35, -66,-124, -31, 108, -42, -82, 82,-112, 73, -15, -15, -69, -23, -21, -77, -90, -37, 60,-121, 12, 62,-103, 36, 94, 13, 28, 6, -73, 71, -34, -77, 18, 77, -56, 67, -69,-117, -90, 31, 3, 90, 125, 9, 56, 37, 31, 93, -44, -53, -4,-106, -11, 69, 59, 19, 13,-119, 10, 28, -37, -82, 50, 32,-102, 80, -18, 64, 120, 54, -3, 18, 73, 50, -10, -98, 125, 73, -36, -83, 79, 20, -14, 68, 64, 102, -48, 107, -60, 48, -73, 50, 59, -95, 34, -10, 34,-111, -99, -31,-117, 31, -38, -80, -54,-103, 2, -71, 114, -99, 73, 44,-128, 126, -59,-103, -43, -23,-128, -78, -22, -55, -52, 83, -65, 103, -42, -65, 20, -42, 126, 45, -36,-114, 102, -125, -17, 87, 73, 97, -1, 105,-113, 97, -51, -47, 30, -99,-100, 22, 114, 114, -26, 29, -16,-124, 79, 74, 119, 2, -41, -24, 57, 44, 83, -53, -55, 18, 30, 51, 116, -98, 12, -12, -43, -44, -97, -44, -92, 89, 126, 53, -49, 50, 34, -12, -52, -49, -45,-112, 45, 72, -45,-113, 117, -26, -39, 29, 42, -27, -64, -9, 43, 120,-127,-121, 68, 14, 95, 80, 0, -44, 97,-115, -66, 123, 5, 21, 7, 59, 51,-126, 31, 24, 112,-110, -38, 100, 84, -50, -79, -123, 62, 105, 21, -8, 70, 106, 4, -106, 115, 14, -39, 22, 47, 103, 104, -44, -9, 74, 74, -48, 87, 104, 118, -6, 22, -69, 17, -83, -82, 36,-120, 121, -2, 82, -37, 37, 67, -27, 60, -12, 69, -45, -40, 40, -50, 11, -11, -59, 96, 89, 61,-105, 39,-118, 89, 118, 45, -48, -62, -55, -51, 104, -44, 73, 106, 121, 37, 8, 97, 64, 20, -79, 59, 106, -91, 17, 40, -63,-116, -42, -87, 11,-121,-105,-116, 47, -15, 21, 29,-102,-107, -63,-101, -31, -64, 126, -23, -88,-102, -89,-122, -62, -75, 84, -65,-102, -25, -39, 35, -47, 85, -112, 56, 40, -47, -39, 108, -95, 102, 94, 78, -31, 48,-100, -2, -39, 113, -97, -30, -91, -30, 12,-101, -76, 71, 101, 56, 42, 70,-119, -87,-126, 121, 122, 118, 120, -62, 99, -79, 38, -33, -38, 41, 109, 62, 98, -32,-106, 18, 52, -65, 57, -90, 63,-119, 94, -15, 109, 14, -29, 108, 40, -95, 30, 32, 29, -53, -62, 3, 63, 65, 7,-124, 15, 20, 5, 101, 27, 40, 97, -55, -59, -25, 44,-114, 70, 54, 8, -36, -13, -88,-115, -2, -66, -14, -21, 113, -1, -96, -48, 59, 117, 6,-116, 126, -121, 120, 115, 77, -48, -66,-126, -66, -37, -62, 70, 65, 43,-116, -6, 48, 127, 112, -16, -89, 84,-122, 50,-107, -86, 91, 104, 19, 11, -26, -4, -11, -54, -66, 125, -97,-119,-118, 65, 27, -3, -72, 79, 104, -10, 114, 123, 20, -103, -51, -45, 13, -16, 68, 58, -76, -90, 102, 83, 51, 11, -53, -95, 16 }; static const byte freqTable[] = { 3, 10, 17, 24, 31, 38, 45, 51, 58, 65, 71, 77, 83, 90, 96, 102, 107, 113, 119, 125, 130, 136, 141, 146, 151, 157, 162, 167, 172, 177, 181, 186, 191, 195, 200, 204, 209, 213, 217, 221, 226, 230, 234, 238, 242, 246, 249, 253 }; /*static const byte amplTable[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 % 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 10 % 0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, // 20 % 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x20, // 30% 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x40, 0x40, 0x00, 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x20, // 40 % 0x30, 0x30, 0x40, 0x40, 0x40, 0x50, 0x50, 0x60, 0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x30, 0x30, // 50% 0x40, 0x40, 0x50, 0x50, 0x60, 0x60, 0x70, 0x70, 0x00, 0x00, 0x10, 0x10, 0x20, 0x30, 0x30, 0x40, // 60 % 0x40, 0x50, 0x60, 0x60, 0x70, 0x70, 0x80, 0x90, 0x00, 0x00, 0x10, 0x20, 0x20, 0x30, 0x40, 0x40, // 70 % 0x50, 0x60, 0x70, 0x70, 0x80, 0x90, 0x90, 0xA0, 0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x40, 0x50, // 80 % 0x60, 0x70, 0x80, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0x00, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, // 90 % 0x70, 0x80, 0x90, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // 100 % 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0 };*/ static const byte octaveTable[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0A, 0x00, 0x0B, 0x01, 0x00, 0x01, 0x01, 0x01, 0x02, 0x01, 0x03, 0x01, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x07, 0x01, 0x08, 0x01, 0x09, 0x01, 0x0A, 0x01, 0x0B, 0x02, 0x00, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x09, 0x02, 0x0A, 0x02, 0x0B, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x03, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x07, 0x03, 0x08, 0x03, 0x09, 0x03, 0x0A, 0x03, 0x0B, 0x04, 0x00, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x07, 0x04, 0x08, 0x04, 0x09, 0x04, 0x0A, 0x04, 0x0B, 0x05, 0x00, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x05, 0x04, 0x05, 0x05, 0x05, 0x06, 0x05, 0x07, 0x05, 0x08, 0x05, 0x09, 0x05, 0x0A, 0x05, 0x0B, 0x06, 0x00, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x06, 0x04, 0x06, 0x05, 0x06, 0x06, 0x06, 0x07, 0x06, 0x08, 0x06, 0x09, 0x06, 0x0A, 0x06, 0x0B, 0x07, 0x00, 0x07, 0x01, 0x07, 0x02, 0x07, 0x03, 0x07, 0x04, 0x07, 0x05, 0x07, 0x06, 0x07, 0x07, 0x07, 0x08, 0x07, 0x09, 0x07, 0x0A, 0x07, 0x0B, 0x08, 0x00, 0x08, 0x01, 0x08, 0x02, 0x08, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0A, 0x08, 0x0B, 0x09, 0x00, 0x09, 0x01, 0x09, 0x02, 0x09, 0x03, 0x09, 0x04, 0x09, 0x05, 0x09, 0x06, 0x09, 0x07, 0x09, 0x08, 0x09, 0x09, 0x09, 0x0A, 0x09, 0x0B, 0x0A, 0x00, 0x0A, 0x01, 0x0A, 0x02, 0x0A, 0x03, 0x0A, 0x04, 0x0A, 0x05, 0x0A, 0x06, 0x0A, 0x07, 0x0A, 0x08, 0x0A, 0x09, 0x0A, 0x0A, 0x0A, 0x0B }; static const byte attackRate[] = { 0, 2, 4, 7, 14, 26, 48, 82, 128, 144, 160, 176, 192, 208, 224, 255 }; static const byte decayRate[] = { 0, 1, 2, 3, 4, 6, 12, 24, 48, 96, 192, 215, 255, 255, 255, 255 }; static const byte sustainRate[] = { 255, 180, 128, 96, 80, 64, 56, 48, 42, 36, 32, 28, 24, 20, 16, 0 }; static const byte releaseRate[] = { 0, 1, 2, 4, 6, 9, 14, 22, 36, 56, 80, 100, 120, 140, 160, 255 }; static const uint16 pcjr_freq_table[12] = { 65472, 61760, 58304, 55040, 52032, 49024, 46272, 43648, 41216, 38912, 36736, 34624 }; static const byte volumeTable[] = { 0x00, 0x10, 0x10, 0x11, 0x11, 0x21, 0x22, 0x22, 0x33, 0x44, 0x55, 0x66, 0x88, 0xAA, 0xCC, 0xFF }; static CMSEmulator *g_cmsEmu = 0; Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) { int i; _isV3Game = (scumm->_game.version >= 3); _vm = scumm; _mixer = mixer; // debug("mixer rate: %d", _mixer->getOutputRate()); _sample_rate = CMS_RATE; _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6; // Initialize sound queue _current_nr = _next_nr = 0; _current_data = _next_data = 0; // Initialize channel code for (i = 0; i < 4; ++i) clear_channel(i); _next_tick = 0; _tick_len = (_sample_rate << FIXP_SHIFT) / FREQ_HZ; // Initialize V3 music timer _music_timer_ctr = _music_timer = 0; _ticks_per_music_timer = 65535; setMusicVolume(255); _timer_output = 0; for (i = 0; i < 4; i++) _timer_count[i] = 0; memset(_cmsVoicesBase, 0, sizeof(Voice)*16); memset(_cmsVoices, 0, sizeof(Voice2)*8); memset(_cmsChips, 0, sizeof(MusicChip)*2); _midiDelay = _octaveMask = _looping = _tempo = 0; _midiData = _midiSongBegin = 0; _loadedMidiSong = 0; memset(_midiChannel, 0, sizeof(Voice2*)*16); memset(_midiChannelUse, 0, sizeof(byte)*16); _cmsVoices[0].amplitudeOutput = &(_cmsChips[0].ampl[0]); _cmsVoices[0].freqOutput = &(_cmsChips[0].freq[0]); _cmsVoices[0].octaveOutput = &(_cmsChips[0].octave[0]); _cmsVoices[1].amplitudeOutput = &(_cmsChips[0].ampl[1]); _cmsVoices[1].freqOutput = &(_cmsChips[0].freq[1]); _cmsVoices[1].octaveOutput = &(_cmsChips[0].octave[0]); _cmsVoices[2].amplitudeOutput = &(_cmsChips[0].ampl[2]); _cmsVoices[2].freqOutput = &(_cmsChips[0].freq[2]); _cmsVoices[2].octaveOutput = &(_cmsChips[0].octave[1]); _cmsVoices[3].amplitudeOutput = &(_cmsChips[0].ampl[3]); _cmsVoices[3].freqOutput = &(_cmsChips[0].freq[3]); _cmsVoices[3].octaveOutput = &(_cmsChips[0].octave[1]); _cmsVoices[4].amplitudeOutput = &(_cmsChips[1].ampl[0]); _cmsVoices[4].freqOutput = &(_cmsChips[1].freq[0]); _cmsVoices[4].octaveOutput = &(_cmsChips[1].octave[0]); _cmsVoices[5].amplitudeOutput = &(_cmsChips[1].ampl[1]); _cmsVoices[5].freqOutput = &(_cmsChips[1].freq[1]); _cmsVoices[5].octaveOutput = &(_cmsChips[1].octave[0]); _cmsVoices[6].amplitudeOutput = &(_cmsChips[1].ampl[2]); _cmsVoices[6].freqOutput = &(_cmsChips[1].freq[2]); _cmsVoices[6].octaveOutput = &(_cmsChips[1].octave[1]); _cmsVoices[7].amplitudeOutput = &(_cmsChips[1].ampl[3]); _cmsVoices[7].freqOutput = &(_cmsChips[1].freq[3]); _cmsVoices[7].octaveOutput = &(_cmsChips[1].octave[1]); // inits the CMS Emulator like in the original g_cmsEmu = new CMSEmulator(_sample_rate); static const byte cmsInitData[13*2] = { 0x1C, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x14, 0x3F, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1C, 0x01 }; i = 0; for (int cmsPort = 0x220; i < 2; cmsPort += 2, ++i) { for (int off = 0; off < 13; ++off) { g_cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]); g_cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]); } } _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, false, true); } Player_V2CMS::~Player_V2CMS() { mutex_up(); _mixer->stopHandle(_soundHandle); delete g_cmsEmu; mutex_down(); } void Player_V2CMS::setMusicVolume(int vol) { } void Player_V2CMS::chainSound(int nr, byte *data) { int offset = _header_len + 10; _current_nr = nr; _current_data = data; for (int i = 0; i < 4; i++) { clear_channel(i); _channels[i].d.music_script_nr = nr; if (data) { _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i); if (_channels[i].d.next_cmd) { _channels[i].d.time_left = 1; } } } _music_timer = 0; } void Player_V2CMS::chainNextSound() { if (_next_nr) { chainSound(_next_nr, _next_data); _next_nr = 0; _next_data = 0; } } void Player_V2CMS::stopAllSounds() { mutex_up(); for (int i = 0; i < 4; i++) { clear_channel(i); } _next_nr = _current_nr = 0; _next_data = _current_data = 0; _midiData = 0; _midiSongBegin = 0; _midiDelay = 0; offAllChannels(); mutex_down(); } void Player_V2CMS::stopSound(int nr) { mutex_up(); if (_next_nr == nr) { _next_nr = 0; _next_data = 0; } if (_current_nr == nr) { for (int i = 0; i < 4; i++) { clear_channel(i); } _current_nr = 0; _current_data = 0; chainNextSound(); } if (_loadedMidiSong == nr) { _midiData = 0; _midiSongBegin = 0; _midiDelay = 0; offAllChannels(); } mutex_down(); } void Player_V2CMS::startSound(int nr) { byte *data = _vm->getResourceAddress(rtSound, nr); assert(data); if (data[6] == 0x80) { mutex_up(); loadMidiData(data, nr); mutex_down(); } else { mutex_up(); int cprio = _current_data ? *(_current_data + _header_len) : 0; int prio = *(data + _header_len); int nprio = _next_data ? *(_next_data + _header_len) : 0; int restartable = *(data + _header_len + 1); if (!_current_nr || cprio <= prio) { int tnr = _current_nr; int tprio = cprio; byte *tdata = _current_data; chainSound(nr, data); nr = tnr; prio = tprio; data = tdata; restartable = data ? *(data + _header_len + 1) : 0; } if (!_current_nr) { nr = 0; _next_nr = 0; _next_data = 0; } if (nr != _current_nr && restartable && (!_next_nr || nprio <= prio)) { _next_nr = nr; _next_data = data; } mutex_down(); } } void Player_V2CMS::loadMidiData(byte *data, int sound) { memset(_midiChannelUse, 0, sizeof(byte)*16); _tempo = data[7]; _looping = data[8]; byte channels = data[14]; byte curChannel = 0; byte *voice2 = data + 23; for (; channels != 0; ++curChannel, --channels, voice2 += 16) { if (*(data + 15 + curChannel)) { byte channel = *(data + 15 + curChannel) - 1; _midiChannelUse[channel] = 1; Voice *voiceDef = &_cmsVoicesBase[channel]; byte attackDecay = voice2[10]; voiceDef->attack = attackRate[attackDecay >> 4]; voiceDef->decay = decayRate[attackDecay & 0x0F]; byte sustainRelease = voice2[11]; voiceDef->sustain = sustainRate[sustainRelease >> 4]; voiceDef->release = releaseRate[sustainRelease & 0x0F]; if (voice2[3] & 0x40) { voiceDef->vibrato = 0x0301; if (voice2[13] & 0x40) { voiceDef->vibrato = 0x0601; } } else { voiceDef->vibrato = 0; } if (voice2[8] & 0x80) { voiceDef->vibrato2 = 0x0506; if (voice2[13] & 0x80) { voiceDef->vibrato2 = 0x050C; } } else { voiceDef->vibrato2 = 0; } if ((voice2[8] & 0x0F) > 1) { voiceDef->octadd = 0x01; } else { voiceDef->octadd = 0x00; } } } for (int i = 0, channel = 0; i < 8; ++i, channel += 2) { _cmsVoices[i].chanNumber = 0xFF; _cmsVoices[i].curVolume = 0; _cmsVoices[i].nextVoice = 0; _midiChannel[channel] = 0; } _midiDelay = 0; memset(_cmsChips, 0, sizeof(MusicChip)*2); _midiData = data + 151; _midiSongBegin = _midiData + data[9]; _loadedMidiSong = sound; } int Player_V2CMS::getSoundStatus(int nr) const { return _current_nr == nr || _next_nr == nr || _loadedMidiSong == nr; } void Player_V2CMS::clear_channel(int i) { ChannelInfo *channel = &_channels[i]; memset(channel, 0, sizeof(ChannelInfo)); } int Player_V2CMS::getMusicTimer() const { if (_isV3Game) return _music_timer; else return _channels[0].d.music_timer; } void Player_V2CMS::execute_cmd(ChannelInfo *channel) { uint16 value; int16 offset; uint8 *script_ptr; ChannelInfo * current_channel; ChannelInfo * dest_channel; current_channel = channel; if (channel->d.next_cmd == 0) goto check_stopped; script_ptr = &_current_data[channel->d.next_cmd]; for (;;) { uint8 opcode = *script_ptr++; if (opcode >= 0xf8) { switch (opcode) { case 0xf8: // set hull curve debug(7, "channels[%d]: hull curve %2d", (uint)(channel - _channels), *script_ptr); channel->d.hull_curve = hull_offsets[*script_ptr / 2]; script_ptr++; break; case 0xf9: // set freqmod curve debug(7, "channels[%d]: freqmod curve %2d", (uint)(channel - _channels), *script_ptr); channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4]; channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4]; script_ptr++; break; case 0xfd: // clear other channel value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo); debug(7, "clear channel %d", value); script_ptr += 2; // In Indy3, when traveling to Venice a command is // issued to clear channel 4. So we introduce a 4th // channel, which is never used. All OOB accesses are // mapped to this channel. // // The original game had room for 8 channels, but only // channels 0-3 are read, changes to other channels // had no effect. if (value >= ARRAYSIZE (_channels)) value = 4; channel = &_channels[value]; // fall through case 0xfa: // clear current channel if (opcode == 0xfa) debug(7, "clear channel"); channel->d.next_cmd = 0; channel->d.base_freq = 0; channel->d.freq_delta = 0; channel->d.freq = 0; channel->d.volume = 0; channel->d.volume_delta = 0; channel->d.inter_note_pause = 0; channel->d.transpose = 0; channel->d.hull_curve = 0; channel->d.hull_offset = 0; channel->d.hull_counter = 0; channel->d.freqmod_table = 0; channel->d.freqmod_offset = 0; channel->d.freqmod_incr = 0; channel->d.freqmod_multiplier = 0; channel->d.freqmod_modulo = 0; break; case 0xfb: // ret from subroutine debug(7, "ret from sub"); script_ptr = _retaddr; break; case 0xfc: // call subroutine offset = READ_LE_UINT16 (script_ptr); debug(7, "subroutine %d", offset); script_ptr += 2; _retaddr = script_ptr; script_ptr = _current_data + offset; break; case 0xfe: // loop music opcode = *script_ptr++; offset = READ_LE_UINT16 (script_ptr); script_ptr += 2; debug(7, "loop if %d to %d", opcode, offset); if (!channel->array[opcode / 2] || --channel->array[opcode/2]) script_ptr += offset; break; case 0xff: // set parameter opcode = *script_ptr++; value = READ_LE_UINT16 (script_ptr); channel->array[opcode / 2] = value; debug(7, "channels[%d]: set param %2d = %5d", (uint)(channel - _channels), opcode, value); script_ptr += 2; if (opcode == 14) { /* tempo var */ _ticks_per_music_timer = 125; } if (opcode == 0) goto end; break; } } else { // opcode < 0xf8 for (;;) { int16 note, octave; int is_last_note; dest_channel = &_channels[(opcode >> 5) & 3]; if (!(opcode & 0x80)) { int tempo = channel->d.tempo; if (!tempo) tempo = 1; channel->d.time_left = tempo * note_lengths[opcode & 0x1f]; note = *script_ptr++; is_last_note = note & 0x80; note &= 0x7f; if (note == 0x7f) { debug(8, "channels[%d]: pause %d", (uint)(channel - _channels), channel->d.time_left); goto end; } } else { channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++; if ((opcode & 0x10)) { debug(8, "channels[%d]: pause %d", (uint)(channel - _channels), channel->d.time_left); goto end; } is_last_note = 0; note = (*script_ptr++) & 0x7f; } debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s", (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0, note, (signed short) dest_channel->d.transpose, channel->d.time_left, dest_channel->d.hull_curve, dest_channel->d.freqmod_table, dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier, is_last_note ? "last":""); uint16 myfreq; dest_channel->d.time_left = channel->d.time_left; dest_channel->d.note_length = channel->d.time_left - dest_channel->d.inter_note_pause; note += dest_channel->d.transpose; while (note < 0) note += 12; octave = note / 12; note = note % 12; dest_channel->d.hull_offset = 0; dest_channel->d.hull_counter = 1; if (dest_channel == &_channels[3]) { dest_channel->d.hull_curve = 196 + note * 12; myfreq = 384 - 64 * octave; } else { myfreq = pcjr_freq_table[note] >> octave; } dest_channel->d.freq = dest_channel->d.base_freq = myfreq; if (is_last_note) goto end; opcode = *script_ptr++; } } } end: channel = current_channel; if (channel->d.time_left) { channel->d.next_cmd = script_ptr - _current_data; return; } channel->d.next_cmd = 0; check_stopped: int i; for (i = 0; i < 4; i++) { if (_channels[i].d.time_left) return; } _current_nr = 0; _current_data = 0; chainNextSound(); return; } void Player_V2CMS::next_freqs(ChannelInfo *channel) { channel->d.volume += channel->d.volume_delta; channel->d.base_freq += channel->d.freq_delta; channel->d.freqmod_offset += channel->d.freqmod_incr; if (channel->d.freqmod_offset != 0) if (channel->d.freqmod_offset > channel->d.freqmod_modulo) channel->d.freqmod_offset -= channel->d.freqmod_modulo; channel->d.freq = (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)]) * (int) channel->d.freqmod_multiplier / 256 + channel->d.base_freq; debug(9, "Freq: %d/%d, %d/%d/%d*%d %d", channel->d.base_freq, (int16)channel->d.freq_delta, channel->d.freqmod_table, channel->d.freqmod_offset, channel->d.freqmod_incr, channel->d.freqmod_multiplier, channel->d.freq); if (channel->d.note_length && !--channel->d.note_length) { channel->d.hull_offset = 16; channel->d.hull_counter = 1; } if (!--channel->d.time_left) { execute_cmd(channel); } if (channel->d.hull_counter && !--channel->d.hull_counter) { for (;;) { const int16 *hull_ptr = hulls + channel->d.hull_curve + channel->d.hull_offset / 2; if (hull_ptr[1] == -1) { channel->d.volume = hull_ptr[0]; if (hull_ptr[0] == 0) channel->d.volume_delta = 0; channel->d.hull_offset += 4; } else { channel->d.volume_delta = hull_ptr[0]; channel->d.hull_counter = hull_ptr[1]; channel->d.hull_offset += 4; break; } } } } void Player_V2CMS::nextTick() { for (int i = 0; i < 4; i++) { if (!_channels[i].d.time_left) continue; next_freqs(&_channels[i]); } if (_music_timer_ctr++ >= _ticks_per_music_timer) { _music_timer_ctr = 0; _music_timer++; } } void Player_V2CMS::processMidiData(uint ticks) { byte *currentData = _midiData; byte command = 0x00; int16 temp = 0; if (!_midiDelay) { while (true) { if ((command = *currentData++) == 0xFF) { if ((command = *currentData++) == 0x2F) { if (_looping == 0) { currentData = _midiData = _midiSongBegin; continue; } _midiData = _midiSongBegin = 0; offAllChannels(); return; } else { if (command == 0x58) { currentData += 6; } } } else { _lastMidiCommand = command; if (command < 0x90) { clearNote(currentData); } else { playNote(currentData); } } temp = command = *currentData++; if (command & 0x80) { temp = (command & 0x7F) << 8; command = *currentData++; temp |= (command << 1); temp >>= 1; } temp >>= 1; int lastBit = temp & 1; temp >>= 1; temp += lastBit; if (temp) break; } _midiData = currentData; _midiDelay = temp; } _midiDelay -= ticks; if (_midiDelay < 0) _midiDelay = 0; return; } int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) { mutex_up(); uint step = 1; int len = numSamples/2; // maybe this needs a complete rewrite do { if (_midiData) { --_clkFrequenz; if (!(_clkFrequenz & 0x01)) { playVoice(); } _tempoSum += _tempo; // FIXME: _tempoSum is declared as char; on some systems char is unsigned. // E.g. on OS X. Hence the following check is always false. // Moral of the story: Use uint8, int8 or any of the other types provided by // ScummVM if you want to ensure signedness and number of available bits. if (_tempoSum < 0) { // this have to be called in the same rate as in the original (I think) processMidiData(1); } } if (!(_next_tick >> FIXP_SHIFT) && !_midiData) { _next_tick += _tick_len; nextTick(); play(); } step = len; if (step > (_next_tick >> FIXP_SHIFT)) step = (_next_tick >> FIXP_SHIFT); g_cmsEmu->readBuffer(buffer, step); buffer += 2 * step; _next_tick -= step << FIXP_SHIFT; } while (len -= step); mutex_down(); return numSamples; } void Player_V2CMS::playVoice() { if (_outputTableReady) { playMusicChips(_cmsChips); _outputTableReady = 0; } _octaveMask = 0xF0; Voice2 *voice =0; for (int i = 0; i < 8; ++i) { voice = &_cmsVoices[i]; _octaveMask = ~_octaveMask; if (voice->chanNumber != 0xFF) { processChannel(voice); continue; } if (!voice->curVolume) { *(voice->amplitudeOutput) = 0; } int volume = voice->curVolume - voice->releaseRate; voice->curVolume = volume; if (volume < 0) { volume = voice->curVolume = 0; } *(voice->amplitudeOutput) = ((volume >> 4) | (volume & 0xF0)) & voice->channel; ++_outputTableReady; } } void Player_V2CMS::processChannel(Voice2 *channel) { ++_outputTableReady; switch (channel->nextProcessState) { case PROCESS_RELEASE: processRelease(channel); break; case PROCESS_ATTACK: processAttack(channel); break; case PROCESS_DECAY: processDecay(channel); break; case PROCESS_SUSTAIN: processSustain(channel); break; case PROCESS_VIBRATO: processVibrato(channel); break; default: break; } } void Player_V2CMS::processRelease(Voice2 *channel) { channel->curVolume -= channel->releaseRate; if (channel->curVolume < 0) channel->curVolume = 0; processVibrato(channel); } void Player_V2CMS::processAttack(Voice2 *channel) { channel->curVolume += channel->attackRate; if (channel->curVolume >= 0) { if (channel->curVolume <= channel->maxAmpl) return processVibrato(channel); } channel->curVolume = channel->maxAmpl; channel->nextProcessState = PROCESS_DECAY; processVibrato(channel); } void Player_V2CMS::processDecay(Voice2 *channel) { channel->curVolume -= channel->decayRate; if (channel->curVolume >= 0) { if (channel->curVolume > channel->sustainRate) return processVibrato(channel); } channel->curVolume = channel->sustainRate; channel->nextProcessState = PROCESS_SUSTAIN; processVibrato(channel); } void Player_V2CMS::processSustain(Voice2 *channel) { if (channel->unkVibratoRate) { int volume = (int)channel->curVolume + (int)channel->unkRate; if (volume & 0xFF00) { volume = ((~volume) >> 8) & 0xFF; } channel->curVolume = volume; --(channel->unkCount); if (!channel->unkCount) { channel->unkRate = ~(channel->unkRate); channel->unkCount = (channel->unkVibratoDepth & 0x0F) << 1; } } processVibrato(channel); } void Player_V2CMS::processVibrato(Voice2 *channel) { if (channel->vibratoRate) { uint16 temp = channel->curFreq + channel->curVibratoRate; channel->curOctave += (temp & 0xFF00) >> 8; channel->curFreq = temp & 0xFF; --(channel->curVibratoUnk); if (!channel->curVibratoUnk) { channel->curVibratoRate = ~(channel->curVibratoRate); channel->curVibratoUnk = (channel->vibratoDepth & 0x0F) << 1; } } byte *output = channel->amplitudeOutput; *output = ((channel->curVolume >> 4) | (channel->curVolume & 0xF0)) & channel->channel; output = channel->freqOutput; *output = channel->curFreq; output = channel->octaveOutput; *output = ((((channel->curOctave >> 4) | (channel->curOctave & 0x0F)) & _octaveMask) | ((~_octaveMask) & *output)); } void Player_V2CMS::offAllChannels() { warning("offAllChannels STUB"); /* // after using this sound can not be played anymore (since it would deinit the emulator) static const byte cmsOffData[10*2] = { 0x1C, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x14, 0x3F, 0x15, 0x00, 0x16, 0x00 }; for (int cmsPort = 0x220, i = 0; i < 2; cmsPort += 2, ++i) { for (int off = 0; off < 10; ++off) { g_cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]); g_cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]); } }*/ } Player_V2CMS::Voice2 *Player_V2CMS::getFreeVoice() { Voice2 *curVoice = 0; Voice2 *selected = 0; uint8 volume = 0xFF; for (int i = 0; i < 8; ++i) { curVoice = &_cmsVoices[i]; if (curVoice->chanNumber == 0xFF) { if (!curVoice->curVolume) { selected = curVoice; break; } if (curVoice->curVolume < volume) { selected = curVoice; volume = selected->curVolume; } } } if (selected) { selected->chanNumber = _lastMidiCommand & 0x0F; uint8 channel = selected->chanNumber; Voice2 *oldChannel = _midiChannel[channel]; _midiChannel[channel] = selected; selected->nextVoice = oldChannel; } return selected; } void Player_V2CMS::playNote(byte *&data) { byte channel = _lastMidiCommand & 0x0F; if (_midiChannelUse[channel]) { Voice2 *freeVoice = getFreeVoice(); if (freeVoice) { Voice *voice = &_cmsVoicesBase[freeVoice->chanNumber]; freeVoice->attackRate = voice->attack; freeVoice->decayRate = voice->decay; freeVoice->sustainRate = voice->sustain; freeVoice->releaseRate = voice->release; freeVoice->octaveAdd = voice->octadd; freeVoice->vibratoRate = freeVoice->curVibratoRate = voice->vibrato; freeVoice->unkVibratoRate = freeVoice->unkRate = voice->vibrato2; freeVoice->maxAmpl = 0xFF; uint8 rate = freeVoice->attackRate; uint8 volume = freeVoice->curVolume >> 1; if (rate < volume) rate = volume; rate -= freeVoice->attackRate; freeVoice->curVolume = rate; freeVoice->playingNote = *data; int octave = octaveTable[(*data + 3) << 1] + freeVoice->octaveAdd - 3; if (octave < 0) octave = 0; if (octave > 7) octave = 7; if (!octave) ++octave; freeVoice->curOctave = octave; freeVoice->curFreq = freqTable[volume << 2]; freeVoice->curVolume = 0; freeVoice->nextProcessState = PROCESS_ATTACK; if (_lastMidiCommand & 1) freeVoice->channel = 0xF0; else freeVoice->channel = 0x0F; } } data += 2; } Player_V2CMS::Voice2 *Player_V2CMS::getPlayVoice(byte param) { byte channelNum = _lastMidiCommand & 0x0F; Voice2 *channel = _midiChannel[channelNum]; if (channel) { Voice2 *backUp = 0; while (true) { if (channel->playingNote == param) break; backUp = channel; channel = channel->nextVoice; if (!channel) return 0; } Voice2 *backUp2 = channel->nextVoice; { Voice2 *temp = backUp; backUp = channel; channel = temp; } if (channel) { channel->nextVoice = backUp2; } else { _midiChannel[channelNum] = backUp2; } channel = backUp; } return channel; } void Player_V2CMS::clearNote(byte *&data) { Voice2 *voice = getPlayVoice(*data); if (voice) { voice->chanNumber = 0xFF; voice->nextVoice = 0; voice->nextProcessState = PROCESS_RELEASE; } data += 2; } void Player_V2CMS::play() { _octaveMask = 0xF0; channel_data *chan = &(_channels[0].d); static byte volumeReg[4] = { 0x00, 0x00, 0x00, 0x00 }; static byte octaveReg[4] = { 0x66, 0x66, 0x66, 0x66 }; static byte freqReg[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; static byte freqEnable = 0x3E; static byte noiseEnable = 0x01; static byte noiseGen = 0x02; for (int i = 1; i <= 4; ++i) { if (chan->time_left) { uint16 freq = chan->freq; if (i == 4) { if ((freq >> 8) & 0x40) { noiseGen = freq & 0xFF; } else { noiseGen = 3; freqReg[0] = freqReg[3]; octaveReg[0] = (octaveReg[0] & 0xF0) | ((octaveReg[1] & 0xF0) >> 4); } } else { if (freq == 0) { freq = 0xFFC0; } int cmsOct = 2; int freqOct = 0x8000; while (true) { if (freq >= freqOct) { break; } freqOct >>= 1; ++cmsOct; if (cmsOct == 8) { --cmsOct; freq = 1024; break; } } byte oct = cmsOct << 4; oct |= cmsOct; oct &= _octaveMask; oct |= ((~_octaveMask) & octaveReg[((i & 3) >> 1)]); octaveReg[((i & 3) >> 1)] = oct; freq >>= -(cmsOct-9); freqReg[(i&3)] = (-(freq-511)) & 0xFF; } volumeReg[i & 3] = volumeTable[chan->volume >> 12]; } else { volumeReg[i & 3] = 0; } chan = &(_channels[i].d); _octaveMask ^= 0xFF; } // with the high nibble of the volumeReg value // the right channels amplitude is set // with the low value the left channels amplitude g_cmsEmu->portWrite(0x221, 0); g_cmsEmu->portWrite(0x220, volumeReg[0]); g_cmsEmu->portWrite(0x221, 1); g_cmsEmu->portWrite(0x220, volumeReg[1]); g_cmsEmu->portWrite(0x221, 2); g_cmsEmu->portWrite(0x220, volumeReg[2]); g_cmsEmu->portWrite(0x221, 3); g_cmsEmu->portWrite(0x220, volumeReg[3]); g_cmsEmu->portWrite(0x221, 8); g_cmsEmu->portWrite(0x220, freqReg[0]); g_cmsEmu->portWrite(0x221, 9); g_cmsEmu->portWrite(0x220, freqReg[1]); g_cmsEmu->portWrite(0x221, 10); g_cmsEmu->portWrite(0x220, freqReg[2]); g_cmsEmu->portWrite(0x221, 11); g_cmsEmu->portWrite(0x220, freqReg[3]); g_cmsEmu->portWrite(0x221, 0x10); g_cmsEmu->portWrite(0x220, octaveReg[0]); g_cmsEmu->portWrite(0x221, 0x11); g_cmsEmu->portWrite(0x220, octaveReg[1]); g_cmsEmu->portWrite(0x221, 0x14); g_cmsEmu->portWrite(0x220, freqEnable); g_cmsEmu->portWrite(0x221, 0x15); g_cmsEmu->portWrite(0x220, noiseEnable); g_cmsEmu->portWrite(0x221, 0x16); g_cmsEmu->portWrite(0x220, noiseGen); } void Player_V2CMS::playMusicChips(const MusicChip *table) { int cmsPort = 0x21E; do { cmsPort += 2; g_cmsEmu->portWrite(cmsPort+1, 0); g_cmsEmu->portWrite(cmsPort, table->ampl[0]); g_cmsEmu->portWrite(cmsPort+1, 1); g_cmsEmu->portWrite(cmsPort, table->ampl[1]); g_cmsEmu->portWrite(cmsPort+1, 2); g_cmsEmu->portWrite(cmsPort, table->ampl[2]); g_cmsEmu->portWrite(cmsPort+1, 3); g_cmsEmu->portWrite(cmsPort, table->ampl[3]); g_cmsEmu->portWrite(cmsPort+1, 8); g_cmsEmu->portWrite(cmsPort, table->freq[0]); g_cmsEmu->portWrite(cmsPort+1, 9); g_cmsEmu->portWrite(cmsPort, table->freq[1]); g_cmsEmu->portWrite(cmsPort+1, 10); g_cmsEmu->portWrite(cmsPort, table->freq[2]); g_cmsEmu->portWrite(cmsPort+1, 11); g_cmsEmu->portWrite(cmsPort, table->freq[3]); g_cmsEmu->portWrite(cmsPort+1, 0x10); g_cmsEmu->portWrite(cmsPort, table->octave[0]); g_cmsEmu->portWrite(cmsPort+1, 0x11); g_cmsEmu->portWrite(cmsPort, table->octave[1]); g_cmsEmu->portWrite(cmsPort+1, 0x14); g_cmsEmu->portWrite(cmsPort, 0x3F); g_cmsEmu->portWrite(cmsPort+1, 0x15); g_cmsEmu->portWrite(cmsPort, 0x00); ++table; } while ((cmsPort & 2) == 0); } void Player_V2CMS::mutex_up() { _mutex.lock(); } void Player_V2CMS::mutex_down() { _mutex.unlock(); } } // end of namespace Scumm