aboutsummaryrefslogtreecommitdiff
path: root/engines/cine/sound_driver.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2006-02-22 22:40:53 +0000
committerEugene Sandulenko2006-02-22 22:40:53 +0000
commit71c170bb136ab94d70eb10d55cfd897dc89c9682 (patch)
treed555cf8177ba4f6fe3fbc0b0fd1d5d45df58874e /engines/cine/sound_driver.cpp
parenta467247e6ed972e0a13bf26af07811dda55bd69a (diff)
downloadscummvm-rg350-71c170bb136ab94d70eb10d55cfd897dc89c9682.tar.gz
scummvm-rg350-71c170bb136ab94d70eb10d55cfd897dc89c9682.tar.bz2
scummvm-rg350-71c170bb136ab94d70eb10d55cfd897dc89c9682.zip
Initial version of Cinematique engine evo 1.
svn-id: r20813
Diffstat (limited to 'engines/cine/sound_driver.cpp')
-rw-r--r--engines/cine/sound_driver.cpp435
1 files changed, 435 insertions, 0 deletions
diff --git a/engines/cine/sound_driver.cpp b/engines/cine/sound_driver.cpp
new file mode 100644
index 0000000000..b027c76ec0
--- /dev/null
+++ b/engines/cine/sound_driver.cpp
@@ -0,0 +1,435 @@
+/* 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/sound_driver.h"
+
+#include "sound/mixer.h"
+#include "sound/fmopl.h"
+
+u8 snd_useAdlib = 0;
+u16 snd_fadeOutCounter = 0;
+u16 snd_songTicksCounter = 0;
+u8 *snd_adlibInstrumentsTable[4];
+sndDriverStruct snd_driver;
+
+static u8 snd_adlibVibrato = 0;
+static s16 snd_adlibChannelVolume[4];
+
+static const u16 snd_adlibFreqTable[] = {
+ 0x0157, 0x016C, 0x0181, 0x0198, 0x01B1, 0x01CB, 0x01E6, 0x0203,
+ 0x0222, 0x0243, 0x0266, 0x028A
+};
+
+static const u8 snd_adlibOpTable[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x08, 0x09, 0x0A,
+ 0x0B, 0x0C, 0x0D, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15
+};
+
+static const u8 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 s16 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 u8 *instrumentData, int channelNum) {
+ s16 tmp;
+
+ u8 waveSelect1 = instrumentData[54] & 3; /* var2 */
+ u8 waveSelect2 = instrumentData[56] & 3; /* var1 */
+
+ u8 fl = *instrumentData++; /* varB */
+ u8 ch = *instrumentData++; /* var4 */
+
+ u8 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 (readU16LE(instrumentData + 18) != 0)
+ tmp |= 0x80;
+ if (readU16LE(instrumentData + 20) != 0)
+ tmp |= 0x40;
+ if (readU16LE(instrumentData + 10) != 0)
+ tmp |= 0x20;
+ if (readU16LE(instrumentData + 22) != 0)
+ tmp |= 0x10;
+ tmp |= (readU16LE(instrumentData + 2) & 0xF);
+ snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_EG_KS + adlibOp1, tmp);
+
+ /* key scaling */
+ tmp = 0x3F - (readU16LE(instrumentData + 16) & 0x3F);
+ tmp = snd_adlibChannelVolume[channelNum] * tmp;
+ tmp += tmp + 0x7F;
+ tmp = 0x3F - (tmp / 0xFE);
+ if (readU16LE(instrumentData + 24) != 0)
+ tmp = readU16LE(instrumentData + 16) & 0x3F;
+ tmp |= readU16LE(instrumentData) << 6;
+ snd_adlibWriteData(ADLIB_REG_KEY_SCALING_OPERATOR_OUTPUT +
+ adlibOp1, tmp);
+
+ /* attack/decay rates */
+ tmp =
+ (readU16LE(instrumentData +
+ 6) << 4) | (readU16LE(instrumentData + 12) & 0xF);
+ snd_adlibWriteData(ADLIB_REG_ATTACK_RATE_DECAY_RATE + adlibOp1,
+ tmp);
+
+ /* sustain/release rates */
+ tmp =
+ (readU16LE(instrumentData +
+ 8) << 4) | (readU16LE(instrumentData + 14) & 0xF);
+ snd_adlibWriteData(ADLIB_REG_SUSTAIN_LEVEL_RELEASE_RATE_0 +
+ adlibOp1, tmp);
+
+ if (fl != 0) {
+ tmp = readU16LE(instrumentData + 4) * 2;
+ if (readU16LE(instrumentData + 24) == 0)
+ tmp |= 1;
+
+ snd_adlibWriteData
+ (ADLIB_REG_FEEDBACK_STRENGTH_CONNECTION_TYPE + ch,
+ tmp);
+ } else {
+ tmp = readU16LE(instrumentData + 4) * 2;
+ if (readU16LE(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 (readU16LE(instrumentData + 18) != 0)
+ tmp |= 0x80;
+ if (readU16LE(instrumentData + 20) != 0)
+ tmp |= 0x40;
+ if (readU16LE(instrumentData + 10) != 0)
+ tmp |= 0x20;
+ if (readU16LE(instrumentData + 22) != 0)
+ tmp |= 0x10;
+ tmp |= (readU16LE(instrumentData + 2) & 0xF);
+ snd_adlibWriteData(ADLIB_REG_AM_VIBRATO_EG_KS + adlibOp2, tmp);
+
+ /* key scaling */
+ tmp = 0x3F - (readU16LE(instrumentData + 16) & 0x3F);
+ tmp = snd_adlibChannelVolume[channelNum] * tmp;
+ tmp += tmp + 0x7F;
+ tmp = 0x3F - (tmp / 0xFE);
+ tmp |= readU16LE(instrumentData) << 6;
+ snd_adlibWriteData(ADLIB_REG_KEY_SCALING_OPERATOR_OUTPUT + adlibOp2,
+ tmp);
+
+ /* attack/decay rates */
+ tmp =
+ (readU16LE(instrumentData + 6) << 4) | (readU16LE(instrumentData +
+ 12) & 0xF);
+ snd_adlibWriteData(ADLIB_REG_ATTACK_RATE_DECAY_RATE + adlibOp2, tmp);
+
+ /* sustain/release rates */
+ tmp =
+ (readU16LE(instrumentData + 8) << 4) | (readU16LE(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, s16 *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 u8 *data,
+ int instrumentNum) {
+ s16 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 u8 *instr = snd_adlibInstrumentsTable[channelNum];
+ u8 fl = *instr++; /* var2 */
+ u8 ch = *instr++; /* var1 */
+
+ if (fl != 0 && ch == 6)
+ channelNum = 6;
+
+ if (fl == 0 || channelNum == 6) {
+ u16 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 u8 *instr = snd_adlibInstrumentsTable[channelNum];
+ u8 fl = *instr++; /* var2 */
+ u8 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(u8 * data, int channelNum, int volume) {
+/* if (_snd_mute) return;*/
+ u8 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) {
+ u16 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 sndDriverStruct 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);
+}