/* ScummVM - Scumm Interpreter * Copyright (C) 2006 The ScummVM project * * cinE Engine is (C) 2004-2005 by CinE Team * * 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 "cine/cine.h" #include "cine/sfx_player.h" #include "cine/sound_driver.h" #include "sound/mixer.h" #include "sound/fmopl.h" namespace Cine { extern AdlibMusic *g_cine_adlib; uint8 snd_useAdlib = 0; uint16 snd_fadeOutCounter = 0; uint16 snd_songTicksCounter = 0; uint8 *snd_adlibInstrumentsTable[4]; SoundDriver snd_driver; static uint8 snd_adlibVibrato = 0; static int16 snd_adlibChannelVolume[4]; static const uint16 snd_adlibFreqTable[] = { 0x0157, 0x016C, 0x0181, 0x0198, 0x01B1, 0x01CB, 0x01E6, 0x0203, 0x0222, 0x0243, 0x0266, 0x028A }; static const uint8 snd_adlibOpTable[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15 }; static const uint8 snd_adlibNoteTable[] = { 0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x06, 0x09, 0x07, 0x0A, 0x08, 0x0B, 0x0C, 0x0F, 0x10, 0x10, 0x0E, 0x0E, 0x11, 0x11, 0x0D, 0x0D, 0x00, 0x00 }; static const int16 snd_adlibNoteFreqTable[] = { 0x0EEE, 0x0E17, 0x0D4D, 0x0C8C, 0x0BD9, 0x0B2F, 0x0A8E, 0x09F7, 0x0967, 0x08E0, 0x0861, 0x07E8, 0x0777, 0x070B, 0x06A6, 0x0647, 0x05EC, 0x0597, 0x0547, 0x04FB, 0x04B3, 0x0470, 0x0430, 0x03F4, 0x03BB, 0x0385, 0x0353, 0x0323, 0x02F6, 0x02CB, 0x02A3, 0x027D, 0x0259, 0x0238, 0x0218, 0x01FA, 0x01DD, 0x01C2, 0x01A9, 0x0191, 0x017B, 0x0165, 0x0151, 0x013E, 0x012C, 0x011C, 0x010C, 0x00FD, 0x00EE, 0x00E1, 0x00D4, 0x00C8, 0x00BD, 0x00B2, 0x00A8, 0x009F, 0x0096, 0x008E, 0x0086, 0x007E, 0x0077, 0x0070, 0x006A, 0x0064, 0x005E, 0x0059, 0x0054, 0x004F, 0x004B, 0x0047, 0x0043, 0x003F, 0x003B, 0x0038, 0x0035, 0x0032, 0x002F, 0x002C, 0x002A, 0x0027, 0x0025, 0x0023, 0x0021, 0x001F, 0x001D, 0x001C, 0x001A, 0x0019, 0x0017, 0x0016, 0x0015, 0x0013, 0x0012, 0x0011, 0x0010, 0x000F }; static void snd_adlibWriteData(int port, int value) { OPLWriteReg(g_cine_adlib->getOPL(), port, value); } static void snd_adlibDriverSetupInstrument(const uint8 *instrumentData, int channelNum) { int16 tmp; uint8 waveSelect1 = instrumentData[54] & 3; /* var2 */ uint8 waveSelect2 = instrumentData[56] & 3; /* var1 */ uint8 fl = *instrumentData++; /* varB */ uint8 ch = *instrumentData++; /* var4 */ uint8 adlibOp1, adlibOp2; /* _di, varA */ if (fl != 0) { adlibOp1 = snd_adlibOpTable[snd_adlibNoteTable[ch * 2 + 0]]; adlibOp2 = snd_adlibOpTable[snd_adlibNoteTable[ch * 2 + 1]]; } else { adlibOp1 = snd_adlibOpTable[snd_adlibNoteTable[channelNum * 2 + 0]]; adlibOp2 = snd_adlibOpTable[snd_adlibNoteTable[channelNum * 2 + 1]]; } if (fl == 0 || ch == 6) { // vibrato tmp = 0; if (READ_LE_UINT16(instrumentData + 18) != 0) tmp |= 0x80; if (READ_LE_UINT16(instrumentData + 20) != 0) tmp |= 0x40; if (READ_LE_UINT16(instrumentData + 10) != 0) tmp |= 0x20; if (READ_LE_UINT16(instrumentData + 22) != 0) tmp |= 0x10; tmp |= (READ_LE_UINT16(instrumentData + 2) & 0xF); snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_EG_KS + adlibOp1, tmp); // key scaling tmp = 0x3F - (READ_LE_UINT16(instrumentData + 16) & 0x3F); tmp = snd_adlibChannelVolume[channelNum] * tmp; tmp += tmp + 0x7F; tmp = 0x3F - (tmp / 0xFE); if (READ_LE_UINT16(instrumentData + 24) != 0) tmp = READ_LE_UINT16(instrumentData + 16) & 0x3F; tmp |= READ_LE_UINT16(instrumentData) << 6; snd_adlibWriteData(ADLIB_REG_KEY_SCALING_OPERATOR_OUTPUT + adlibOp1, tmp); // attack/decay rates tmp = (READ_LE_UINT16(instrumentData + 6) << 4) | (READ_LE_UINT16(instrumentData + 12) & 0xF); snd_adlibWriteData(ADLIB_REG_ATTACK_RATE_DECAY_RATE + adlibOp1, tmp); // sustain/release rates tmp = (READ_LE_UINT16(instrumentData + 8) << 4) | (READ_LE_UINT16(instrumentData + 14) & 0xF); snd_adlibWriteData(ADLIB_REG_SUSTAIN_LEVEL_RELEASE_RATE_0 + adlibOp1, tmp); if (fl != 0) { tmp = READ_LE_UINT16(instrumentData + 4) * 2; if (READ_LE_UINT16(instrumentData + 24) == 0) tmp |= 1; snd_adlibWriteData(ADLIB_REG_FEEDBACK_STRENGTH_CONNECTION_TYPE + ch, tmp); } else { tmp = READ_LE_UINT16(instrumentData + 4) * 2; if (READ_LE_UINT16(instrumentData + 24) == 0) tmp |= 1; snd_adlibWriteData(ADLIB_REG_FEEDBACK_STRENGTH_CONNECTION_TYPE + channelNum, tmp); } snd_adlibWriteData(ADLIB_REG_WAVE_SELECT + adlibOp1, waveSelect1); instrumentData += 26; } // vibrato tmp = 0; if (READ_LE_UINT16(instrumentData + 18) != 0) tmp |= 0x80; if (READ_LE_UINT16(instrumentData + 20) != 0) tmp |= 0x40; if (READ_LE_UINT16(instrumentData + 10) != 0) tmp |= 0x20; if (READ_LE_UINT16(instrumentData + 22) != 0) tmp |= 0x10; tmp |= (READ_LE_UINT16(instrumentData + 2) & 0xF); snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_EG_KS + adlibOp2, tmp); // key scaling tmp = 0x3F - (READ_LE_UINT16(instrumentData + 16) & 0x3F); tmp = snd_adlibChannelVolume[channelNum] * tmp; tmp += tmp + 0x7F; tmp = 0x3F - (tmp / 0xFE); tmp |= READ_LE_UINT16(instrumentData) << 6; snd_adlibWriteData(ADLIB_REG_KEY_SCALING_OPERATOR_OUTPUT + adlibOp2, tmp); // attack/decay rates */ tmp =(READ_LE_UINT16(instrumentData + 6) << 4) | (READ_LE_UINT16(instrumentData + 12) & 0xF); snd_adlibWriteData(ADLIB_REG_ATTACK_RATE_DECAY_RATE + adlibOp2, tmp); // sustain/release rates */ tmp = (READ_LE_UINT16(instrumentData + 8) << 4) | (READ_LE_UINT16(instrumentData + 14) & 0xF); snd_adlibWriteData(ADLIB_REG_SUSTAIN_LEVEL_RELEASE_RATE_0 + adlibOp2, tmp); snd_adlibWriteData(ADLIB_REG_WAVE_SELECT + adlibOp2, waveSelect2); } static void snd_adlibInterrupt(void *param, int16 *buf, int len) { int16 *origData = buf; uint origLen = len; static int samplesLeft = 0; while (len != 0) { int count; if (samplesLeft == 0) { if (snd_songIsPlaying || (snd_fadeOutCounter != 0 && snd_fadeOutCounter < 100)) { ++snd_songTicksCounter; if (snd_songTicksCounter > snd_eventsDelay) { snd_handleEvents(); snd_songTicksCounter = 0; } } samplesLeft = g_cine_adlib->getRate() / 50; } count = samplesLeft; if (count > len) count = len; YM3812UpdateOne(g_cine_adlib->getOPL(), buf, count); samplesLeft -= count; len -= count; buf += count; } // Convert mono data to stereo for (int i = (origLen - 1); i >= 0; i--) { origData[2 * i] = origData[2 * i + 1] = origData[i]; } } static void snd_adlibDriverSetupChannel(int channelNum, const uint8 *data, int instrumentNum) { int16 vol = snd_sfxState.songData[instrumentNum]; if (vol != 0 && vol < 0x50) vol = 0x50; vol -= snd_fadeOutCounter; if (vol < 0) vol = 0; vol += vol / 4; if (vol > 0x7F) vol = 0x7F; snd_adlibChannelVolume[channelNum] = vol; snd_adlibDriverSetupInstrument(data, channelNum); } static void snd_getAdlibFrequency(int frequency, int *adlibFreq) { int i; *adlibFreq = 95; for (i = 0; i < 96; ++i) { if (snd_adlibNoteFreqTable[i] <= frequency) { *adlibFreq = i; break; } } } static void snd_adlibDriverSetChannelFrequency(int channelNum, int frequency) { const uint8 *instr = snd_adlibInstrumentsTable[channelNum]; uint8 fl = *instr++; // var2 uint8 ch = *instr++; // var1 if (fl != 0 && ch == 6) channelNum = 6; if (fl == 0 || channelNum == 6) { uint16 freqLow, freqHigh; // var8 int adlibFreq; snd_getAdlibFrequency(frequency, &adlibFreq); if (channelNum == 6) adlibFreq %= 12; freqLow = snd_adlibFreqTable[adlibFreq % 12]; snd_adlibWriteData(ADLIB_REG_FREQUENCY_0 + channelNum, freqLow); freqHigh = ((adlibFreq / 12) << 2) | ((freqLow & 0x300) >> 8); if (fl == 0) freqHigh |= 0x20; snd_adlibWriteData(ADLIB_REG_KEY_ON_OCTAVE_FREQUENCY_0 + channelNum, freqHigh); } if (fl != 0) { snd_adlibVibrato |= 1 << (10 - ch); snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_RHYTHM, snd_adlibVibrato); } } static void snd_adlibDriverStopChannel(int channelNum) { const uint8 *instr = snd_adlibInstrumentsTable[channelNum]; uint8 fl = *instr++; // var2 uint8 ch = *instr++; // var1 if (fl != 0 && ch == 6) channelNum = 6; if (fl == 0 || channelNum == 6) snd_adlibWriteData(ADLIB_REG_KEY_ON_OCTAVE_FREQUENCY_0 + channelNum, 0); if (fl != 0) { snd_adlibVibrato &= (1 << (10 - ch)) ^ 0xFF; snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_RHYTHM, snd_adlibVibrato); } } static void snd_adlibDriverPlaySound(uint8 * data, int channelNum, int volume) { // if (_snd_mute) return; uint8 fl, ch; // var2, var1 assert(channelNum < 4); data += 257; snd_adlibInstrumentsTable[channelNum] = data; snd_resetChannel(channelNum); snd_adlibChannelVolume[channelNum] = 0x7F; snd_adlibDriverSetupInstrument(data, channelNum); fl = *data++; ch = *data++; if (fl != 0 && ch == 6) channelNum = 6; if (fl == 0 || channelNum == 6) { uint16 freqLow, freqHigh; freqLow = snd_adlibFreqTable[0]; snd_adlibWriteData(ADLIB_REG_FREQUENCY_0 + channelNum, freqLow); freqHigh = 4 | ((freqLow & 0x300) >> 8); if (fl == 0) freqHigh |= 0x20; snd_adlibWriteData(ADLIB_REG_KEY_ON_OCTAVE_FREQUENCY_0 + channelNum, freqHigh); } if (fl != 0) { snd_adlibVibrato = 1 << (10 - ch); snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_RHYTHM, snd_adlibVibrato); } } static SoundDriver snd_adlibDriver = { &snd_adlibDriverSetupChannel, &snd_adlibDriverSetChannelFrequency, &snd_adlibDriverStopChannel, &snd_adlibDriverPlaySound }; void snd_adlibDriverStopSong() { int i; for (i = 0; i < 18; ++i) snd_adlibWriteData(ADLIB_REG_KEY_SCALING_OPERATOR_OUTPUT + snd_adlibOpTable[i], 0x3F); for (i = 0; i < 9; ++i) snd_adlibWriteData(ADLIB_REG_KEY_ON_OCTAVE_FREQUENCY_0 + i, 0); snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_RHYTHM, 0); } void snd_resetChannel(int channelNum) { (*snd_driver.stopChannel) (channelNum); if (snd_useAdlib) snd_adlibDriverStopSong(); } AdlibMusic::AdlibMusic(Audio::Mixer *pMixer) { _mixer = pMixer; _sampleRate = pMixer->getOutputRate(); g_cine_adlib = this; _opl = makeAdlibOPL(_sampleRate); snd_adlibVibrato = 0x20; snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_RHYTHM, snd_adlibVibrato); snd_adlibWriteData(0x08, 0x40); int i; for (i = 0; i < 18; ++i) snd_adlibWriteData(ADLIB_REG_KEY_SCALING_OPERATOR_OUTPUT + snd_adlibOpTable[i], 0); for (i = 0; i < 9; ++i) snd_adlibWriteData(ADLIB_REG_KEY_ON_OCTAVE_FREQUENCY_0 + i, 0); for (i = 0; i < 9; ++i) snd_adlibWriteData(ADLIB_REG_FEEDBACK_STRENGTH_CONNECTION_TYPE + i, 0); for (i = 0; i < 18; ++i) snd_adlibWriteData(ADLIB_REG_ATTACK_RATE_DECAY_RATE + snd_adlibOpTable[i], 0); for (i = 0; i < 18; ++i) snd_adlibWriteData(ADLIB_REG_SUSTAIN_LEVEL_RELEASE_RATE_0 + snd_adlibOpTable[i], 0); for (i = 0; i < 18; ++i) snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_EG_KS + snd_adlibOpTable[i], 0); for (i = 0; i < 18; ++i) snd_adlibWriteData(ADLIB_REG_WAVE_SELECT + snd_adlibOpTable[i], 0); snd_adlibWriteData(1, 0x20); snd_adlibWriteData(1, 0); for (i = 0; i < 4; ++i) snd_adlibInstrumentsTable[i] = snd_nullInstrument; snd_useAdlib = 1; snd_driver = snd_adlibDriver; _mixer->setupPremix(this); } void AdlibMusic::premixerCall(int16 *data, uint len) { snd_adlibInterrupt(NULL, data, len); } void AdlibMusic::setVolume(uint8 volume) { for (int i = 0; i < 4; ++i) snd_adlibChannelVolume[i] = volume | 128; } AdlibMusic::~AdlibMusic(void) { _mixer->setupPremix(NULL); } } // End of namespace Cine