mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 22:58:09 +00:00
9c80770411
svn-id: r20957
406 lines
12 KiB
C++
406 lines
12 KiB
C++
/* 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
|