/* WARNING - WARNING - WARNING * This code is not finished, doesn't compile, doesn't work. * I think I won't finish the code but try a different approach. * This code may be useful as a reference nontheless. */ /* ScummVM - Scumm Interpreter * Copyright (C) 2001-2003 The ScummVM project * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header: /cvsroot/scummvm/scummvm/scumm/player_v2.cpp,v 2.14 2003/06/07 09:48:10 hoenicke Exp $ */ #include "stdafx.h" #include "common/engine.h" #include "sound/fmopl.h" #include "player_adlib.h" #include "scumm.h" #define FREQ_HZ (473 / 4) // Don't change! #define SPK_DECAY 0xfa00 /* Depends on sample rate */ #define PCJR_DECAY 0xf600 /* Depends on sample rate */ #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 */ #define HIGH_NIBBLE(x) ((byte) (x) >> 4) #define LOW_NIBBLE(x) ((x) & 15) struct modifier_xlat { int instr_offset; int opl_reg; int opl_regtype; int maxvalue; int reverse; int mask; int shift; } const struct modifier_xlat mod2adlib[7] = { { INSTR_FREQ , 0xa0, 2, 0xff, 0, 0xff, 0 }, { INSTR_FEEDBACK , 0xc0, 2, 0x07, 0, 0x0e, 1 }, { INSTR_LEVEL2 , 0x40, 1, 0x3f, 0x3f, 0x3f, 0 }, { INSTR_AMP2 , 0x20, 1, 0x0f, 0, 0x0f, 0 }, { INSTR_LEVEL , 0x40, 0, 0x3f, 0x3f, 0x3f, 0 }, { INSTR_AMP , 0x20, 0, 0x0f, 0, 0x0f, 0 }, { -1 , 0, 0, 0x3f, 0, 0, 0 } } mod2param[7] { 29, 27, 13, 16, 0, 3, -1 }; static const AdlibSetParams adlib_setparam_table[] = { {0x40, 0, 63, 63}, // level {0xE0, 2, 0, 0}, // unused {0x40, 6, 192, 0}, // level key scaling {0x20, 0, 15, 0}, // modulator frequency multiple {0x60, 4, 240, 15}, // attack rate {0x60, 0, 15, 15}, // decay rate {0x80, 4, 240, 15}, // sustain level {0x80, 0, 15, 15}, // release rate {0xE0, 0, 3, 0}, // waveform select {0x20, 7, 128, 0}, // amp mod {0x20, 6, 64, 0}, // vib {0x20, 5, 32, 0}, // eg typ {0x20, 4, 16, 0}, // ksr {0xC0, 0, 1, 0}, // decay alg {0xC0, 1, 14, 0} // feedback }; enum instr_offsets { INSTR_FREQ = 1, INSTR_FEEDBACK = 3, INSTR_AMP = 4, INSTR_LEVEL = 5, INSTR_ATTACK = 6,  INSTR_SUSTAIN = 7,  INSTR_WAVEFORM = 8,  INSTR_AMP2 = 9, INSTR_LEVEL2 = 10, INSTR_ATTACK2 = 11, INSTR_SUSTAIN2 = 12, INSTR_WAVEFORM2 = 13 } struct ModifierInfo { int offset; int remaining_time; int current_value; int delta; int change_time; }; struct ChannelInfo { int mode; int sound_nr; byte * chunk_ptr; InstrumentParams instrument; }; const static int channel2opl[18] = { 0, 3, 1, 4, 2, 5, 8, 11, 9, 12, 10, 13, 16, 19, 17, 20, 18, 21 }; //////////////////////////////////////// // // V2 PC-Speaker MIDI driver // //////////////////////////////////////// Player_Adlib::Player_Adlib(Scumm *scumm) : _scumm(scumm) { int i; // This simulates the pc speaker sound, which is driven // by the 8253 (square wave generator) and a low-band filter. _system = scumm->_system; _sample_rate = _system->property(OSystem::PROP_GET_SAMPLE_RATE, 0); _mutex = _system->create_mutex(); _header_len = (scumm->_features & GF_OLD_BUNDLE) ? 4 : 6; // Allocate Channels channels = new ChannelInfo[9]; modinfos = new ModifierInfo[9*2]; // 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; // Initialize square generator _level = 0; _RNG = NG_PRESET; set_pcjr(true); set_master_volume(255); scumm->_mixer->setupPremix(this, premix_proc); } Player_Adlib::~Player_Adlib() { mutex_up(); // Detach the premix callback handler _scumm->_mixer->setupPremix (0, 0); mutex_down(); _system->delete_mutex (_mutex); delete (channels); delete (modinfos); } void Player_Adlib::set_pcjr (bool pcjr) { mutex_up(); _pcjr = pcjr; if (_pcjr) { _decay = PCJR_DECAY; _update_step = (_sample_rate << FIXP_SHIFT) / (111860 * 2); _freqs_table = pcjr_freq_table; } else { _decay = SPK_DECAY; _update_step = (_sample_rate << FIXP_SHIFT) / (1193000 * 2); _freqs_table = spk_freq_table; } /* adapt _decay to sample rate. It must be squared when * sample rate doubles. */ int i; for (i = 0; (_sample_rate << i) < 30000; i++) _decay = _decay * _decay / 65536; _timer_output = 0; for (i = 0; i < 4; i++) _timer_count[i] = 0; if (current_data) restartSound(); mutex_down(); } void Player_Adlib::set_master_volume (int vol) { if (vol > 255) vol = 255; /* scale to int16, FIXME: find best value */ double out = vol * 128 / 3; /* build volume table (2dB per step) */ for (int i = 0; i < 15; i++) { /* limit volume to avoid clipping */ if (out > 0x7fff) _volumetable[i] = 0x7fff; else _volumetable[i] = (int) out; out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */ } _volumetable[15] = 0; } void Player_Adlib::chainSound(int nr, byte *data) { int offset = _header_len + (_pcjr ? 10 : 2); 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_Adlib::chainNextSound() { if (next_nr) { chainSound(next_nr, next_data); next_nr = 0; next_data = 0; } } void Player_Adlib::stopAllSounds() { mutex_up(); for (int i = 0; i < 4; i++) { clear_channel(i); } next_nr = current_nr = 0; next_data = current_data = 0; mutex_down(); } void Player_Adlib::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(); } mutex_down(); } void Player_Adlib::startSound(int nr, byte *data) { 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_Adlib::restartSound() { if (*(current_data + _header_len + 1)) { /* current sound is restartable */ chainSound(current_nr, current_data); } else { chainNextSound(); } } int Player_Adlib::getSoundStatus(int nr) { return current_nr == nr || next_nr == nr; } void Player_Adlib::premix_proc(void *param, int16 *buf, uint len) { ((Player_Adlib *) param)->do_mix(buf, len); } void Player_Adlib::clear_channel(int i) { ChannelInfo *channel = &channels[i]; channel->d.time_left = 0; 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; } int Player_Adlib::getMusicTimer() { if (_scumm->_version == 3) return _music_timer; else return channels[0].d.music_timer; } void Player_Adlib::adlib_write(byte port, byte value) { if (_adlib_reg_cache[port] == value) return; _adlib_reg_cache[port] = value; YM3812Write(0, 0, port); YM3812Write(0, 1, value); } void Player_Adlib::set_alasw(int channel, byte* ptr) { adlib_write(0x20 + channel, ptr[0]); adlib_write(0x40 + channel, ptr[1]); adlib_write(0x60 + channel, ptr[2]); adlib_write(0x80 + channel, ptr[3]); adlib_write(0xe0 + channel, ptr[4]); } void Player_Adlib::set_instrument(int channel, byte* ptr) { adlib_write(0xc0 + channel, *(ptr + INSTR_FEEDBACK)); set_alasw(channel2opl[2 * channel + 0], ptr + INSTR_AMP); set_alasw(channel2opl[2 * channel + 1], ptr + INSTR_AMP2); adlib_write(0xa0 + channel, *(ptr + INSTR_FREQ)); adlib_write(0xb0 + channel, *(ptr + INSTR_FREQ+1) & 0xdf); } int Player_Adlib::get_value(int modparam, int reg_val) { if (modparam == 6) return 0; if (!reg_val) { if (mod2adlib[modparam].opl_regtype == 2) { oplchan = channel; } else { oplchan = channel2opl[2 * channel + mod2adlib[modparam].opl_regtype]; } reg_val = _adlib_reg_cache[mod2adlib[modparam].opl_reg + oplchan]; } return (reg_val & mod2adlib[modparam].mask) >> shift; } void Player_Adlib::set_value(int modparam, int value) { if (modparam == 6) return; if (mod2adlib[modparam].opl_regtype == 2) { oplchan = channel; } else { oplchan = channel2opl[2 * channel + mod2adlib[modparam].opl_regtype]; } int port = mod2adlib[modparam].opl_reg + oplchan; adlib_write(port, (_adlib_reg_cache[port] & ~mod2adlib[modparam].mask) | (value << mod2adlib[modparam]. shift)); } int Player_Adlib::next_chunk2_cmd(int channel, int subchunk) { byte *ptr = channels[channel].chunk_ptr + 1 + 5 * subchunk; ModifierInfo *modinfo = modinfos[2 * channel + subchunk] int chunk2_ctr = ++modinfo->chunk2_ctr; if (chunk2_ctr ==  4) return 1; int modparam = ptr[0] & 7; modinfo->modparam = modparam; if (chunk2_ctr == 0) cl = channels[channel].instrument_definition[modparam]; else cl = 0; int src_value = get_value(modparam, cl); int change_time; int dest_value; if (mod2adlib[modparam].reverse) src_value = mod2adlib[modparam].reverse - src_value; if (chunk2_ctr <= 2) { byte value = ptr[1 + chunk2_ctr]; change_time = decode_time[HIGH_NIBBLE(value)]; if (chunk2_ctr == 2) { if (ptr[0] & 0x40) change_time = _scumm->_rnd->getRandomNumber(change_time); modinfo->remaining_time = change_time; return 0; } dest_value = modparam_maxval[modparam] * LOW_NIBBLE(value) / 15; } else { change_time = decode_time[LOW_NIBBLE(ptr[3])]; dest_value = 0; } modinfo->delta = dest_value - src_value; modinfo->change_time = change_time; modinfo->remaining_time = change_time; modinfo->current_value = src_value * delta; return 0; } int Player_Adlib::modify_value(ModifierInfo *modinfo) { } void Player_Adlib::init_chunk2(int channel, int subchunk) { byte *ptr = channels[channel].chunk_ptr + 1 + 5 * subchunk; channels[channel].chunk2_ctr[subchunk] = -1; next_chunk2_cmd(channel, subchunk); channels[channel].chunk2_repeattime = 0; if (ptr[0] & 0x20) { channels[channel].chunk2_repeattime = HIGH_NIBBLE(ptr[4]) * 118 + LOW_NIBBLE(ptr[4]) * 8; } } void Player_Adlib::restart_note(int channel) { /* toggle the mute bit ??? */ int old_value = _adlib_reg_cache[0xb0 + channel]; adlib_write(0xb0 + channel, old_value & 0xdf); adlib_write(0xb0 + channel, old_value | 0x20); } void Player_Adlib::next_command2(int channel) { byte *ptr = channels[channel].chunk_ptr + 1; for (subchunk = 0; subchunk < 2; subchunk++, ptr += 5) { ModifierInfo *modinfo = &modinfos[2 * channel + subchunk]; if (!(ptr[0] & 0x80)) continue; if (channels[channel].chunk2_ctr[subchunk] != 2) { modinfo->current_value += change_time; value = modinfo->current_value / delta; if (mod2adlib[modparam].reverse) value = mod2adlib[modparam].reverse - value; set_value(modparam, value); } if (--modinfo->remaining_time) goto l3f12; if (!next_chunk2_cmd(channel, subchunk)) goto l3f12; if ((ptr[0] & 0x08)) { if ((ptr[0] & 0x10)) restart_note(channel); channels[channel].chunk2_ctr[subchunk] = -1; next_chunk2_cmd(channel, subchunk); l3f12: if ((ptr[0] & 0x20) && !--channels[channel].chunk2_repeattime) { channels[channel].chunk_ptr += 11; channels[channel].mode = 1; } } else { channels[channel].chunk_ptr += 11; channels[channel].mode = 1; } } } void Player_Adlib::find_next_chunk(int channel) { for(;;) { byte header = *channels[channel].chunk_ptr; switch (header) { case 1: channels[channel].instrument.freq = channels[channel].chunk_ptr + INSTR_FREQ; channels[channel].instrument.feedback = channels[channel].chunk_ptr + INSTR_FEEDBACK; channels[channel].instrument.level2 = channels[channel].chunk_ptr + INSTR_LEVEL2; channels[channel].instrument.amp2 = channels[channel].chunk_ptr + INSTR_AMP2; channels[channel].instrument.level = channels[channel].chunk_ptr + INSTR_LEVEL; channels[channel].instrument.amp = channels[channel].chunk_ptr + INSTR_AMP; channels[channel].instrument.xyz = 0; set_instrument(channel, channels[channel].chunk_ptr); channels[channel].chunk_ptr += 15; break; case 2: channels[channel].mode = 2; restart_note(channel); init_chunk2(channel, 0); init_chunk2(channel, 1); return; case 0x80: channels[channel].chunk_ptr += channels[channel].loop_offset; break; default: clear_freq(channel); channels[channel].mode = 0; if (channel >= 6) { channel = 6; } else { if (channel >= 3) channel = 3; else channel = 0; } if (!channels[channel].mode && !channels[channel+1].mode && !channels[channel+1].mode) { channels[channel].sound_nr = 0; inactivate_sound(channel); } return; } } } void Player_Adlib::next_command(int channel) { if (channels[channel].mode == 1) find_next_chunk(channel); else next_command2(channel); } void Player_Adlib::on_timer() { int i; if (--_tick_172 != 0) return; _tick_172 = 4; for (i = 0; i < 9; i++) { if (channels[i].mode != 0) { next_command(i); } } } void Player_Adlib::do_mix (int16 *data, int len) { mutex_up(); int step; do { step = len; if (step > _next_tick) step = _next_tick; YM3812UpdateOne(0, data, step); _next_tick -= step << FIXP_SHIFT; if (!(_next_tick >> FIXP_SHIFT)) { on_timer(); _next_tick += _tick_len; } data += step; len -= step; } while (len); mutex_down(); }