2010-09-17 20:03:20 +00:00
|
|
|
/* 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 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2010-09-17 20:03:20 +00:00
|
|
|
* 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.
|
2014-02-18 02:34:24 +01:00
|
|
|
*
|
2010-09-17 20:03:20 +00:00
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sci/sound/drivers/mididriver.h"
|
|
|
|
|
2011-02-09 01:09:01 +00:00
|
|
|
#include "audio/softsynth/emumidi.h"
|
|
|
|
#include "audio/softsynth/cms.h"
|
|
|
|
#include "audio/mixer.h"
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2011-04-24 11:34:27 +03:00
|
|
|
#include "common/system.h"
|
|
|
|
|
2010-09-17 20:03:20 +00:00
|
|
|
#include "sci/resource.h"
|
2016-12-31 20:39:57 -06:00
|
|
|
#include "sci/util.h"
|
2010-09-17 20:03:20 +00:00
|
|
|
|
|
|
|
namespace Sci {
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
// FIXME: We don't seem to be sending the polyphony init data, so disable this for now
|
|
|
|
#define CMS_DISABLE_VOICE_MAPPING
|
2010-09-24 00:38:18 +00:00
|
|
|
|
2010-09-17 20:03:20 +00:00
|
|
|
class MidiDriver_CMS : public MidiDriver_Emulated {
|
|
|
|
public:
|
2019-06-21 14:18:03 +02:00
|
|
|
MidiDriver_CMS(Audio::Mixer *mixer, ResourceManager *resMan)
|
|
|
|
: MidiDriver_Emulated(mixer), _resMan(resMan), _cms(0), _rate(0), _playSwitch(true), _masterVolume(0) {
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
|
|
|
int open();
|
|
|
|
void close();
|
|
|
|
|
|
|
|
void send(uint32 b);
|
|
|
|
uint32 property(int prop, uint32 param);
|
|
|
|
|
|
|
|
MidiChannel *allocateChannel() { return 0; }
|
|
|
|
MidiChannel *getPercussionChannel() { return 0; }
|
|
|
|
|
|
|
|
bool isStereo() const { return true; }
|
|
|
|
int getRate() const { return _rate; }
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void playSwitch(bool play);
|
2010-09-17 20:03:20 +00:00
|
|
|
private:
|
|
|
|
void generateSamples(int16 *buffer, int len);
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
ResourceManager *_resMan;
|
|
|
|
CMSEmulator *_cms;
|
|
|
|
|
|
|
|
void writeToChip1(int address, int data);
|
|
|
|
void writeToChip2(int address, int data);
|
|
|
|
|
|
|
|
int32 _samplesPerCallback;
|
|
|
|
int32 _samplesPerCallbackRemainder;
|
|
|
|
int32 _samplesTillCallback;
|
|
|
|
int32 _samplesTillCallbackRemainder;
|
|
|
|
|
|
|
|
int _rate;
|
|
|
|
bool _playSwitch;
|
|
|
|
uint16 _masterVolume;
|
|
|
|
|
|
|
|
Common::SpanOwner<SciSpan<uint8> > _patchData;
|
|
|
|
|
2010-09-17 20:03:20 +00:00
|
|
|
struct Channel {
|
2019-06-21 14:18:03 +02:00
|
|
|
Channel()
|
|
|
|
: patch(0), volume(0), pan(0x40), hold(0), extraVoices(0),
|
|
|
|
pitchWheel(0x2000), pitchModifier(0), pitchAdditive(false),
|
|
|
|
lastVoiceUsed(0) {
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8 patch;
|
2010-09-17 20:03:20 +00:00
|
|
|
uint8 volume;
|
|
|
|
uint8 pan;
|
|
|
|
uint8 hold;
|
2019-06-21 14:18:03 +02:00
|
|
|
uint8 extraVoices;
|
SCI: (CMS sound driver) - add support for SCI0
I haven't found an elegant and non-intrusive way to squeeze SCI0 support into LordHoto's existing code. The drivers are too different. So I made some rearrangements. The basic mechanisms of LordHoto's SCI1 code should remain the same as before, though. I only introduced some more classes, moved some code into these classes and renamed some things (mainly for myself, so as not to get confused).
I fixed two voice mapping bugs in the existing driver code. The first bug in bindVocies() effectively hindered the driver from playing anything at all when the CMS_DISABLE_VOICE_MAPPING #define wasn't set (_voice[i].channel == 0xFF instead of _voice[i].channel != 0xFF). The second bug in unbindVoices() was not a complete show stopper, but the function simply did not "unbind the voice". The line which does the actual removal of the channel assignment was missing.
The SCI0 driver portions have been tested with: PQ2, KQ4, LSL3, QFG1, ICE and COC.
SCI_0_EARLY versions apparently don't support the CMS. At least I haven't seen a driver file so far. And there seems to be no no instrument patch resource. Although the latter issue needn't necessarily be one, since the patch data array in the driver is actually preset with data (which gets overwritten as soon as a patch file is loaded). Maybe this would work for SCI_0_EARLY. However, I haven't tested this, since I really would have have a look at a driver file first if one actually exists. For now, I have limited the driver to SCI_0_LATE.
SCI1 has been tested with KQ5 and LSL5 (not extensively, just to see whether anything got broken and whether my voice mapping fixes work).
2019-04-28 14:44:22 +02:00
|
|
|
uint16 pitchWheel;
|
2019-06-21 14:18:03 +02:00
|
|
|
uint8 pitchModifier;
|
|
|
|
bool pitchAdditive;
|
|
|
|
uint8 lastVoiceUsed;
|
2010-09-17 20:03:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Channel _channel[16];
|
SCI: (CMS sound driver) - add support for SCI0
I haven't found an elegant and non-intrusive way to squeeze SCI0 support into LordHoto's existing code. The drivers are too different. So I made some rearrangements. The basic mechanisms of LordHoto's SCI1 code should remain the same as before, though. I only introduced some more classes, moved some code into these classes and renamed some things (mainly for myself, so as not to get confused).
I fixed two voice mapping bugs in the existing driver code. The first bug in bindVocies() effectively hindered the driver from playing anything at all when the CMS_DISABLE_VOICE_MAPPING #define wasn't set (_voice[i].channel == 0xFF instead of _voice[i].channel != 0xFF). The second bug in unbindVoices() was not a complete show stopper, but the function simply did not "unbind the voice". The line which does the actual removal of the channel assignment was missing.
The SCI0 driver portions have been tested with: PQ2, KQ4, LSL3, QFG1, ICE and COC.
SCI_0_EARLY versions apparently don't support the CMS. At least I haven't seen a driver file so far. And there seems to be no no instrument patch resource. Although the latter issue needn't necessarily be one, since the patch data array in the driver is actually preset with data (which gets overwritten as soon as a patch file is loaded). Maybe this would work for SCI_0_EARLY. However, I haven't tested this, since I really would have have a look at a driver file first if one actually exists. For now, I have limited the driver to SCI_0_LATE.
SCI1 has been tested with KQ5 and LSL5 (not extensively, just to see whether anything got broken and whether my voice mapping fixes work).
2019-04-28 14:44:22 +02:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
struct Voice {
|
|
|
|
Voice() : channel(0xFF), note(0xFF), sustained(0xFF), ticks(0),
|
|
|
|
turnOffTicks(0), patchDataPtr(), patchDataIndex(0),
|
|
|
|
amplitudeTimer(0), amplitudeModifier(0), turnOff(false),
|
|
|
|
velocity(0) {
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
uint8 channel;
|
|
|
|
uint8 note;
|
|
|
|
uint8 sustained;
|
|
|
|
uint16 ticks;
|
|
|
|
uint16 turnOffTicks;
|
|
|
|
SciSpan<uint8> patchDataPtr;
|
|
|
|
uint8 patchDataIndex;
|
|
|
|
uint8 amplitudeTimer;
|
|
|
|
uint8 amplitudeModifier;
|
|
|
|
bool turnOff;
|
|
|
|
uint8 velocity;
|
|
|
|
};
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
Voice _voice[12];
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void voiceOn(int voice, int note, int velocity);
|
|
|
|
void voiceOff(int voice);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void noteSend(int voice);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void noteOn(int channel, int note, int velocity);
|
|
|
|
void noteOff(int channel, int note);
|
|
|
|
void controlChange(int channel, int control, int value);
|
|
|
|
void pitchWheel(int channel, int value);
|
2010-09-24 00:38:18 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void voiceMapping(int channel, int value);
|
|
|
|
void bindVoices(int channel, int voices);
|
|
|
|
void unbindVoices(int channel, int voices);
|
|
|
|
void donateVoices();
|
|
|
|
int findVoice(int channel);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
int findVoiceBasic(int channel);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void updateVoiceAmplitude(int voice);
|
|
|
|
void setupVoiceAmplitude(int voice);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
uint8 _octaveRegs[2][3];
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
static const int _timerFreq = 60;
|
SCI: (CMS sound driver) - add support for SCI0
I haven't found an elegant and non-intrusive way to squeeze SCI0 support into LordHoto's existing code. The drivers are too different. So I made some rearrangements. The basic mechanisms of LordHoto's SCI1 code should remain the same as before, though. I only introduced some more classes, moved some code into these classes and renamed some things (mainly for myself, so as not to get confused).
I fixed two voice mapping bugs in the existing driver code. The first bug in bindVocies() effectively hindered the driver from playing anything at all when the CMS_DISABLE_VOICE_MAPPING #define wasn't set (_voice[i].channel == 0xFF instead of _voice[i].channel != 0xFF). The second bug in unbindVoices() was not a complete show stopper, but the function simply did not "unbind the voice". The line which does the actual removal of the channel assignment was missing.
The SCI0 driver portions have been tested with: PQ2, KQ4, LSL3, QFG1, ICE and COC.
SCI_0_EARLY versions apparently don't support the CMS. At least I haven't seen a driver file so far. And there seems to be no no instrument patch resource. Although the latter issue needn't necessarily be one, since the patch data array in the driver is actually preset with data (which gets overwritten as soon as a patch file is loaded). Maybe this would work for SCI_0_EARLY. However, I haven't tested this, since I really would have have a look at a driver file first if one actually exists. For now, I have limited the driver to SCI_0_LATE.
SCI1 has been tested with KQ5 and LSL5 (not extensively, just to see whether anything got broken and whether my voice mapping fixes work).
2019-04-28 14:44:22 +02:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
static const int _frequencyTable[];
|
|
|
|
static const int _velocityTable[];
|
2010-09-17 20:03:20 +00:00
|
|
|
};
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
const int MidiDriver_CMS::_frequencyTable[] = {
|
2010-09-17 20:03:20 +00:00
|
|
|
3, 10, 17, 24,
|
|
|
|
31, 38, 46, 51,
|
|
|
|
58, 64, 71, 77,
|
|
|
|
83, 89, 95, 101,
|
|
|
|
107, 113, 119, 124,
|
|
|
|
130, 135, 141, 146,
|
|
|
|
151, 156, 162, 167,
|
|
|
|
172, 177, 182, 186,
|
|
|
|
191, 196, 200, 205,
|
|
|
|
209, 213, 217, 222,
|
|
|
|
226, 230, 234, 238,
|
|
|
|
242, 246, 250, 253
|
|
|
|
};
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
const int MidiDriver_CMS::_velocityTable[] = {
|
2010-09-17 20:03:20 +00:00
|
|
|
1, 3, 6, 8, 9, 10, 11, 12,
|
|
|
|
12, 13, 13, 14, 14, 14, 15, 15,
|
|
|
|
0, 1, 2, 2, 3, 4, 4, 5,
|
|
|
|
6, 6, 7, 8, 8, 9, 10, 10
|
|
|
|
};
|
|
|
|
|
|
|
|
int MidiDriver_CMS::open() {
|
|
|
|
if (_cms)
|
|
|
|
return MERR_ALREADY_OPEN;
|
|
|
|
|
|
|
|
assert(_resMan);
|
2016-12-31 20:39:57 -06:00
|
|
|
Resource *res = _resMan->findResource(ResourceId(kResourceTypePatch, 101), false);
|
2010-09-17 20:03:20 +00:00
|
|
|
if (!res)
|
|
|
|
return -1;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
_patchData->allocateFromSpan(*res);
|
|
|
|
|
2010-09-17 20:03:20 +00:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_channel); ++i)
|
|
|
|
_channel[i] = Channel();
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i)
|
|
|
|
_voice[i] = Voice();
|
|
|
|
|
|
|
|
_rate = _mixer->getOutputRate();
|
|
|
|
_cms = new CMSEmulator(_rate);
|
|
|
|
assert(_cms);
|
2010-09-17 20:03:20 +00:00
|
|
|
_playSwitch = true;
|
|
|
|
_masterVolume = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < 31; ++i) {
|
2019-06-21 14:18:03 +02:00
|
|
|
writeToChip1(i, 0);
|
|
|
|
writeToChip2(i, 0);
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
writeToChip1(0x14, 0xFF);
|
|
|
|
writeToChip2(0x14, 0xFF);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
writeToChip1(0x1C, 1);
|
|
|
|
writeToChip2(0x1C, 1);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
_samplesPerCallback = getRate() / _timerFreq;
|
|
|
|
_samplesPerCallbackRemainder = getRate() % _timerFreq;
|
|
|
|
_samplesTillCallback = 0;
|
|
|
|
_samplesTillCallbackRemainder = 0;
|
2010-09-17 20:03:20 +00:00
|
|
|
|
|
|
|
int retVal = MidiDriver_Emulated::open();
|
|
|
|
if (retVal != 0)
|
|
|
|
return retVal;
|
|
|
|
|
|
|
|
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiDriver_CMS::close() {
|
|
|
|
_mixer->stopHandle(_mixerSoundHandle);
|
|
|
|
|
2016-12-31 20:39:57 -06:00
|
|
|
_patchData.clear();
|
2010-09-17 20:03:20 +00:00
|
|
|
delete _cms;
|
2016-12-31 20:39:57 -06:00
|
|
|
_cms = nullptr;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MidiDriver_CMS::send(uint32 b) {
|
|
|
|
const uint8 command = b & 0xf0;
|
|
|
|
const uint8 channel = b & 0xf;
|
|
|
|
const uint8 op1 = (b >> 8) & 0xff;
|
|
|
|
const uint8 op2 = (b >> 16) & 0xff;
|
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
case 0x80:
|
|
|
|
noteOff(channel, op1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x90:
|
|
|
|
noteOn(channel, op1, op2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xB0:
|
|
|
|
controlChange(channel, op1, op2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xC0:
|
2019-06-21 14:18:03 +02:00
|
|
|
_channel[channel].patch = op1;
|
2010-09-17 20:03:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xE0:
|
|
|
|
pitchWheel(channel, (op1 & 0x7f) | ((op2 & 0x7f) << 7));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32 MidiDriver_CMS::property(int prop, uint32 param) {
|
|
|
|
switch (prop) {
|
|
|
|
case MIDI_PROP_MASTER_VOLUME:
|
|
|
|
if (param != 0xffff)
|
|
|
|
_masterVolume = param;
|
|
|
|
return _masterVolume;
|
2019-06-21 14:18:03 +02:00
|
|
|
|
2010-09-17 20:03:20 +00:00
|
|
|
default:
|
|
|
|
return MidiDriver_Emulated::property(prop, param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::playSwitch(bool play) {
|
|
|
|
_playSwitch = play;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::writeToChip1(int address, int data) {
|
|
|
|
_cms->portWrite(0x221, address);
|
|
|
|
_cms->portWrite(0x220, data);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (address >= 16 && address <= 18)
|
|
|
|
_octaveRegs[0][address - 16] = data;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::writeToChip2(int address, int data) {
|
|
|
|
_cms->portWrite(0x223, address);
|
|
|
|
_cms->portWrite(0x222, data);
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (address >= 16 && address <= 18)
|
|
|
|
_octaveRegs[1][address - 16] = data;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::voiceOn(int voiceNr, int note, int velocity) {
|
|
|
|
Voice &voice = _voice[voiceNr];
|
|
|
|
voice.note = note;
|
|
|
|
voice.turnOff = false;
|
|
|
|
voice.patchDataIndex = 0;
|
|
|
|
voice.amplitudeTimer = 0;
|
|
|
|
voice.ticks = 0;
|
|
|
|
voice.turnOffTicks = 0;
|
|
|
|
voice.patchDataPtr = _patchData->subspan(_patchData->getUint16LEAt(_channel[voice.channel].patch * 2));
|
|
|
|
if (velocity)
|
|
|
|
velocity = _velocityTable[(velocity >> 3)];
|
|
|
|
voice.velocity = velocity;
|
|
|
|
noteSend(voiceNr);
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::voiceOff(int voiceNr) {
|
|
|
|
Voice &voice = _voice[voiceNr];
|
|
|
|
voice.velocity = 0;
|
|
|
|
voice.note = 0xFF;
|
|
|
|
voice.sustained = 0;
|
|
|
|
voice.turnOff = false;
|
|
|
|
voice.patchDataIndex = 0;
|
|
|
|
voice.amplitudeTimer = 0;
|
|
|
|
voice.amplitudeModifier = 0;
|
|
|
|
voice.ticks = 0;
|
|
|
|
voice.turnOffTicks = 0;
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
setupVoiceAmplitude(voiceNr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MidiDriver_CMS::noteSend(int voiceNr) {
|
|
|
|
Voice &voice = _voice[voiceNr];
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
int frequency = (CLIP<int>(voice.note, 21, 116) - 21) * 4;
|
|
|
|
if (_channel[voice.channel].pitchModifier) {
|
|
|
|
int modifier = _channel[voice.channel].pitchModifier;
|
|
|
|
|
|
|
|
if (!_channel[voice.channel].pitchAdditive) {
|
|
|
|
if (frequency > modifier)
|
|
|
|
frequency -= modifier;
|
|
|
|
else
|
|
|
|
frequency = 0;
|
|
|
|
} else {
|
|
|
|
int tempFrequency = 384 - frequency;
|
|
|
|
if (modifier < tempFrequency)
|
|
|
|
frequency += modifier;
|
|
|
|
else
|
|
|
|
frequency = 383;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
int chipNumber = 0;
|
|
|
|
if (voiceNr >= 6) {
|
|
|
|
voiceNr -= 6;
|
|
|
|
chipNumber = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int octave = 0;
|
|
|
|
while (frequency >= 48) {
|
|
|
|
frequency -= 48;
|
|
|
|
++octave;
|
2019-06-10 16:53:00 +09:00
|
|
|
}
|
2019-06-21 14:18:03 +02:00
|
|
|
|
|
|
|
frequency = _frequencyTable[frequency];
|
|
|
|
|
|
|
|
if (chipNumber == 1)
|
|
|
|
writeToChip2(8 + voiceNr, frequency);
|
|
|
|
else
|
|
|
|
writeToChip1(8 + voiceNr, frequency);
|
|
|
|
|
|
|
|
uint8 octaveData = _octaveRegs[chipNumber][voiceNr >> 1];
|
|
|
|
|
|
|
|
if (voiceNr & 1) {
|
|
|
|
octaveData &= 0x0F;
|
|
|
|
octaveData |= (octave << 4);
|
|
|
|
} else {
|
|
|
|
octaveData &= 0xF0;
|
|
|
|
octaveData |= octave;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chipNumber == 1)
|
|
|
|
writeToChip2(0x10 + (voiceNr >> 1), octaveData);
|
|
|
|
else
|
|
|
|
writeToChip1(0x10 + (voiceNr >> 1), octaveData);
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::noteOn(int channel, int note, int velocity) {
|
2010-09-17 20:03:20 +00:00
|
|
|
if (note < 21 || note > 116)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (velocity == 0) {
|
2019-06-21 14:18:03 +02:00
|
|
|
noteOff(channel, note);
|
2010-09-17 20:03:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == channel && _voice[i].note == note) {
|
|
|
|
_voice[i].sustained = 0;
|
|
|
|
voiceOff(i);
|
|
|
|
voiceOn(i, note, velocity);
|
2010-09-17 20:03:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-24 00:38:18 +00:00
|
|
|
#ifdef CMS_DISABLE_VOICE_MAPPING
|
2019-06-21 14:18:03 +02:00
|
|
|
int voice = findVoiceBasic(channel);
|
2010-09-24 00:38:18 +00:00
|
|
|
#else
|
2019-06-21 14:18:03 +02:00
|
|
|
int voice = findVoice(channel);
|
2010-09-24 00:38:18 +00:00
|
|
|
#endif
|
2019-06-21 14:18:03 +02:00
|
|
|
if (voice != -1)
|
|
|
|
voiceOn(voice, note, velocity);
|
|
|
|
}
|
|
|
|
|
|
|
|
int MidiDriver_CMS::findVoiceBasic(int channel) {
|
|
|
|
int voice = -1;
|
|
|
|
int oldestVoice = -1;
|
|
|
|
int oldestAge = -1;
|
|
|
|
|
|
|
|
// Try to find a voice assigned to this channel that is free (round-robin)
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_voice); i++) {
|
|
|
|
int v = (_channel[channel].lastVoiceUsed + i + 1) % ARRAYSIZE(_voice);
|
|
|
|
|
|
|
|
if (_voice[v].note == 0xFF) {
|
|
|
|
voice = v;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We also keep track of the oldest note in case the search fails
|
|
|
|
if (_voice[v].ticks > oldestAge) {
|
|
|
|
oldestAge = _voice[v].ticks;
|
|
|
|
oldestVoice = v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (voice == -1) {
|
|
|
|
if (oldestVoice >= 0) {
|
|
|
|
voiceOff(oldestVoice);
|
|
|
|
voice = oldestVoice;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
2019-06-21 14:18:03 +02:00
|
|
|
|
|
|
|
_voice[voice].channel = channel;
|
|
|
|
_channel[channel].lastVoiceUsed = voice;
|
|
|
|
return voice;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::noteOff(int channel, int note) {
|
2010-09-17 20:03:20 +00:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
2019-06-21 14:18:03 +02:00
|
|
|
if (_voice[i].channel == channel && _voice[i].note == note) {
|
|
|
|
if (_channel[channel].hold != 0)
|
|
|
|
_voice[i].sustained = true;
|
2010-09-17 20:03:20 +00:00
|
|
|
else
|
2019-06-21 14:18:03 +02:00
|
|
|
_voice[i].turnOff = true;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::controlChange(int channel, int control, int value) {
|
2010-09-17 20:03:20 +00:00
|
|
|
switch (control) {
|
|
|
|
case 7:
|
2019-06-21 14:18:03 +02:00
|
|
|
if (value) {
|
|
|
|
value >>= 3;
|
|
|
|
if (!value)
|
|
|
|
++value;
|
|
|
|
}
|
|
|
|
|
|
|
|
_channel[channel].volume = value;
|
2010-09-17 20:03:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 10:
|
2019-06-21 14:18:03 +02:00
|
|
|
_channel[channel].pan = value;
|
2010-09-17 20:03:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 64:
|
2019-06-21 14:18:03 +02:00
|
|
|
_channel[channel].hold = value;
|
2010-09-17 20:03:20 +00:00
|
|
|
|
|
|
|
if (!value) {
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == channel && _voice[i].sustained) {
|
|
|
|
_voice[i].sustained = 0;
|
|
|
|
_voice[i].turnOff = true;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 75:
|
2010-09-24 00:38:18 +00:00
|
|
|
#ifndef CMS_DISABLE_VOICE_MAPPING
|
2019-06-21 14:18:03 +02:00
|
|
|
voiceMapping(channel, value);
|
2010-09-24 00:38:18 +00:00
|
|
|
#endif
|
2010-09-17 20:03:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 123:
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
2019-06-21 14:18:03 +02:00
|
|
|
if (_voice[i].channel == channel && _voice[i].note != 0xFF)
|
|
|
|
voiceOff(i);
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::pitchWheel(int channelNr, int value) {
|
|
|
|
Channel &channel = _channel[channelNr];
|
|
|
|
channel.pitchWheel = value;
|
|
|
|
channel.pitchAdditive = false;
|
|
|
|
channel.pitchModifier = 0;
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (value < 0x2000) {
|
|
|
|
channel.pitchModifier = (0x2000 - value) / 170;
|
|
|
|
} else if (value > 0x2000) {
|
|
|
|
channel.pitchModifier = (value - 0x2000) / 170;
|
|
|
|
channel.pitchAdditive = true;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == channelNr && _voice[i].note != 0xFF)
|
|
|
|
noteSend(i);
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-24 00:38:18 +00:00
|
|
|
void MidiDriver_CMS::voiceMapping(int channelNr, int value) {
|
|
|
|
int curVoices = 0;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == channelNr)
|
2010-09-24 00:38:18 +00:00
|
|
|
++curVoices;
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
curVoices += _channel[channelNr].extraVoices;
|
2010-09-24 00:38:18 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (curVoices == value) {
|
|
|
|
return;
|
|
|
|
} else if (curVoices < value) {
|
|
|
|
bindVoices(channelNr, value - curVoices);
|
|
|
|
} else {
|
|
|
|
unbindVoices(channelNr, curVoices - value);
|
|
|
|
donateVoices();
|
2010-09-24 00:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::bindVoices(int channel, int voices) {
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == 0xFF)
|
2010-09-24 00:38:18 +00:00
|
|
|
continue;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
Voice &voice = _voice[i];
|
|
|
|
voice.channel = channel;
|
2010-09-24 00:38:18 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (voice.note != 0xFF)
|
|
|
|
voiceOff(i);
|
2010-09-24 00:38:18 +00:00
|
|
|
|
|
|
|
--voices;
|
|
|
|
if (voices == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
_channel[channel].extraVoices += voices;
|
|
|
|
|
|
|
|
// The original called "PatchChange" here, since this just
|
|
|
|
// copies the value of _channel[channel].patch to itself
|
|
|
|
// it is left out here though.
|
2010-09-24 00:38:18 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::unbindVoices(int channelNr, int voices) {
|
2010-09-24 00:38:18 +00:00
|
|
|
Channel &channel = _channel[channelNr];
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (channel.extraVoices >= voices) {
|
|
|
|
channel.extraVoices -= voices;
|
2010-09-24 00:38:18 +00:00
|
|
|
} else {
|
2019-06-21 14:18:03 +02:00
|
|
|
voices -= channel.extraVoices;
|
|
|
|
channel.extraVoices = 0;
|
2010-09-24 00:38:18 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == channelNr
|
|
|
|
&& _voice[i].note == 0xFF) {
|
2010-09-24 00:38:18 +00:00
|
|
|
--voices;
|
|
|
|
if (voices == 0)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
uint16 voiceTime = 0;
|
|
|
|
uint voiceNr = 0;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel != channelNr)
|
2010-09-24 00:38:18 +00:00
|
|
|
continue;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
uint16 curTime = _voice[i].turnOffTicks;
|
2010-09-24 00:38:18 +00:00
|
|
|
if (curTime)
|
|
|
|
curTime += 0x8000;
|
|
|
|
else
|
2019-06-21 14:18:03 +02:00
|
|
|
curTime = _voice[i].ticks;
|
2010-09-24 00:38:18 +00:00
|
|
|
|
|
|
|
if (curTime >= voiceTime) {
|
|
|
|
voiceNr = i;
|
|
|
|
voiceTime = curTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
_voice[voiceNr].sustained = 0;
|
|
|
|
voiceOff(voiceNr);
|
|
|
|
_voice[voiceNr].channel = 0xFF;
|
2010-09-24 00:38:18 +00:00
|
|
|
--voices;
|
|
|
|
} while (voices != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::donateVoices() {
|
2010-09-24 00:38:18 +00:00
|
|
|
int freeVoices = 0;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].channel == 0xFF)
|
2010-09-24 00:38:18 +00:00
|
|
|
++freeVoices;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!freeVoices)
|
|
|
|
return;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
for (uint i = 0; i < ARRAYSIZE(_channel); ++i) {
|
2010-09-24 00:38:18 +00:00
|
|
|
Channel &channel = _channel[i];
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (!channel.extraVoices) {
|
2010-09-24 00:38:18 +00:00
|
|
|
continue;
|
2019-06-21 14:18:03 +02:00
|
|
|
} else if (channel.extraVoices < freeVoices) {
|
|
|
|
freeVoices -= channel.extraVoices;
|
|
|
|
channel.extraVoices = 0;
|
|
|
|
bindVoices(i, channel.extraVoices);
|
2010-09-24 00:38:18 +00:00
|
|
|
} else {
|
2019-06-21 14:18:03 +02:00
|
|
|
channel.extraVoices -= freeVoices;
|
|
|
|
bindVoices(i, freeVoices);
|
2010-09-24 00:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
int MidiDriver_CMS::findVoice(int channelNr) {
|
2010-09-24 00:38:18 +00:00
|
|
|
Channel &channel = _channel[channelNr];
|
|
|
|
int voiceNr = channel.lastVoiceUsed;
|
2019-06-21 14:18:03 +02:00
|
|
|
|
2010-09-24 00:38:18 +00:00
|
|
|
int newVoice = 0;
|
|
|
|
uint16 newVoiceTime = 0;
|
|
|
|
|
|
|
|
bool loopDone = false;
|
|
|
|
do {
|
|
|
|
++voiceNr;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (voiceNr == 12)
|
2010-09-24 00:38:18 +00:00
|
|
|
voiceNr = 0;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
Voice &voice = _voice[voiceNr];
|
|
|
|
|
2010-09-24 00:38:18 +00:00
|
|
|
if (voiceNr == channel.lastVoiceUsed)
|
|
|
|
loopDone = true;
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (voice.channel == channelNr) {
|
|
|
|
if (voice.note == 0xFF) {
|
|
|
|
channel.lastVoiceUsed = voiceNr;
|
2010-09-24 00:38:18 +00:00
|
|
|
return voiceNr;
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
uint16 curTime = voice.turnOffTicks;
|
2010-09-24 00:38:18 +00:00
|
|
|
if (curTime)
|
|
|
|
curTime += 0x8000;
|
|
|
|
else
|
2019-06-21 14:18:03 +02:00
|
|
|
curTime = voice.ticks;
|
2010-09-24 00:38:18 +00:00
|
|
|
|
|
|
|
if (curTime >= newVoiceTime) {
|
|
|
|
newVoice = voiceNr;
|
|
|
|
newVoiceTime = curTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (!loopDone);
|
|
|
|
|
|
|
|
if (newVoiceTime > 0) {
|
|
|
|
voiceNr = newVoice;
|
2019-06-21 14:18:03 +02:00
|
|
|
_voice[voiceNr].sustained = 0;
|
|
|
|
voiceOff(voiceNr);
|
|
|
|
channel.lastVoiceUsed = voiceNr;
|
SCI: (CMS sound driver) - add support for SCI0
I haven't found an elegant and non-intrusive way to squeeze SCI0 support into LordHoto's existing code. The drivers are too different. So I made some rearrangements. The basic mechanisms of LordHoto's SCI1 code should remain the same as before, though. I only introduced some more classes, moved some code into these classes and renamed some things (mainly for myself, so as not to get confused).
I fixed two voice mapping bugs in the existing driver code. The first bug in bindVocies() effectively hindered the driver from playing anything at all when the CMS_DISABLE_VOICE_MAPPING #define wasn't set (_voice[i].channel == 0xFF instead of _voice[i].channel != 0xFF). The second bug in unbindVoices() was not a complete show stopper, but the function simply did not "unbind the voice". The line which does the actual removal of the channel assignment was missing.
The SCI0 driver portions have been tested with: PQ2, KQ4, LSL3, QFG1, ICE and COC.
SCI_0_EARLY versions apparently don't support the CMS. At least I haven't seen a driver file so far. And there seems to be no no instrument patch resource. Although the latter issue needn't necessarily be one, since the patch data array in the driver is actually preset with data (which gets overwritten as soon as a patch file is loaded). Maybe this would work for SCI_0_EARLY. However, I haven't tested this, since I really would have have a look at a driver file first if one actually exists. For now, I have limited the driver to SCI_0_LATE.
SCI1 has been tested with KQ5 and LSL5 (not extensively, just to see whether anything got broken and whether my voice mapping fixes work).
2019-04-28 14:44:22 +02:00
|
|
|
return voiceNr;
|
2019-06-21 14:18:03 +02:00
|
|
|
} else {
|
|
|
|
return -1;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::updateVoiceAmplitude(int voiceNr) {
|
|
|
|
Voice &voice = _voice[voiceNr];
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
if (voice.amplitudeTimer != 0 && voice.amplitudeTimer != 254) {
|
|
|
|
--voice.amplitudeTimer;
|
|
|
|
return;
|
|
|
|
} else if (voice.amplitudeTimer == 254) {
|
|
|
|
if (!voice.turnOff)
|
|
|
|
return;
|
SCI: (CMS sound driver) - add support for SCI0
I haven't found an elegant and non-intrusive way to squeeze SCI0 support into LordHoto's existing code. The drivers are too different. So I made some rearrangements. The basic mechanisms of LordHoto's SCI1 code should remain the same as before, though. I only introduced some more classes, moved some code into these classes and renamed some things (mainly for myself, so as not to get confused).
I fixed two voice mapping bugs in the existing driver code. The first bug in bindVocies() effectively hindered the driver from playing anything at all when the CMS_DISABLE_VOICE_MAPPING #define wasn't set (_voice[i].channel == 0xFF instead of _voice[i].channel != 0xFF). The second bug in unbindVoices() was not a complete show stopper, but the function simply did not "unbind the voice". The line which does the actual removal of the channel assignment was missing.
The SCI0 driver portions have been tested with: PQ2, KQ4, LSL3, QFG1, ICE and COC.
SCI_0_EARLY versions apparently don't support the CMS. At least I haven't seen a driver file so far. And there seems to be no no instrument patch resource. Although the latter issue needn't necessarily be one, since the patch data array in the driver is actually preset with data (which gets overwritten as soon as a patch file is loaded). Maybe this would work for SCI_0_EARLY. However, I haven't tested this, since I really would have have a look at a driver file first if one actually exists. For now, I have limited the driver to SCI_0_LATE.
SCI1 has been tested with KQ5 and LSL5 (not extensively, just to see whether anything got broken and whether my voice mapping fixes work).
2019-04-28 14:44:22 +02:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
voice.amplitudeTimer = 0;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
int nextDataIndex = voice.patchDataIndex;
|
|
|
|
uint8 timerData = 0;
|
|
|
|
uint8 amplitudeData = voice.patchDataPtr[nextDataIndex];
|
|
|
|
|
|
|
|
if (amplitudeData == 255) {
|
|
|
|
timerData = amplitudeData = 0;
|
|
|
|
voiceOff(voiceNr);
|
|
|
|
} else {
|
|
|
|
timerData = voice.patchDataPtr[nextDataIndex + 1];
|
|
|
|
nextDataIndex += 2;
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
voice.patchDataIndex = nextDataIndex;
|
|
|
|
voice.amplitudeTimer = timerData;
|
|
|
|
voice.amplitudeModifier = amplitudeData;
|
SCI: (CMS sound driver) - add support for SCI0
I haven't found an elegant and non-intrusive way to squeeze SCI0 support into LordHoto's existing code. The drivers are too different. So I made some rearrangements. The basic mechanisms of LordHoto's SCI1 code should remain the same as before, though. I only introduced some more classes, moved some code into these classes and renamed some things (mainly for myself, so as not to get confused).
I fixed two voice mapping bugs in the existing driver code. The first bug in bindVocies() effectively hindered the driver from playing anything at all when the CMS_DISABLE_VOICE_MAPPING #define wasn't set (_voice[i].channel == 0xFF instead of _voice[i].channel != 0xFF). The second bug in unbindVoices() was not a complete show stopper, but the function simply did not "unbind the voice". The line which does the actual removal of the channel assignment was missing.
The SCI0 driver portions have been tested with: PQ2, KQ4, LSL3, QFG1, ICE and COC.
SCI_0_EARLY versions apparently don't support the CMS. At least I haven't seen a driver file so far. And there seems to be no no instrument patch resource. Although the latter issue needn't necessarily be one, since the patch data array in the driver is actually preset with data (which gets overwritten as soon as a patch file is loaded). Maybe this would work for SCI_0_EARLY. However, I haven't tested this, since I really would have have a look at a driver file first if one actually exists. For now, I have limited the driver to SCI_0_LATE.
SCI1 has been tested with KQ5 and LSL5 (not extensively, just to see whether anything got broken and whether my voice mapping fixes work).
2019-04-28 14:44:22 +02:00
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void MidiDriver_CMS::setupVoiceAmplitude(int voiceNr) {
|
|
|
|
Voice &voice = _voice[voiceNr];
|
|
|
|
uint amplitude = 0;
|
|
|
|
|
|
|
|
if (_channel[voice.channel].volume && voice.velocity
|
|
|
|
&& voice.amplitudeModifier && _masterVolume) {
|
|
|
|
amplitude = _channel[voice.channel].volume * voice.velocity;
|
|
|
|
amplitude /= 0x0F;
|
|
|
|
amplitude *= voice.amplitudeModifier;
|
|
|
|
amplitude /= 0x0F;
|
|
|
|
amplitude *= _masterVolume;
|
|
|
|
amplitude /= 0x0F;
|
|
|
|
|
|
|
|
if (!amplitude)
|
|
|
|
++amplitude;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8 amplitudeData = 0;
|
|
|
|
int pan = _channel[voice.channel].pan >> 2;
|
|
|
|
if (pan >= 16) {
|
|
|
|
amplitudeData = (amplitude * (31 - pan) / 0x0F) & 0x0F;
|
|
|
|
amplitudeData |= (amplitude << 4);
|
|
|
|
} else {
|
|
|
|
amplitudeData = (amplitude * pan / 0x0F) & 0x0F;
|
|
|
|
amplitudeData <<= 4;
|
|
|
|
amplitudeData |= amplitude;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_playSwitch)
|
|
|
|
amplitudeData = 0;
|
|
|
|
|
|
|
|
if (voiceNr >= 6)
|
|
|
|
writeToChip2(voiceNr - 6, amplitudeData);
|
|
|
|
else
|
|
|
|
writeToChip1(voiceNr, amplitudeData);
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MidiDriver_CMS::generateSamples(int16 *buffer, int len) {
|
2019-06-21 14:18:03 +02:00
|
|
|
while (len) {
|
|
|
|
if (!_samplesTillCallback) {
|
|
|
|
for (uint i = 0; i < ARRAYSIZE(_voice); ++i) {
|
|
|
|
if (_voice[i].note == 0xFF)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
++_voice[i].ticks;
|
|
|
|
if (_voice[i].turnOff)
|
|
|
|
++_voice[i].turnOffTicks;
|
|
|
|
|
|
|
|
updateVoiceAmplitude(i);
|
|
|
|
setupVoiceAmplitude(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
_samplesTillCallback = _samplesPerCallback;
|
|
|
|
_samplesTillCallbackRemainder += _samplesPerCallbackRemainder;
|
|
|
|
if (_samplesTillCallbackRemainder >= _timerFreq) {
|
|
|
|
_samplesTillCallback++;
|
|
|
|
_samplesTillCallbackRemainder -= _timerFreq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 render = MIN<int32>(len, _samplesTillCallback);
|
|
|
|
len -= render;
|
|
|
|
_samplesTillCallback -= render;
|
|
|
|
_cms->readBuffer(buffer, render);
|
|
|
|
buffer += render * 2;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
|
2010-09-17 20:03:20 +00:00
|
|
|
class MidiPlayer_CMS : public MidiPlayer {
|
|
|
|
public:
|
2019-06-21 14:18:03 +02:00
|
|
|
MidiPlayer_CMS(SciVersion version) : MidiPlayer(version) {
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
int open(ResourceManager *resMan) {
|
|
|
|
if (_driver)
|
|
|
|
return MidiDriver::MERR_ALREADY_OPEN;
|
2010-09-17 20:03:20 +00:00
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
_driver = new MidiDriver_CMS(g_system->getMixer(), resMan);
|
|
|
|
int driverRetVal = _driver->open();
|
|
|
|
if (driverRetVal != 0)
|
|
|
|
return driverRetVal;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void close() {
|
|
|
|
_driver->setTimerCallback(0, 0);
|
|
|
|
_driver->close();
|
|
|
|
delete _driver;
|
|
|
|
_driver = nullptr;
|
|
|
|
}
|
2010-09-17 20:03:20 +00:00
|
|
|
|
|
|
|
bool hasRhythmChannel() const { return false; }
|
2019-06-21 14:18:03 +02:00
|
|
|
byte getPlayId() const { return 9; }
|
2010-09-17 20:03:20 +00:00
|
|
|
int getPolyphony() const { return 12; }
|
|
|
|
|
2019-06-21 14:18:03 +02:00
|
|
|
void playSwitch(bool play) { static_cast<MidiDriver_CMS *>(_driver)->playSwitch(play); }
|
2010-09-17 20:03:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
MidiPlayer *MidiPlayer_CMS_create(SciVersion version) {
|
|
|
|
return new MidiPlayer_CMS(version);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace SCI
|