Initial commit
This commit is contained in:
667
engines/gob/sound/adlib.cpp
Normal file
667
engines/gob/sound/adlib.cpp
Normal file
@@ -0,0 +1,667 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* This file is dual-licensed.
|
||||
* In addition to the GPLv3 license mentioned above, this code is also
|
||||
* licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
|
||||
* full text of the license.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/config-manager.h"
|
||||
|
||||
#include "audio/fmopl.h"
|
||||
|
||||
#include "gob/gob.h"
|
||||
#include "gob/sound/adlib.h"
|
||||
|
||||
namespace Gob {
|
||||
|
||||
static const int kPitchTom = 24;
|
||||
static const int kPitchTomToSnare = 7;
|
||||
static const int kPitchSnareDrum = kPitchTom + kPitchTomToSnare;
|
||||
|
||||
|
||||
// Attenuation map for GUI volume slider
|
||||
// Note: no volume control in the original engine
|
||||
const uint8 AdLib::kVolumeTable[Audio::Mixer::kMaxMixerVolume + 1] = {
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 62, 61, 59, 57, 56, 55,
|
||||
53, 52, 51, 50, 49, 48, 47, 46, 46, 45, 44, 43, 43, 42, 41, 41,
|
||||
40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 33,
|
||||
32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27,
|
||||
27, 26, 26, 26, 26, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23,
|
||||
22, 22, 22, 22, 21, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19,
|
||||
19, 19, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 16, 16, 16,
|
||||
16, 16, 16, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 13,
|
||||
13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11,
|
||||
11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9,
|
||||
9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6,
|
||||
6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
||||
0
|
||||
};
|
||||
|
||||
// Is the operator a modulator (0) or a carrier (1)?
|
||||
const uint8 AdLib::kOperatorType[kOperatorCount] = {
|
||||
0, 0, 0, 1, 1, 1,
|
||||
0, 0, 0, 1, 1, 1,
|
||||
0, 0, 0, 1, 1, 1
|
||||
};
|
||||
|
||||
// Operator number to register offset on the OPL
|
||||
const uint8 AdLib::kOperatorOffset[kOperatorCount] = {
|
||||
0, 1, 2, 3, 4, 5,
|
||||
8, 9, 10, 11, 12, 13,
|
||||
16, 17, 18, 19, 20, 21
|
||||
};
|
||||
|
||||
// For each operator, the voice it belongs to
|
||||
const uint8 AdLib::kOperatorVoice[kOperatorCount] = {
|
||||
0, 1, 2,
|
||||
0, 1, 2,
|
||||
3, 4, 5,
|
||||
3, 4, 5,
|
||||
6, 7, 8,
|
||||
6, 7, 8,
|
||||
};
|
||||
|
||||
// Voice to operator set, for the 9 melodyvoices (only 6 useable in percussion mode)
|
||||
const uint8 AdLib::kVoiceMelodyOperator[kOperatorsPerVoice][kMelodyVoiceCount] = {
|
||||
{0, 1, 2, 6, 7, 8, 12, 13, 14},
|
||||
{3, 4, 5, 9, 10, 11, 15, 16, 17}
|
||||
};
|
||||
|
||||
// Voice to operator set, for the 5 percussion voices (only useable in percussion mode)
|
||||
const uint8 AdLib::kVoicePercussionOperator[kOperatorsPerVoice][kPercussionVoiceCount] = {
|
||||
{12, 16, 14, 17, 13},
|
||||
{15, 0, 0, 0, 0}
|
||||
};
|
||||
|
||||
// Mask bits to set each percussion instrument on/off
|
||||
const byte AdLib::kPercussionMasks[kPercussionVoiceCount] = {0x10, 0x08, 0x04, 0x02, 0x01};
|
||||
|
||||
// Default instrument presets
|
||||
const uint16 AdLib::kPianoParams [kOperatorsPerVoice][kParamCount] = {
|
||||
{ 1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0},
|
||||
{ 0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0} };
|
||||
const uint16 AdLib::kBaseDrumParams[kOperatorsPerVoice][kParamCount] = {
|
||||
{ 0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0 },
|
||||
{ 0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0 } };
|
||||
const uint16 AdLib::kSnareDrumParams[kParamCount] = {
|
||||
0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0 };
|
||||
const uint16 AdLib::kTomParams [kParamCount] = {
|
||||
0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 };
|
||||
const uint16 AdLib::kCymbalParams [kParamCount] = {
|
||||
0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0 };
|
||||
const uint16 AdLib::kHihatParams [kParamCount] = {
|
||||
0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
|
||||
AdLib::AdLib(int callbackFreq) : _opl(nullptr),
|
||||
_toPoll(0), _repCount(0), _first(true), _playing(false), _ended(true), _volume(0) {
|
||||
|
||||
initFreqs();
|
||||
|
||||
createOPL();
|
||||
initOPL();
|
||||
|
||||
syncVolume();
|
||||
|
||||
_opl->start(new Common::Functor0Mem<void, AdLib>(this, &AdLib::onTimer), callbackFreq);
|
||||
}
|
||||
|
||||
AdLib::~AdLib() {
|
||||
delete _opl;
|
||||
}
|
||||
|
||||
// Creates the OPL. Try to use the DOSBox emulator, unless that one is not compiled in,
|
||||
// or the user explicitly wants the MAME emulator. The MAME one is slightly buggy, leading
|
||||
// to some wrong sounds, especially noticeable in the title music of Gobliins 2, so we
|
||||
// really don't want to use it, if we can help it.
|
||||
void AdLib::createOPL() {
|
||||
Common::String oplDriver = ConfMan.get("opl_driver");
|
||||
|
||||
if (oplDriver.empty() || (oplDriver == "auto") || (OPL::Config::parse(oplDriver) == -1)) {
|
||||
// User has selected OPL driver auto detection or an invalid OPL driver.
|
||||
// Set it to our preferred driver (DOSBox), if we can.
|
||||
|
||||
if (OPL::Config::parse("db") <= 0) {
|
||||
warning("The DOSBox AdLib emulator is not compiled in. Please keep in mind that the MAME one is buggy");
|
||||
} else
|
||||
oplDriver = "db";
|
||||
|
||||
} else if (oplDriver == "mame") {
|
||||
// User has selected the MAME OPL driver. It is buggy, so warn the user about that.
|
||||
|
||||
warning("You have selected the MAME AdLib emulator. It is buggy; AdLib music might be slightly glitchy now");
|
||||
}
|
||||
|
||||
_opl = OPL::Config::create(OPL::Config::parse(oplDriver), OPL::Config::kOpl2);
|
||||
if (!_opl || !_opl->init()) {
|
||||
delete _opl;
|
||||
|
||||
error("Could not create an AdLib emulator");
|
||||
}
|
||||
}
|
||||
|
||||
void AdLib::onTimer() {
|
||||
Common::StackLock slock(_mutex);
|
||||
|
||||
// Nothing to do
|
||||
if (!_playing)
|
||||
return;
|
||||
|
||||
// Check if there's anything to do on this step
|
||||
// If not, decrease the poll number and move on
|
||||
if (_toPoll > 0) {
|
||||
_toPoll--;
|
||||
return;
|
||||
}
|
||||
|
||||
// Poll until we have to delay until the next poll
|
||||
while (_toPoll == 0 && _playing) {
|
||||
// Song ended, break out
|
||||
if (_ended) {
|
||||
_toPoll = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Poll more music
|
||||
_toPoll = pollMusic(_first);
|
||||
_first = false;
|
||||
}
|
||||
|
||||
// Song ended, loop if requested
|
||||
if (_ended) {
|
||||
_toPoll = 0;
|
||||
|
||||
// _repCount == 0: No looping (anymore); _repCount < 0: Infinite looping
|
||||
if (_repCount != 0) {
|
||||
if (_repCount > 0)
|
||||
_repCount--;
|
||||
|
||||
_first = true;
|
||||
_ended = false;
|
||||
|
||||
reset();
|
||||
rewind();
|
||||
} else
|
||||
_playing = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AdLib::isPlaying() const {
|
||||
return _playing;
|
||||
}
|
||||
|
||||
int32 AdLib::getRepeating() const {
|
||||
Common::StackLock slock(_mutex);
|
||||
|
||||
return _repCount;
|
||||
}
|
||||
|
||||
void AdLib::setRepeating(int32 repCount) {
|
||||
Common::StackLock slock(_mutex);
|
||||
|
||||
_repCount = repCount;
|
||||
}
|
||||
|
||||
void AdLib::startPlay() {
|
||||
Common::StackLock slock(_mutex);
|
||||
|
||||
_playing = true;
|
||||
_ended = false;
|
||||
_first = true;
|
||||
|
||||
reset();
|
||||
rewind();
|
||||
}
|
||||
|
||||
void AdLib::stopPlay() {
|
||||
Common::StackLock slock(_mutex);
|
||||
|
||||
end(true);
|
||||
|
||||
_playing = false;
|
||||
}
|
||||
|
||||
void AdLib::writeOPL(byte reg, byte val) {
|
||||
debugC(6, kDebugSound, "AdLib::writeOPL (%02X, %02X)", reg, val);
|
||||
|
||||
_opl->writeReg(reg, val);
|
||||
}
|
||||
|
||||
void AdLib::reset() {
|
||||
allOff();
|
||||
initOPL();
|
||||
}
|
||||
|
||||
void AdLib::allOff() {
|
||||
// NOTE: Explicit casts are necessary, because of 5.16 paragraph 4 of the C++ standard
|
||||
int numVoices = isPercussionMode() ? (int)kMaxVoiceCount : (int)kMelodyVoiceCount;
|
||||
|
||||
for (int i = 0; i < numVoices; i++)
|
||||
noteOff(i);
|
||||
}
|
||||
|
||||
void AdLib::end(bool killRepeat) {
|
||||
reset();
|
||||
|
||||
_ended = true;
|
||||
|
||||
if (killRepeat)
|
||||
_repCount = 0;
|
||||
}
|
||||
|
||||
void AdLib::initOPL() {
|
||||
_tremoloDepth = false;
|
||||
_vibratoDepth = false;
|
||||
_keySplit = false;
|
||||
|
||||
_enableWaveSelect = true;
|
||||
|
||||
for (int i = 0; i < kMaxVoiceCount; i++) {
|
||||
_voiceNote[i] = 0;
|
||||
_voiceOn [i] = 0;
|
||||
}
|
||||
|
||||
/* NOTE: We used to completely reset the OPL here, via _opl->reset(). However,
|
||||
* with the OPL timer change in 73e8ac2a, reset() must not be called while
|
||||
* the callback is still active. With the Gob AdLib rewrite in 03ef6689,
|
||||
* this reset shouldn't be necessary anymore either, since this function
|
||||
* here cleans everything properly anyway. If suddenly a certain piece of
|
||||
* music in a Gob game sounds weird, we need to re-examine that. */
|
||||
|
||||
initOperatorVolumes();
|
||||
resetFreqs();
|
||||
|
||||
setPercussionMode(false);
|
||||
|
||||
setTremoloDepth(false);
|
||||
setVibratoDepth(false);
|
||||
setKeySplit(false);
|
||||
|
||||
for(int i = 0; i < kMelodyVoiceCount; i++)
|
||||
voiceOff(i);
|
||||
|
||||
setPitchRange(1);
|
||||
|
||||
enableWaveSelect(true);
|
||||
}
|
||||
|
||||
bool AdLib::isPercussionMode() const {
|
||||
return _percussionMode;
|
||||
}
|
||||
|
||||
void AdLib::setPercussionMode(bool percussion) {
|
||||
if (percussion) {
|
||||
voiceOff(kVoiceBaseDrum);
|
||||
voiceOff(kVoiceSnareDrum);
|
||||
voiceOff(kVoiceTom);
|
||||
|
||||
/* set the frequency for the last 4 percussion voices: */
|
||||
setFreq(kVoiceTom, kPitchTom, 0);
|
||||
setFreq(kVoiceSnareDrum, kPitchSnareDrum, 0);
|
||||
}
|
||||
|
||||
_percussionMode = percussion;
|
||||
_percussionBits = 0;
|
||||
|
||||
initOperatorParams();
|
||||
writeTremoloVibratoDepthPercMode();
|
||||
}
|
||||
|
||||
void AdLib::enableWaveSelect(bool enable) {
|
||||
_enableWaveSelect = enable;
|
||||
|
||||
for (int i = 0; i < kOperatorCount; i++)
|
||||
writeOPL(0xE0 + kOperatorOffset[i], 0);
|
||||
|
||||
writeOPL(0x011, _enableWaveSelect ? 0x20 : 0);
|
||||
}
|
||||
|
||||
void AdLib::setPitchRange(uint8 range) {
|
||||
_pitchRange = CLIP<uint8>(range, 0, 12);
|
||||
_pitchRangeStep = _pitchRange * kPitchStepCount;
|
||||
}
|
||||
|
||||
void AdLib::setTremoloDepth(bool tremoloDepth) {
|
||||
_tremoloDepth = tremoloDepth;
|
||||
|
||||
writeTremoloVibratoDepthPercMode();
|
||||
}
|
||||
|
||||
void AdLib::setVibratoDepth(bool vibratoDepth) {
|
||||
_vibratoDepth = vibratoDepth;
|
||||
|
||||
writeTremoloVibratoDepthPercMode();
|
||||
}
|
||||
|
||||
void AdLib::setKeySplit(bool keySplit) {
|
||||
_keySplit = keySplit;
|
||||
|
||||
writeKeySplit();
|
||||
}
|
||||
|
||||
void AdLib::setVoiceTimbre(uint8 voice, const uint16 *params) {
|
||||
const uint16 *params0 = params;
|
||||
const uint16 *params1 = params + kParamCount - 1;
|
||||
const uint16 *waves = params + 2 * (kParamCount - 1);
|
||||
|
||||
const int voicePerc = voice - kVoiceBaseDrum;
|
||||
|
||||
if (!isPercussionMode() || (voice < kVoiceBaseDrum)) {
|
||||
if (voice < kMelodyVoiceCount) {
|
||||
setOperatorParams(kVoiceMelodyOperator[0][voice], params0, waves[0]);
|
||||
setOperatorParams(kVoiceMelodyOperator[1][voice], params1, waves[1]);
|
||||
}
|
||||
} else if (voice == kVoiceBaseDrum) {
|
||||
setOperatorParams(kVoicePercussionOperator[0][voicePerc], params0, waves[0]);
|
||||
setOperatorParams(kVoicePercussionOperator[1][voicePerc], params1, waves[1]);
|
||||
} else {
|
||||
setOperatorParams(kVoicePercussionOperator[0][voicePerc], params0, waves[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void AdLib::setVoiceVolume(uint8 voice, uint8 volume) {
|
||||
int oper;
|
||||
|
||||
const int voicePerc = voice - kVoiceBaseDrum;
|
||||
|
||||
if (!isPercussionMode() || (voice < kVoiceBaseDrum))
|
||||
oper = kVoiceMelodyOperator[1][ voice];
|
||||
else
|
||||
oper = kVoicePercussionOperator[voice == kVoiceBaseDrum ? 1 : 0][voicePerc];
|
||||
|
||||
_operatorVolume[oper] = MIN<uint8>(volume, kMaxVolume);
|
||||
writeKeyScaleLevelVolume(oper);
|
||||
}
|
||||
|
||||
void AdLib::bendVoicePitch(uint8 voice, uint16 pitchBend) {
|
||||
if (isPercussionMode() && (voice > kVoiceBaseDrum))
|
||||
return;
|
||||
|
||||
changePitch(voice, MIN<uint16>(pitchBend, kMaxPitch));
|
||||
setFreq(voice, _voiceNote[voice], _voiceOn[voice]);
|
||||
}
|
||||
|
||||
void AdLib::noteOn(uint8 voice, uint8 note) {
|
||||
note = MAX<int>(0, note - (kStandardMidC - kOPLMidC));
|
||||
|
||||
if (isPercussionMode() && (voice >= kVoiceBaseDrum)) {
|
||||
|
||||
if (voice == kVoiceBaseDrum) {
|
||||
setFreq(kVoiceBaseDrum , note , false);
|
||||
} else if (voice == kVoiceTom) {
|
||||
setFreq(kVoiceTom , note , false);
|
||||
setFreq(kVoiceSnareDrum, note + kPitchTomToSnare, false);
|
||||
}
|
||||
|
||||
_percussionBits |= kPercussionMasks[voice - kVoiceBaseDrum];
|
||||
writeTremoloVibratoDepthPercMode();
|
||||
|
||||
} else
|
||||
setFreq(voice, note, true);
|
||||
}
|
||||
|
||||
void AdLib::noteOff(uint8 voice) {
|
||||
if (isPercussionMode() && (voice >= kVoiceBaseDrum)) {
|
||||
_percussionBits &= ~kPercussionMasks[voice - kVoiceBaseDrum];
|
||||
writeTremoloVibratoDepthPercMode();
|
||||
} else
|
||||
setFreq(voice, _voiceNote[voice], false);
|
||||
}
|
||||
|
||||
void AdLib::writeKeyScaleLevelVolume(uint8 oper) {
|
||||
uint16 volume = 0;
|
||||
|
||||
volume = (63 - (_operatorParams[oper][kParamLevel] & 0x3F)) * _operatorVolume[oper];
|
||||
volume = 63 - ((2 * volume + kMaxVolume) / (2 * kMaxVolume));
|
||||
|
||||
// Adjust carriers for GUI volume slider
|
||||
if (kOperatorType[oper] == 1) {
|
||||
volume += kVolumeTable[_volume];
|
||||
if (volume > 63)
|
||||
volume = 63;
|
||||
}
|
||||
|
||||
uint8 keyScale = _operatorParams[oper][kParamKeyScaleLevel] << 6;
|
||||
|
||||
writeOPL(0x40 + kOperatorOffset[oper], volume | keyScale);
|
||||
}
|
||||
|
||||
void AdLib::writeKeySplit() {
|
||||
writeOPL(0x08, _keySplit ? 0x40 : 0);
|
||||
}
|
||||
|
||||
void AdLib::writeFeedbackFM(uint8 oper) {
|
||||
if (kOperatorType[oper] == 1)
|
||||
return;
|
||||
|
||||
uint8 value = 0;
|
||||
|
||||
value |= _operatorParams[oper][kParamFeedback] << 1;
|
||||
value |= _operatorParams[oper][kParamFM] ? 0 : 1;
|
||||
|
||||
writeOPL(0xC0 + kOperatorVoice[oper], value);
|
||||
}
|
||||
|
||||
void AdLib::writeAttackDecay(uint8 oper) {
|
||||
uint8 value = 0;
|
||||
|
||||
value |= _operatorParams[oper][kParamAttack] << 4;
|
||||
value |= _operatorParams[oper][kParamDecay] & 0x0F;
|
||||
|
||||
writeOPL(0x60 + kOperatorOffset[oper], value);
|
||||
}
|
||||
|
||||
void AdLib::writeSustainRelease(uint8 oper) {
|
||||
uint8 value = 0;
|
||||
|
||||
value |= _operatorParams[oper][kParamSustain] << 4;
|
||||
value |= _operatorParams[oper][kParamRelease] & 0x0F;
|
||||
|
||||
writeOPL(0x80 + kOperatorOffset[oper], value);
|
||||
}
|
||||
|
||||
void AdLib::writeTremoloVibratoSustainingKeyScaleRateFreqMulti(uint8 oper) {
|
||||
uint8 value = 0;
|
||||
|
||||
value |= _operatorParams[oper][kParamAM] ? 0x80 : 0;
|
||||
value |= _operatorParams[oper][kParamVib] ? 0x40 : 0;
|
||||
value |= _operatorParams[oper][kParamSustaining] ? 0x20 : 0;
|
||||
value |= _operatorParams[oper][kParamKeyScaleRate] ? 0x10 : 0;
|
||||
value |= _operatorParams[oper][kParamFreqMulti] & 0x0F;
|
||||
|
||||
writeOPL(0x20 + kOperatorOffset[oper], value);
|
||||
}
|
||||
|
||||
void AdLib::writeTremoloVibratoDepthPercMode() {
|
||||
uint8 value = 0;
|
||||
|
||||
value |= _tremoloDepth ? 0x80 : 0;
|
||||
value |= _vibratoDepth ? 0x40 : 0;
|
||||
value |= isPercussionMode() ? 0x20 : 0;
|
||||
value |= _percussionBits;
|
||||
|
||||
writeOPL(0xBD, value);
|
||||
}
|
||||
|
||||
void AdLib::writeWaveSelect(uint8 oper) {
|
||||
uint8 wave = 0;
|
||||
if (_enableWaveSelect)
|
||||
wave = _operatorParams[oper][kParamWaveSelect] & 0x03;
|
||||
|
||||
writeOPL(0xE0 + kOperatorOffset[ oper], wave);
|
||||
}
|
||||
|
||||
void AdLib::writeAllParams(uint8 oper) {
|
||||
writeTremoloVibratoDepthPercMode();
|
||||
writeKeySplit();
|
||||
writeKeyScaleLevelVolume(oper);
|
||||
writeFeedbackFM(oper);
|
||||
writeAttackDecay(oper);
|
||||
writeSustainRelease(oper);
|
||||
writeTremoloVibratoSustainingKeyScaleRateFreqMulti(oper);
|
||||
writeWaveSelect(oper);
|
||||
}
|
||||
|
||||
void AdLib::initOperatorParams() {
|
||||
for (int i = 0; i < kOperatorCount; i++)
|
||||
setOperatorParams(i, kPianoParams[kOperatorType[i]], kPianoParams[kOperatorType[i]][kParamCount - 1]);
|
||||
|
||||
if (isPercussionMode()) {
|
||||
setOperatorParams(12, kBaseDrumParams [0], kBaseDrumParams [0][kParamCount - 1]);
|
||||
setOperatorParams(15, kBaseDrumParams [1], kBaseDrumParams [1][kParamCount - 1]);
|
||||
setOperatorParams(16, kSnareDrumParams , kSnareDrumParams [kParamCount - 1]);
|
||||
setOperatorParams(14, kTomParams , kTomParams [kParamCount - 1]);
|
||||
setOperatorParams(17, kCymbalParams , kCymbalParams [kParamCount - 1]);
|
||||
setOperatorParams(13, kHihatParams , kHihatParams [kParamCount - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
void AdLib::initOperatorVolumes() {
|
||||
for(int i = 0; i < kOperatorCount; i++)
|
||||
_operatorVolume[i] = kMaxVolume;
|
||||
}
|
||||
|
||||
void AdLib::setOperatorParams(uint8 oper, const uint16 *params, uint8 wave) {
|
||||
byte *operParams = _operatorParams[oper];
|
||||
|
||||
for (int i = 0; i < (kParamCount - 1); i++)
|
||||
operParams[i] = params[i];
|
||||
|
||||
operParams[kParamCount - 1] = wave & 0x03;
|
||||
|
||||
writeAllParams(oper);
|
||||
}
|
||||
|
||||
void AdLib::voiceOff(uint8 voice) {
|
||||
writeOPL(0xA0 + voice, 0);
|
||||
writeOPL(0xB0 + voice, 0);
|
||||
}
|
||||
|
||||
int32 AdLib::calcFreq(int32 deltaDemiToneNum, int32 deltaDemiToneDenom) {
|
||||
int32 freq = 0;
|
||||
|
||||
freq = ((deltaDemiToneDenom * 100) + 6 * deltaDemiToneNum) * 52088;
|
||||
freq /= deltaDemiToneDenom * 2500;
|
||||
|
||||
return (freq * 147456) / 111875;
|
||||
}
|
||||
|
||||
void AdLib::setFreqs(uint16 *freqs, int32 num, int32 denom) {
|
||||
int32 val = calcFreq(num, denom);
|
||||
|
||||
*freqs++ = (4 + val) >> 3;
|
||||
|
||||
for (int i = 1; i < kHalfToneCount; i++) {
|
||||
val = (val * 106) / 100;
|
||||
|
||||
*freqs++ = (4 + val) >> 3;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLib::initFreqs() {
|
||||
const int numStep = 100 / kPitchStepCount;
|
||||
|
||||
for (int i = 0; i < kPitchStepCount; i++)
|
||||
setFreqs(_freqs[i], i * numStep, 100);
|
||||
|
||||
resetFreqs();
|
||||
}
|
||||
|
||||
void AdLib::resetFreqs() {
|
||||
for (int i = 0; i < kMaxVoiceCount; i++) {
|
||||
_freqPtr [i] = _freqs[0];
|
||||
_halfToneOffset[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void AdLib::changePitch(uint8 voice, uint16 pitchBend) {
|
||||
|
||||
int full = 0;
|
||||
int frac = 0;
|
||||
int amount = ((pitchBend - kMidPitch) * _pitchRangeStep) / kMidPitch;
|
||||
|
||||
if (amount >= 0) {
|
||||
// Bend up
|
||||
|
||||
full = amount / kPitchStepCount;
|
||||
frac = amount % kPitchStepCount;
|
||||
|
||||
} else {
|
||||
// Bend down
|
||||
|
||||
amount = kPitchStepCount - 1 - amount;
|
||||
|
||||
full = -(amount / kPitchStepCount);
|
||||
frac = (amount - kPitchStepCount + 1) % kPitchStepCount;
|
||||
if (frac)
|
||||
frac = kPitchStepCount - frac;
|
||||
|
||||
}
|
||||
|
||||
_halfToneOffset[voice] = full;
|
||||
_freqPtr [voice] = _freqs[frac];
|
||||
}
|
||||
|
||||
void AdLib::setFreq(uint8 voice, uint16 note, bool on) {
|
||||
_voiceOn [voice] = on;
|
||||
_voiceNote[voice] = note;
|
||||
|
||||
note = CLIP<int>(note + _halfToneOffset[voice], 0, kNoteCount - 1);
|
||||
|
||||
uint16 freq = _freqPtr[voice][note % kHalfToneCount];
|
||||
|
||||
uint8 value = 0;
|
||||
value |= on ? 0x20 : 0;
|
||||
value |= ((note / kHalfToneCount) << 2) | ((freq >> 8) & 0x03);
|
||||
|
||||
writeOPL(0xA0 + voice, freq);
|
||||
writeOPL(0xB0 + voice, value);
|
||||
}
|
||||
|
||||
void AdLib::setTimerFrequency(int timerFrequency) {
|
||||
_opl->setCallbackFrequency(timerFrequency);
|
||||
}
|
||||
|
||||
void AdLib::syncVolume() {
|
||||
Common::StackLock slock(_mutex);
|
||||
|
||||
bool mute = false;
|
||||
if (ConfMan.hasKey("mute"))
|
||||
mute = ConfMan.getBool("mute");
|
||||
|
||||
_volume = (mute ? 0 : ConfMan.getInt("music_volume"));
|
||||
|
||||
if (_playing) {
|
||||
for(int i = 0; i < kOperatorCount; i++)
|
||||
writeKeyScaleLevelVolume(i);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Gob
|
||||
Reference in New Issue
Block a user