[Scummvm-git-logs] scummvm master -> bba5513a2d8a6bce05c8bb6289d1a8deff743e4b
bluegr
bluegr at gmail.com
Tue Apr 2 19:45:42 CEST 2019
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
acfc0c90ce SCI: add PC-9801 sound driver
1fcefba4ee SCI: add SCI0 support to PC-98 sound driver
bc5ecb3b7c SCI: (PC98 sound driver) - bugfixes
037eec3144 SCI: implement SCI0 midi driver track initialization
5d4e22ebc9 SCI: (PC-98 driver) - frequency bug workaround
bba5513a2d SCI: - update MT32/GM driver initTrack method
Commit: acfc0c90ce740cbcc5b4b8662ee9c3ae84a6e013
https://github.com/scummvm/scummvm/commit/acfc0c90ce740cbcc5b4b8662ee9c3ae84a6e013
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-02T20:45:35+03:00
Commit Message:
SCI: add PC-9801 sound driver
(supports SCI1 for now)
Changed paths:
A engines/sci/sound/drivers/pc9801.cpp
engines/sci/module.mk
engines/sci/sound/drivers/mididriver.h
engines/sci/sound/music.cpp
diff --git a/engines/sci/module.mk b/engines/sci/module.mk
index 7decff6..6a341b6 100644
--- a/engines/sci/module.mk
+++ b/engines/sci/module.mk
@@ -77,6 +77,7 @@ MODULE_OBJS := \
sound/drivers/fmtowns.o \
sound/drivers/midi.o \
sound/drivers/pcjr.o \
+ sound/drivers/pc9801.o \
video/seq_decoder.o
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index c7c0198a..f51851b 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -133,6 +133,7 @@ extern MidiPlayer *MidiPlayer_CMS_create(SciVersion version);
extern MidiPlayer *MidiPlayer_Midi_create(SciVersion version);
extern MidiPlayer *MidiPlayer_Fb01_create(SciVersion version);
extern MidiPlayer *MidiPlayer_FMTowns_create(SciVersion version);
+extern MidiPlayer *MidiPlayer_PC9801_create(SciVersion version);
} // End of namespace Sci
diff --git a/engines/sci/sound/drivers/pc9801.cpp b/engines/sci/sound/drivers/pc9801.cpp
new file mode 100644
index 0000000..767779e
--- /dev/null
+++ b/engines/sci/sound/drivers/pc9801.cpp
@@ -0,0 +1,1488 @@
+/* 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.
+ *
+ * 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.
+ *
+ */
+
+#include "sci/sci.h"
+
+#include "common/file.h"
+#include "common/system.h"
+#include "common/textconsole.h"
+
+#include "audio/softsynth/fmtowns_pc98/pc98_audio.h"
+
+#include "sci/resource.h"
+#include "sci/sound/drivers/mididriver.h"
+
+/* PC-98 sound driver for:
+ Quest for Glory 1 SCI_VERSION_0_LATE first driver type
+ Police Quest 2 SCI_VERSION_0_LATE second driver type
+ Castle of Dr. Brain SCI_VERSION_1_LATE third driver type
+ Space Quest IV SCI_VERSION_1_LATE third driver type
+ King's Quest V SCI_VERSION_1_LATE latest driver type
+*/
+
+//#define SCI_PC98_AUDIO_EXTENDED
+
+namespace Sci {
+
+class MidiPart_PC9801;
+class SoundChannel_PC9801 {
+public:
+ SoundChannel_PC9801(PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, int type, bool &soundOn);
+ virtual ~SoundChannel_PC9801() {}
+
+ void noteOff();
+ void noteOn(uint8 note, uint8 velo);
+
+ virtual void processNoteEvent(uint8 note, bool noteOn);
+ void update();
+ void setVolume(uint8 volume);
+
+ virtual void reset();
+
+ uint8 _assign;
+ uint8 _note;
+ uint8 _sustain;
+ uint16 _duration;
+
+ static const uint8 *_instrumentData;
+
+protected:
+ enum ChannelStatusFlags {
+ kChanVbrEnable = 0x01,
+ kChanVbrRestartEnv = 0x02,
+ kChanKeyOn = 0x04,
+ kChanNgEnable = 0x08,
+ kChanNgRestartEnv = 0x10,
+ kChanNgDecrease = 0x20,
+ kChanVbrMode = 0x40,
+ kChanVbrDecrease = 0x80
+ };
+
+ int recalculateFrequency(uint16 note, uint16 modifier, uint8 *destOctaveBlock, uint16 *destFrequency, uint8 *destVbrFrequencyModifier);
+ uint8 getVolume();
+ virtual void processSounds();
+ void programChangeInit(const uint8 *data);
+ void writeReg(uint8 part, uint8 reg, uint8 val);
+
+ int8 _transpose;
+ uint8 _vbrInitialDelay;
+ uint8 _vbrEnvelopeTimer;
+ uint8 _vbrEnvelopeSpeed;
+
+ uint16 _frequencyCourse;
+ uint8 _frequencyBlock;
+ uint8 _frequencyNoteModifier;
+
+ uint8 _vbrDepthIncr;
+ uint8 _vbrDecrTime;
+ uint8 _vbrDepthDecr;
+ uint8 _vbrIncrTime;
+ uint8 _vbrSensitivity;
+ uint16 _vbrIncrStep;
+ uint16 _vbrDecrStep;
+ uint8 _vbrModulationTimer;
+ int16 _vbrFrequencyModifier;
+ uint8 _vbrCur;
+ uint8 _flags;
+
+ const uint16 *_noteFrequency;
+ const uint16 *_noteFrequencyModifier;
+ const uint8 *_selectedInstrument;
+
+private:
+ virtual void programChange(uint8 program) = 0;
+ virtual void prepareFrequencyAndVolume(bool updateVolume);
+ virtual void sendSoundOnOff(bool noteOn) = 0;
+ virtual void sendFrequency() = 0;
+ virtual void sendVolume() = 0;
+
+ uint8 _velo;
+ uint8 _volume;
+ uint8 _program;
+ bool &_soundOn;
+
+ MidiPart_PC9801 **_parts;
+ PC98AudioCore *_pc98a;
+ const uint8 _noteRangeLow;
+ const uint8 _noteRangeHigh;
+ const int _type;
+ const SciVersion _version;
+};
+
+class SoundChannel_PC9801_FM4OP : public SoundChannel_PC9801 {
+public:
+ SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn);
+ virtual ~SoundChannel_PC9801_FM4OP() {}
+
+private:
+ void programChange(uint8 program);
+ void sendSoundOnOff(bool noteOn);
+ void sendVolume();
+ void sendFrequency();
+
+ uint8 _operatorLevel[4];
+ uint8 _carrier;
+
+ const uint8 _regPrt;
+ const uint8 _regOffs;
+ const uint8 _patchSize;
+};
+
+class SoundChannel_PC9801_FM2OP : public SoundChannel_PC9801 {
+public:
+ SoundChannel_PC9801_FM2OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn);
+ virtual ~SoundChannel_PC9801_FM2OP() {}
+
+ void processNoteEvent(uint8 note, bool noteOn);
+ void reset();
+
+private:
+ void programChange(uint8 program);
+ void prepareFrequencyAndVolume(bool updateVolume);
+ void processSounds();
+ void sendSoundOnOff(bool noteOn);
+ void sendVolume();
+ void sendFrequency();
+
+ uint8 _operatorLevel[2];
+ uint8 _operatorFrqIndex[2];
+ uint16 _frequencyCourse2;
+ uint8 _frequencyNoteModifier2;
+ int16 _vbrFrequencyModifier2;
+ uint16 _vbrIncrStep2;
+ uint16 _vbrDecrStep2;
+ uint8 _vbrCur2;
+ static uint8 _activeOperators;
+
+ const uint16 *_opFreqOffset;
+ const uint8 _patchOffset;
+ const uint8 _regPrt;
+ const uint8 _regOffs;
+ const uint8 _patchSize;
+};
+
+class SoundChannel_PC9801_SSG : public SoundChannel_PC9801 {
+public:
+ SoundChannel_PC9801_SSG(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchOffset, uint8 patchSize, bool &soundOn);
+ virtual ~SoundChannel_PC9801_SSG() {}
+ void reset();
+
+private:
+ enum {
+ kEnvSSG_silent = 0x00,
+ kEnvSSG_decay = 0x01,
+ kEnvSSG_sustain = 0x02,
+ kEnvSSG_attack = 0x03,
+ kEnvSSG_keyOn = 0x10
+ };
+
+ void programChange(uint8 program);
+ void processSounds();
+ void sendSoundOnOff(bool noteOn);
+ void sendVolume();
+ void sendFrequency();
+ void updateNg();
+ void sendActiveChannelsStatus();
+
+ uint8 _instrumentChanMask;
+ uint8 _chanDisableMask;
+ uint8 _currentLevel;
+ uint8 _ngPhaseStep;
+ uint8 _ngPhase;
+ uint8 _ngEnvelopeTimer;
+ uint8 _ngSpeed;
+ uint8 _ssgEnvelopeTimer;
+ uint8 _ssgLevel;
+ uint8 _ssgSpeed;
+ uint8 _ssgEnvelopeState;
+
+ static uint8 _activeChannnelsStatus;
+
+ const uint8 *_ngFreq;
+ const uint8 _regOffs;
+ const uint8 _patchOffset;
+ const uint8 _patchSize;
+};
+
+class MidiPart_PC9801 {
+public:
+ MidiPart_PC9801(uint8 id, SoundChannel_PC9801 **channels, uint8 numChan, SciVersion version);
+ ~MidiPart_PC9801() {}
+
+ void noteOff(uint8 note);
+ void noteOn(uint8 note, uint8 velo);
+ void controlChangeVolume(uint8 vol);
+ void controlChangeSustain(uint8 sus);
+ void controlChangePolyphony(uint8 numChan);
+ void controlChangeAllNotesOff();
+ void programChange(uint8 prg);
+ void pitchBend(int16 val);
+
+ void addChannels(int num, int resetMissingChannels = -1);
+ void dropChannels(int num);
+
+ uint8 program() const { return _program; }
+ uint8 volume() const { return _volume; }
+ uint16 pitchBend() const { return _pitchBend; }
+ uint8 missingChannels() const { return _chanMissing; }
+
+private:
+ int allocateChannel();
+ void assignFreeChannels();
+
+ uint8 _id;
+ uint8 _program;
+ uint8 _volume;
+ uint8 _sustain;
+ uint16 _pitchBend;
+ uint8 _outChan;
+ uint8 _chanMissing;
+
+ SoundChannel_PC9801 **_chan;
+ const SciVersion _version;
+ const int _numChan;
+ const uint8 _noteRangeLow;
+ const uint8 _noteRangeHigh;
+};
+
+class MidiDriver_PC9801 : public MidiDriver, public PC98AudioPluginDriver {
+public:
+ enum {
+ MIDI_PROP_PLAYSWITCH = 1,
+ MIDI_PROP_POLYPHONY = 2,
+ MIDI_PROP_CHANNEL_ID = 3
+ };
+
+ MidiDriver_PC9801(Audio::Mixer *mixer, SciVersion version);
+ ~MidiDriver_PC9801();
+
+ int open();
+ bool isOpen() const { return _isOpen; }
+ void close();
+
+ void send(uint32 b);
+ static void assignFreeChannels(int num);
+ uint32 property(int prop, uint32 param);
+
+ void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
+ uint32 getBaseTempo();
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+
+ void timerCallbackB();
+
+private:
+ void loadInstruments(const SciSpan<const uint8> &data);
+ void updateParser();
+ void updateChannels();
+ void reset();
+
+ Common::TimerManager::TimerProc _timerProc;
+ void *_timerProcPara;
+
+ static MidiPart_PC9801 **_parts;
+ SoundChannel_PC9801 **_chan;
+ uint8 *_instrumentData;
+
+ uint8 _masterVolume;
+ bool _soundOn;
+
+ uint8 _numChan;
+ uint8 _internalVersion;
+ uint8 _ssgPatchOffset;
+ uint8 _patchSize;
+
+ const uint8 _playID;
+ const uint8 _channelMask1;
+ const uint8 _channelMask2;
+
+ bool _isOpen;
+ bool _ready;
+
+ const uint16 _baseTempo;
+
+ PC98AudioCore *_pc98a;
+ const SciVersion _version;
+};
+
+class MidiPlayer_PC9801 : public MidiPlayer {
+public:
+ enum {
+ MIDI_PROP_PLAYSWITCH = 1,
+ MIDI_PROP_POLYPHONY = 2,
+ MIDI_PROP_CHANNEL_ID = 3
+ };
+
+ MidiPlayer_PC9801(SciVersion version);
+ ~MidiPlayer_PC9801();
+
+ bool hasRhythmChannel() const;
+ byte getPlayId() const;
+ int getPolyphony() const;
+ void playSwitch(bool play);
+};
+
+const uint8 *SoundChannel_PC9801::_instrumentData = 0;
+
+SoundChannel_PC9801::SoundChannel_PC9801(PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, int type, bool &soundOn) : _pc98a(pc98a), _parts(parts), _version(version),
+_type(type), _soundOn(soundOn), _assign(0xff), _note(0xff), _velo(0x7F), _volume(0), _transpose(0), _sustain(0), _duration(0), _program(0xff), _vbrInitialDelay(0),
+_vbrEnvelopeTimer(0), _vbrEnvelopeSpeed(0), _vbrDepthIncr(0), _vbrDecrTime(0), _vbrDepthDecr(0), _vbrIncrTime(0), _vbrSensitivity(0), _vbrFrequencyModifier(0), _vbrIncrStep(0),
+_vbrDecrStep(0), _vbrModulationTimer(0), _flags(0), _vbrCur(0x80), _frequencyBlock(0), _frequencyCourse(0), _frequencyNoteModifier(0),
+_noteRangeLow(version > SCI_VERSION_0_LATE ? 12: 24), _noteRangeHigh(version > SCI_VERSION_0_LATE ? 107 : 119) {
+ static const uint16 courseV0[] = { 0x269, 0x28D, 0x2B5, 0x2DE, 0x30A, 0x339, 0x368, 0x39D, 0x3D4, 0x40E, 0x44A, 0x48C };
+ static const uint16 courseV1[] = { 0x26A, 0x28E, 0x2B5, 0x2DF, 0x30A, 0x339, 0x36A, 0x39E, 0x3D5, 0x40F, 0x44D, 0x48F };
+ static const uint16 fine[] = { 0x24, 0x27, 0x2A, 0x2B, 0x2F, 0x31, 0x34, 0x37, 0x3A, 0x3E, 0x42, 0x45 };
+ _noteFrequency = (version > SCI_VERSION_0_LATE) ? courseV1 : courseV0;
+ _noteFrequencyModifier = fine;
+}
+
+void SoundChannel_PC9801::noteOff() {
+ if (_sustain)
+ return;
+
+ processNoteEvent(_note, false);
+
+ _note = 0xFF;
+ _duration = 0;
+}
+
+void SoundChannel_PC9801::noteOn(uint8 note, uint8 velo) {
+ _duration = 0;
+
+ if (_program != _parts[_assign]->program() && _soundOn) {
+ _program = _parts[_assign]->program();
+ programChange(_program);
+ }
+
+ if (_version > SCI_VERSION_0_LATE) {
+ velo >>= 1;
+ if (velo > 63)
+ velo = 63;
+ _velo = velo;
+ }
+
+ processNoteEvent(note, true);
+}
+
+void SoundChannel_PC9801::processNoteEvent(uint8 note, bool noteOn) {
+ if (_note != note) {
+ _note = note;
+ _vbrEnvelopeTimer = _vbrInitialDelay;
+ _vbrFrequencyModifier = 0;
+ _vbrCur = 0x80;
+ _flags |= kChanVbrRestartEnv;
+ }
+
+ prepareFrequencyAndVolume(noteOn);
+ sendSoundOnOff(noteOn);
+}
+
+void SoundChannel_PC9801::update() {
+ processSounds();
+ if (_note != 0xFF)
+ _duration++;
+}
+
+void SoundChannel_PC9801::setVolume(uint8 volume) {
+ static const uint8 volumeTable[] = { 0x7F, 0x7F, 0x30, 0x30, 0x28, 0x28, 0x20, 0x20, 0x18, 0x18, 0x10, 0x10, 0x08, 0x08, 0x00, 0x00 };
+ assert(volume < 16);
+ _volume = volumeTable[volume];
+}
+
+void SoundChannel_PC9801::reset() {
+ _assign = 0xFF;
+ _volume = 0;
+}
+
+int SoundChannel_PC9801::recalculateFrequency(uint16 note, uint16 modifier, uint8 *destOctaveBlock, uint16 *destFrequency, uint8 *destVbrFrequencyModifier) {
+ note += _transpose;
+ uint16 pb = _parts[_assign]->pitchBend();
+
+ if (pb == 0x2000) {
+ pb = 0;
+ } else {
+ int dir = 1;
+ if (pb > 0x2000) {
+ pb -= 0x2000;
+ } else if (pb < 0x2000) {
+ pb = 0x2000 - pb;
+ dir = -1;
+ }
+
+ uint8 noteDiff = pb / 684;
+ note += (noteDiff * dir);
+
+ pb %= 684;
+ if (pb > 682)
+ pb = 682;
+ pb = ((pb * 6) >> 4);
+ pb *= dir;
+ }
+
+ note += (modifier >> 8);
+ pb += (modifier & 0xff);
+ if ((pb >> 8) == 1) {
+ pb &= 0xFF;
+ note++;
+ }
+
+ if (note < _noteRangeLow || note > _noteRangeHigh)
+ return -1;
+
+ uint8 block = (note / 12) - 1;
+ note %= 12;
+ uint16 res = _noteFrequency[note];
+ uint16 pitchVbrMultiplier = _noteFrequencyModifier[note];
+
+ if (_type != 2)
+ res |= (block << 11);
+
+ if (pb)
+ res += (((pb * pitchVbrMultiplier) & 0x0FF0) >> 8);
+
+ if (res < 0x4000) {
+ if (destFrequency)
+ *destFrequency = res;
+ if (destOctaveBlock)
+ *destOctaveBlock = block;
+ if (destVbrFrequencyModifier)
+ *destVbrFrequencyModifier = pitchVbrMultiplier;
+ return res;
+ }
+
+ return -1;
+}
+
+uint8 SoundChannel_PC9801::getVolume() {
+ static const uint8 volumeTable1[] = {
+ 0x00, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x12, 0x13, 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2d, 0x2d, 0x2e,
+ 0x2f, 0x30, 0x31, 0x32, 0x32, 0x33, 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a,
+ 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f
+ };
+
+ static const uint8 volumeTable2[] = {
+ 0x00, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x61,
+ 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x6f, 0x70,
+ 0x71, 0x72, 0x72, 0x73, 0x74, 0x74, 0x75, 0x76, 0x76, 0x77, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a,
+ 0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f
+ };
+
+ uint16 partVolume = ((_assign != 0xFF) ? _parts[_assign]->volume() : 0) + 1;
+ uint16 velocity = volumeTable1[_velo] + 1;
+ uint16 volume = _soundOn ? (partVolume * velocity) >> 6 : 0;
+ volume = volumeTable2[volume] - _volume;
+
+ return (volume < 0x7F ? volume : 0);
+}
+
+void SoundChannel_PC9801::processSounds() {
+ if (!(_flags & kChanVbrEnable))
+ return;
+
+ if (_flags & kChanVbrRestartEnv) {
+ if (--_vbrEnvelopeTimer)
+ return;
+
+ _vbrIncrStep = _vbrDepthIncr * _vbrSensitivity * _frequencyNoteModifier;
+ _vbrDecrStep = _vbrDepthDecr * _vbrSensitivity * _frequencyNoteModifier;
+ _vbrModulationTimer = ((_flags & kChanVbrMode) ? _vbrIncrTime : _vbrDecrTime) >> 1;
+ _vbrCur = 0x80;
+
+ _flags &= ~(kChanVbrDecrease | kChanVbrRestartEnv);
+ if (_flags & kChanVbrMode)
+ _flags |= kChanVbrDecrease;
+ }
+
+ uint16 t = _vbrEnvelopeTimer + _vbrEnvelopeSpeed;
+ _vbrEnvelopeTimer = t & 0xFF;
+ if (t & 0x100)
+ return;
+
+ if (!--_vbrModulationTimer) {
+ _vbrModulationTimer = (_flags & kChanVbrDecrease) ? _vbrDecrTime : _vbrIncrTime;
+ _flags ^= kChanVbrDecrease;
+ }
+
+ if (_flags & kChanVbrDecrease) {
+ int16 s = _vbrCur - (_vbrDecrStep & 0xFF);
+ _vbrCur = s & 0xFF;
+ if (s >= 0)
+ return;
+ _vbrFrequencyModifier -= ((_vbrDecrStep >> 8) + 1);
+ } else {
+ uint16 s = _vbrCur + (_vbrIncrStep & 0xFF);
+ _vbrCur = s & 0xFF;
+ if (s <= 255)
+ return;
+ _vbrFrequencyModifier += ((_vbrIncrStep >> 8) + 1);
+ }
+
+ sendFrequency();
+}
+
+void SoundChannel_PC9801::programChangeInit(const uint8 *data) {
+ _transpose = (int8)(data[0] + data[0]);
+ _transpose >>= 1;
+ _vbrInitialDelay = data[1];
+ _vbrDepthIncr = data[2];
+ _vbrDecrTime = data[3];
+ _vbrDepthDecr = data[4];
+ _vbrIncrTime = data[5];
+ _vbrSensitivity = (data[6] & 3) + 1;
+ _vbrEnvelopeSpeed = data[6] >> 3;
+ _flags = (_flags & ~(kChanVbrEnable | kChanVbrMode)) | ((data[6] << 4) & 0x40);
+ if (_vbrInitialDelay)
+ _flags |= (kChanVbrEnable | kChanVbrRestartEnv);
+}
+
+void SoundChannel_PC9801::writeReg(uint8 part, uint8 reg, uint8 val) {
+ _pc98a->writeReg(part, reg, val);
+}
+
+void SoundChannel_PC9801::prepareFrequencyAndVolume(bool updateVolume) {
+ if (recalculateFrequency(_note, 0, &_frequencyBlock, &_frequencyCourse, &_frequencyNoteModifier) == -1)
+ return;
+ sendFrequency();
+ if (updateVolume)
+ sendVolume();
+}
+
+SoundChannel_PC9801_FM4OP::SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn) : SoundChannel_PC9801(pc98a, parts, version, 0, soundOn),
+_carrier(0), _regPrt(id > 3 ? 1 : 0), _regOffs(id % 3), _patchSize(patchSize) {
+ _operatorLevel[0] = _operatorLevel[1] = _operatorLevel[2] = _operatorLevel[3] = 0x7F;
+}
+
+void SoundChannel_PC9801_FM4OP::programChange(uint8 program) {
+ static const uint8 carrier[] = { 0x10, 0x10, 0x10, 0x10, 0x30, 0x70, 0x70, 0xF0 };
+ const uint8 *pos = _instrumentData + program * _patchSize;
+
+ programChangeInit(pos);
+ pos += 8;
+
+ for (uint8 i = 0x40 + _regOffs; i < 0x50 + _regOffs; i += 4)
+ writeReg(_regPrt, i, 0xFF);
+ for (uint8 i = 0x30 + _regOffs; i < 0x40 + _regOffs; i += 4)
+ writeReg(_regPrt, i, *pos++);
+ for (int i = 0; i < 4; ++i)
+ _operatorLevel[i] = *pos++;
+ for (uint8 i = 0x50 + _regOffs; i < 0xA0 + _regOffs; i += 4)
+ writeReg(_regPrt, i, *pos++);
+ writeReg(_regPrt, 0xB0 + _regOffs, *pos);
+ _carrier = carrier[*pos & 7];
+}
+
+void SoundChannel_PC9801_FM4OP::sendSoundOnOff(bool noteOn) {
+ _flags = noteOn ? (_flags | kChanKeyOn) : (_flags & ~kChanKeyOn);
+ writeReg(_regPrt, 0x28, _regOffs | (noteOn ? 0xF0 : 0));
+}
+
+void SoundChannel_PC9801_FM4OP::sendVolume() {
+ uint8 vol = getVolume();
+ uint16 c = _carrier;
+
+ for (uint8 i = 0; i < 4; ++i) {
+ uint8 r = _operatorLevel[i];
+ c += c;
+ if (c & 0x100) {
+ r = (((r ^ 0x7F) * vol) / 0x7F) * 2;
+ r = ((r < 0x7F) ? 0x7F - r : 0) + 20;
+ if (r > 0x7F)
+ r = 0x7F;
+ }
+ writeReg(_regPrt, 0x40 + (i << 2) + _regOffs, r);
+ }
+}
+
+void SoundChannel_PC9801_FM4OP::sendFrequency() {
+ uint16 freq = _frequencyCourse + _vbrFrequencyModifier;
+ writeReg(_regPrt, 0xA4 + _regOffs, freq >> 8);
+ writeReg(_regPrt, 0xA0 + _regOffs, freq & 0xFF);
+}
+
+uint8 SoundChannel_PC9801_FM2OP::_activeOperators = 0;
+
+SoundChannel_PC9801_FM2OP::SoundChannel_PC9801_FM2OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn) : SoundChannel_PC9801(pc98a, parts, version, 1, soundOn),
+_patchOffset(37), _patchSize(patchSize), _frequencyCourse2(0), _frequencyNoteModifier2(0), _vbrCur2(0x80), _vbrIncrStep2(0), _vbrDecrStep2(0), _regPrt(id > 3 ? 1 : 0), _regOffs(id & 1) {
+ static const uint16 opFreqOffset[] = { 0x0000, 0x0600, 0x07CF, 0x0980 };
+ _operatorLevel[0] = _operatorLevel[1] = 0x7F;
+ _operatorFrqIndex[0] = _operatorFrqIndex[1] = 0;
+ _opFreqOffset = opFreqOffset;
+}
+
+void SoundChannel_PC9801_FM2OP::processNoteEvent(uint8 note, bool noteOn) {
+ if (_note != note) {
+ _vbrCur2 = 0x80;
+ _vbrFrequencyModifier2 = 0;
+ }
+ SoundChannel_PC9801::processNoteEvent(note, noteOn);
+}
+
+void SoundChannel_PC9801_FM2OP::reset() {
+ SoundChannel_PC9801::reset();
+ _activeOperators = 0;
+}
+
+void SoundChannel_PC9801_FM2OP::programChange(uint8 program) {
+ const uint8 *pos = _instrumentData + program * _patchSize + _patchOffset;
+
+ programChangeInit(pos);
+ pos += 7;
+
+ for (uint8 i = 0x42 + (_regOffs << 2); i < 0x52 + (_regOffs << 2); i += 8)
+ writeReg(_regPrt, i, 0xFF);
+ for (uint8 i = 0x32 + (_regOffs << 2); i < 0x42 + (_regOffs << 2); i += 8)
+ writeReg(_regPrt, i, *pos++);
+ for (int i = 0; i < 2; ++i)
+ _operatorLevel[i] = *pos++;
+ for (uint8 i = 0x52 + (_regOffs << 2); i < 0x72 + (_regOffs << 2); i += 8)
+ writeReg(_regPrt, i, *pos++);
+ _operatorFrqIndex[0] = pos[0] >> 6;
+ _operatorFrqIndex[1] = pos[1] >> 6;
+ for (uint8 i = 0x72 + (_regOffs << 2); i < 0xA2 + (_regOffs << 2); i += 8)
+ writeReg(_regPrt, i, *pos++);
+}
+
+void SoundChannel_PC9801_FM2OP::prepareFrequencyAndVolume(bool updateVolume) {
+ if (recalculateFrequency(_note, _opFreqOffset[_operatorFrqIndex[0]], 0, &_frequencyCourse, &_frequencyNoteModifier) == -1)
+ return;
+ if (recalculateFrequency(_note, _opFreqOffset[_operatorFrqIndex[1]], 0, &_frequencyCourse2, &_frequencyNoteModifier2) == -1)
+ return;
+ sendFrequency();
+ if (updateVolume)
+ sendVolume();
+}
+
+void SoundChannel_PC9801_FM2OP::sendSoundOnOff(bool noteOn) {
+ uint8 op = 0x30 << (_regOffs << 1);
+
+ if (noteOn) {
+ _flags |= kChanKeyOn;
+ _activeOperators |= op;
+ } else {
+ _flags &= ~kChanKeyOn;
+ _activeOperators &= ~op;
+ }
+
+ writeReg(_regPrt, 0x28, _activeOperators | 2);
+}
+
+void SoundChannel_PC9801_FM2OP::sendVolume() {
+ writeReg(_regPrt, 0x42 + (_regOffs << 2), _operatorLevel[0]);
+ uint8 r = (((_operatorLevel[1] ^ 0x7F) * getVolume()) / 0x7F) * 2;
+ r = ((r < 0x7F) ? 0x7F - r : 0) + 16;
+ writeReg(_regPrt, 0x4A + (_regOffs << 2), r);
+}
+
+void SoundChannel_PC9801_FM2OP::processSounds() {
+ if (!(_flags & kChanVbrEnable))
+ return;
+
+ if (_flags & kChanVbrRestartEnv) {
+ if (--_vbrEnvelopeTimer)
+ return;
+
+ _vbrIncrStep = _vbrDepthIncr * _vbrSensitivity * _frequencyNoteModifier;
+ _vbrIncrStep2 = _vbrDepthIncr * _vbrSensitivity * _frequencyNoteModifier2;
+ _vbrDecrStep = _vbrDepthDecr * _vbrSensitivity * _frequencyNoteModifier;
+ _vbrDecrStep2 = _vbrDepthDecr * _vbrSensitivity * _frequencyNoteModifier2;
+ _vbrModulationTimer = ((_flags & kChanVbrMode) ? _vbrIncrTime : _vbrDecrTime) >> 1;
+ _vbrCur = 0x80;
+
+ _flags &= ~(kChanVbrDecrease | kChanVbrRestartEnv);
+ if (_flags & kChanVbrMode)
+ _flags |= kChanVbrDecrease;
+ }
+
+ uint16 t = _vbrEnvelopeTimer + _vbrEnvelopeSpeed;
+ _vbrEnvelopeTimer = t & 0xFF;
+ if (t & 0x100)
+ return;
+
+ if (!--_vbrModulationTimer) {
+ _vbrModulationTimer = (_flags & kChanVbrDecrease) ? _vbrDecrTime : _vbrIncrTime;
+ _flags ^= kChanVbrDecrease;
+ }
+
+ if (_flags & kChanVbrDecrease) {
+ int16 s = _vbrCur - (_vbrDecrStep & 0xFF);
+ _vbrCur = s & 0xFF;
+ if (s < 0)
+ _vbrFrequencyModifier -= ((_vbrDecrStep >> 8) + 1);
+ s = _vbrCur2 - (_vbrDecrStep2 & 0xFF);
+ _vbrCur2 = s & 0xFF;
+ if (s < 0)
+ _vbrFrequencyModifier2 -= ((_vbrDecrStep2 >> 8) + 1);
+ } else {
+ uint16 s = _vbrCur + (_vbrIncrStep & 0xFF);
+ _vbrCur = s & 0xFF;
+ if (s > 255)
+ _vbrFrequencyModifier += ((_vbrIncrStep >> 8) + 1);
+ s = _vbrCur2 + (_vbrIncrStep2 & 0xFF);
+ _vbrCur2 = s & 0xFF;
+ if (s > 255)
+ _vbrFrequencyModifier2 += ((_vbrIncrStep2 >> 8) + 1);
+ }
+
+ sendFrequency();
+}
+
+void SoundChannel_PC9801_FM2OP::sendFrequency() {
+ uint16 freq = _frequencyCourse + _vbrFrequencyModifier;
+ writeReg(_regPrt, 0xAD - _regOffs, freq >> 8);
+ writeReg(_regPrt, 0xA9 - _regOffs, freq & 0xFF);
+ freq = _frequencyCourse2 + _vbrFrequencyModifier2;
+ writeReg(_regPrt, 0xAE - (_regOffs << 3), freq >> 8);
+ writeReg(_regPrt, 0xAA - (_regOffs << 3), freq & 0xFF);
+}
+
+uint8 SoundChannel_PC9801_SSG::_activeChannnelsStatus = 0x3F;
+
+SoundChannel_PC9801_SSG::SoundChannel_PC9801_SSG(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchOffset, uint8 patchSize, bool &soundOn) : SoundChannel_PC9801(pc98a, parts, version, 2, soundOn),
+_patchOffset(patchOffset), _patchSize(patchSize), _regOffs(id & 3), _instrumentChanMask(0x3F), _ngPhaseStep(0), _currentLevel(0), _ssgEnvelopeState(kEnvSSG_silent), _ngEnvelopeTimer(0),
+_ngSpeed(0), _ssgEnvelopeTimer(0), _ssgLevel(0), _ssgSpeed(0) {
+ static const uint16 courseV0[] = { 0x82D, 0x8A9, 0x92D, 0x9B6, 0xA4D, 0xAEA, 0xB90, 0xC40, 0xCFA, 0xDC0, 0xE90, 0xF6F };
+ static const uint16 courseV1[] = { 0x82D, 0x8A9, 0x92D, 0x9B6, 0xA4D, 0xAEA, 0xB90, 0xC40, 0xCFA, 0xDC0, 0xE90, 0xF6F };
+ static const uint16 fine[] = { 0x7C, 0x84, 0x8B, 0x94, 0x9D, 0xA6, 0xB0, 0xBA, 0xC5, 0xD0, 0xDE, 0xEB };
+ static const uint8 noiseFrq[] = { 0x00, 0x01, 0x04, 0x07, 0x0A, 0x0D, 0x10, 0x13, 0x16, 0x19, 0x1C, 0x1F };
+ static const uint8 disableMask[] = { 0xF6, 0xED, 0xDB };
+ _noteFrequency = (version > SCI_VERSION_0_LATE) ? courseV1 : courseV0;
+ _noteFrequencyModifier = fine;
+ _ngFreq = noiseFrq;
+ _chanDisableMask = disableMask[id & 3];
+}
+
+void SoundChannel_PC9801_SSG::reset() {
+ SoundChannel_PC9801::reset();
+ _activeChannnelsStatus = 0x3F;
+ sendActiveChannelsStatus();
+}
+
+void SoundChannel_PC9801_SSG::programChange(uint8 program) {
+ _selectedInstrument = _instrumentData + program * _patchSize + _patchOffset;
+ programChangeInit(_selectedInstrument);
+
+ _flags &= ~kChanNgEnable;
+ if (_selectedInstrument[7])
+ _flags |= (kChanNgEnable | kChanNgRestartEnv);
+ _instrumentChanMask = _selectedInstrument[22];
+ _activeChannnelsStatus = (_activeChannnelsStatus & _chanDisableMask) | (~_chanDisableMask & (_instrumentChanMask & 0x3F));
+ if (!(_instrumentChanMask & 8)) {
+ _ngPhaseStep = _selectedInstrument[21] & 0x1F;
+ updateNg();
+ }
+ sendActiveChannelsStatus();
+ _currentLevel = 0;
+ _ssgEnvelopeState = kEnvSSG_silent;
+}
+
+void SoundChannel_PC9801_SSG::processSounds() {
+ if (_ssgEnvelopeState == kEnvSSG_silent)
+ return;
+
+ SoundChannel_PC9801::processSounds();
+
+ if (!(_instrumentChanMask & 0x38) && (_flags & kChanNgEnable)) {
+ bool cont = false;
+ if (_flags & kChanNgRestartEnv) {
+ if (--_ngEnvelopeTimer) {
+ cont = true;
+ } else {
+ uint8 flg = _selectedInstrument[10] & 4;
+ _ngSpeed = ((flg ? _selectedInstrument[9] : _selectedInstrument[8]) & 0x1F) >> 1;
+ _flags = (_flags & ~(kChanNgDecrease | kChanNgRestartEnv)) | (flg << 3);
+ }
+ }
+ if (!cont) {
+ uint16 s = _ngEnvelopeTimer + (_selectedInstrument[10] >> 3);
+ _ngEnvelopeTimer = s & 0xFF;
+ if (s & 0x100)
+ cont = true;
+ if (!cont) {
+ if (--_ngSpeed) {
+ if (_flags & kChanNgDecrease) {
+ _ngSpeed = _selectedInstrument[8] & 0x1F;
+ _flags &= ~kChanNgDecrease;
+ } else {
+ _ngSpeed = _selectedInstrument[9] & 0x1F;
+ _flags |= kChanNgDecrease;
+ }
+ }
+
+ if (_flags & kChanNgDecrease) {
+ _ngPhase -= (_selectedInstrument[9] >> 5) * ((_selectedInstrument[10] & 3) + 1);
+ } else {
+ _ngPhase += (_selectedInstrument[8] >> 5) * ((_selectedInstrument[10] & 3) + 1);
+ }
+ updateNg();
+ }
+ }
+ }
+
+ uint16 s = _ssgEnvelopeTimer + (_selectedInstrument[21] & 0x60);
+ _ssgEnvelopeTimer = s & 0xFF;
+ if ((s & 0x100) || !(_ssgEnvelopeState & 0x0F))
+ return;
+
+ uint8 vol = _currentLevel;
+ if (_ssgLevel & 1)
+ vol = (vol > (_ssgLevel & ~1)) ? vol - (_ssgLevel & ~1) : 0;
+ else
+ vol = (vol + _ssgLevel > 255) ? 255 : vol + _ssgLevel;
+
+ if (!--_ssgSpeed) {
+ if (--_ssgEnvelopeState == (kEnvSSG_keyOn | kEnvSSG_sustain)) {
+ _ssgLevel = _selectedInstrument[13];
+ _ssgSpeed = _selectedInstrument[14];
+ vol = (uint8)CLIP<int16>(vol + (int8)(_selectedInstrument[19] & 0xF0), 0, 255);
+ }
+ if (_ssgEnvelopeState == (kEnvSSG_keyOn | kEnvSSG_decay)) {
+ _ssgLevel = _selectedInstrument[15];
+ _ssgSpeed = _selectedInstrument[16];
+ vol = (uint8)CLIP<int16>(vol + (int8)(_selectedInstrument[20] << 4), 0, 255);
+ }
+ }
+
+ if (_ssgEnvelopeState == kEnvSSG_silent)
+ vol = 0;
+
+ if (_currentLevel != vol) {
+ _currentLevel = vol;
+ sendVolume();
+ }
+}
+
+void SoundChannel_PC9801_SSG::sendSoundOnOff(bool noteOn) {
+ if (noteOn && !(_ssgEnvelopeState & kEnvSSG_keyOn)) {
+ _currentLevel = _selectedInstrument[19] << 4;
+ _ssgEnvelopeState = (kEnvSSG_keyOn | kEnvSSG_attack);
+ _ssgLevel = _selectedInstrument[11];
+ _ssgSpeed = _selectedInstrument[12];
+ _ssgEnvelopeTimer = 1;
+ if (!(_selectedInstrument[22] & 0x38)) {
+ if (_selectedInstrument[21] & 0x80)
+ _ngPhaseStep = _ngFreq[_note % 12];
+ _ngEnvelopeTimer = _selectedInstrument[7];
+ _ngPhase = 0;
+ updateNg();
+ _flags |= kChanNgRestartEnv;
+ }
+ } else if (!noteOn) {
+ int8 v = (int8)(_selectedInstrument[20] & 0xF0);
+ int16 l = _currentLevel + v;
+ _currentLevel = uint8(CLIP<int16>(l, 0, 255) & 0xFF);
+ _ssgEnvelopeState = kEnvSSG_decay;
+ _ssgLevel = _selectedInstrument[17];
+ _ssgSpeed = _selectedInstrument[18];
+ _note = 0xFF;
+ }
+ sendVolume();
+}
+
+void SoundChannel_PC9801_SSG::sendVolume() {
+ uint8 v1 = getVolume();
+ uint8 vol = v1 + (((v1 >> 1) + v1) >> 2);
+
+ if (vol > 0x7F)
+ vol = 0x7F;
+ vol = (vol >> 3) & 0x0F;
+
+ uint16 r = _currentLevel & 0xF0;
+ for (uint8 i = 0; i < 4; ++i) {
+ r += r;
+ if (r & 0x100)
+ r = (r + vol) & 0xFF;
+ }
+
+ writeReg(0, 8 + _regOffs, (r + 15) >> 4);
+}
+
+void SoundChannel_PC9801_SSG::sendFrequency() {
+ uint16 freq = (_frequencyCourse + _vbrFrequencyModifier) >> (8 - _frequencyBlock);
+ if (!freq)
+ return;
+ freq = 62400 / freq;
+ writeReg(0, _regOffs << 1, freq & 0xFF);
+ writeReg(0, (_regOffs << 1) + 1, freq >> 8);
+}
+
+void SoundChannel_PC9801_SSG::updateNg() {
+ int p = _ngPhase + _ngPhaseStep;
+ if (p > 255)
+ p = 0;
+ if (p > 31)
+ p = 31;
+ _ngPhase = p & 0xFF;
+ writeReg(0, 6, _ngPhase);
+}
+
+void SoundChannel_PC9801_SSG::sendActiveChannelsStatus() {
+ writeReg(0, 7, (_activeChannnelsStatus & 0x3F) | 0x80);
+}
+
+MidiPart_PC9801::MidiPart_PC9801(uint8 id, SoundChannel_PC9801 **channels, uint8 numChan, SciVersion version) : _chan(channels), _id(id), _version(version), _numChan(numChan),
+ _program(0), _volume(0x3f), _sustain(0), _chanMissing(0), _pitchBend(0x2000), _outChan(0), _noteRangeLow(version > SCI_VERSION_0_LATE ? 12 : 24), _noteRangeHigh(version > SCI_VERSION_0_LATE ? 107 : 119) {
+}
+
+void MidiPart_PC9801::noteOff(uint8 note) {
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign != _id || _chan[i]->_note != note)
+ continue;
+ if (_sustain && _version > SCI_VERSION_0_LATE)
+ _chan[i]->_sustain = 1;
+ else
+ _chan[i]->noteOff();
+ return;
+ }
+}
+
+void MidiPart_PC9801::noteOn(uint8 note, uint8 velo) {
+ if (note < _noteRangeLow || note > _noteRangeHigh)
+ return;
+
+ if (velo == 0) {
+ noteOff(note);
+ return;
+ }
+
+ velo >>= 1;
+
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign != _id || _chan[i]->_note != note)
+ continue;
+ _chan[i]->_sustain = 0;
+ _chan[i]->noteOff();
+ _chan[i]->noteOn(note, velo);
+ return;
+ }
+
+ int chan = allocateChannel();
+ if (chan != -1)
+ _chan[chan]->noteOn(note, velo);
+}
+
+void MidiPart_PC9801::controlChangeVolume(uint8 vol) {
+ _volume = CLIP(vol >> 1, 0, 0x3f);
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign == _id && _chan[i]->_note != 0xff)
+ _chan[i]->processNoteEvent(_chan[i]->_note, true);
+ }
+}
+
+void MidiPart_PC9801::controlChangeSustain(uint8 sus) {
+ _sustain = sus;
+ if (_sustain)
+ return;
+
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign == _id && _chan[i]->_sustain) {
+ _chan[i]->_sustain = 0;
+ _chan[i]->noteOff();
+ }
+ }
+}
+
+void MidiPart_PC9801::controlChangePolyphony(uint8 numChan) {
+ uint8 numAssigned = 0;
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign == _id)
+ numAssigned++;
+ }
+
+ numAssigned += _chanMissing;
+ if (numAssigned < numChan) {
+ addChannels(numChan - numAssigned);
+ } else if (numAssigned > numChan) {
+ dropChannels(numAssigned - numChan);
+ assignFreeChannels();
+ }
+}
+
+void MidiPart_PC9801::controlChangeAllNotesOff() {
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign == _id && _chan[i]->_note != 0xff)
+ _chan[i]->noteOff();
+ }
+}
+
+void MidiPart_PC9801::programChange(uint8 prg) {
+ _program = prg;
+}
+
+void MidiPart_PC9801::pitchBend(int16 val) {
+ _pitchBend = val;
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign == _id && _chan[i]->_note != 0xff)
+ _chan[i]->processNoteEvent(_chan[i]->_note, true);
+ }
+}
+
+
+void MidiPart_PC9801::addChannels(int num, int resetMissingChannels) {
+ if (resetMissingChannels != -1)
+ _chanMissing = resetMissingChannels;
+
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign != 0xFF)
+ continue;
+
+ _chan[i]->_assign = _id;
+
+#ifdef DEBUG_REMAP
+ debug("===== MidiDriver_PC9801: hardware channel %d is assigned to device channel %d =====", i, _id);
+#endif
+ if (_chan[i]->_note != 0xff)
+ _chan[i]->noteOff();
+
+ if (!--num)
+ break;
+ }
+ _chanMissing += num;
+}
+
+void MidiPart_PC9801::dropChannels(int num) {
+ if (_chanMissing == num) {
+ _chanMissing = 0;
+ return;
+ } else if (_chanMissing > num) {
+ _chanMissing -= num;
+ return;
+ }
+
+ num -= _chanMissing;
+ _chanMissing = 0;
+
+ for (int i = 0; i < _numChan; i++) {
+ if (_chan[i]->_assign != _id || _chan[i]->_note != 0xff)
+ continue;
+ _chan[i]->_assign = 0xff;
+
+#ifdef DEBUG_REMAP
+ debug("===== MidiDriver_PC9801: hardware channel %d is dropped from device channel %d =====", i, _id);
+#endif
+
+ if (!--num)
+ return;
+ }
+
+ for (int i = 0; i < _numChan; i++) {
+ if (_chan[i]->_assign != _id)
+ continue;
+ _chan[i]->_sustain = 0;
+ _chan[i]->noteOff();
+ _chan[i]->_assign = 0xff;
+ if (!--num)
+ return;
+ }
+}
+
+int MidiPart_PC9801::allocateChannel() {
+ int chan = _outChan;
+ int ovrChan = 0;
+ int ld = 0;
+ bool found = false;
+
+ for (bool loop = true; loop; ) {
+ if (++chan == _numChan)
+ chan = 0;
+
+ if (chan == _outChan)
+ loop = false;
+
+ if (_id == _chan[chan]->_assign) {
+ if (_chan[chan]->_note == 0xff || _version < SCI_VERSION_1_LATE) {
+ found = true;
+ break;
+ }
+
+ if (_chan[chan]->_duration >= ld) {
+ ld = _chan[chan]->_duration;
+ ovrChan = chan;
+ }
+ }
+ }
+
+ if (!found) {
+ if (!ld)
+ return -1;
+ chan = ovrChan;
+ _chan[chan]->_sustain = 0;
+ _chan[chan]->noteOff();
+ }
+
+ _outChan = chan;
+ return chan;
+}
+
+void MidiPart_PC9801::assignFreeChannels() {
+ uint8 freeChan = 0;
+ for (int i = 0; i < _numChan; i++) {
+ if (_chan[i]->_assign == 0xff)
+ freeChan++;
+ }
+
+ if (!freeChan)
+ return;
+
+ MidiDriver_PC9801::assignFreeChannels(freeChan);
+}
+
+MidiPart_PC9801 **MidiDriver_PC9801::_parts = 0;
+
+MidiDriver_PC9801::MidiDriver_PC9801(Audio::Mixer *mixer, SciVersion version) : _version(version), _pc98a(0), _chan(0), _numChan(6), _internalVersion(0xFF), _ssgPatchOffset(0xFF),
+_patchSize(0), _instrumentData(0), _timerProc(0), _timerProcPara(0), _baseTempo(10080), _ready(false), _isOpen(false), _masterVolume(0x0f) ,_soundOn(true),
+_playID(0), _channelMask1(0x10), _channelMask2(0x02) {
+ _pc98a =
+#ifdef SCI_PC98_AUDIO_EXTENDED
+ new PC98AudioCore(mixer, this, kType86, true);
+#else
+ new PC98AudioCore(mixer, this, kType26, true);
+#endif
+}
+
+MidiDriver_PC9801::~MidiDriver_PC9801() {
+ _ready = false;
+ delete _pc98a;
+ close();
+}
+
+int MidiDriver_PC9801::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ if (!_ready) {
+ if (!_pc98a->init())
+ return MERR_CANNOT_CONNECT;
+ _pc98a->setSoundEffectChanMask(0);
+ _pc98a->ssgSetVolume(205);
+ _ready = true;
+ }
+
+ ResourceManager *resMan = g_sci->getResMan();
+ loadInstruments(*resMan->findResource(ResourceId(kResourceTypePatch, _version < SCI_VERSION_1_LATE ? 2 : 8), false));
+
+ if (_ssgPatchOffset == 0xFF || _patchSize == 0)
+ return MERR_CANNOT_CONNECT;
+
+ if (_version < SCI_VERSION_1_LATE)
+ _internalVersion = 0;
+ else if (_patchSize == 60 && _ssgPatchOffset == 37)
+ _internalVersion = 1;
+ else if (_patchSize == 81 && _ssgPatchOffset == 58)
+ _internalVersion = 2;
+ else
+ return MERR_CANNOT_CONNECT;
+
+ if (_internalVersion == 2)
+ _numChan++;
+
+#ifdef SCI_PC98_AUDIO_EXTENDED
+ _numChan += 3;
+ if (_internalVersion == 2)
+ _numChan++;
+#endif
+
+ static const int channelConfig[5][11] = {
+ { 0, 0, 0, 2, 2, 2, -1, -1, -1, -1, -1 },
+ { 0, 0, 0, 2, 2, 2, -1, -1, -1, -1, -1 },
+ { 0, 0, 1, 1, 2, 2, 2, -1, -1, -1, -1 },
+ { 0, 0, 0, 0, 0, 0, 2, 2, 2, -1, -1 },
+ { 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 2 }
+ };
+
+ _parts = new MidiPart_PC9801*[16];
+ _chan = new SoundChannel_PC9801*[_numChan];
+ int config = _internalVersion;
+
+#ifdef SCI_PC98_AUDIO_EXTENDED
+ config = (_internalVersion >> 1) + 3;
+#endif
+
+ int numSSG = 0;
+ for (int i = 0; i < _numChan; ++i) {
+ if (channelConfig[config][i] == 0)
+ _chan[i] = new SoundChannel_PC9801_FM4OP(i, _pc98a, _parts, _version, _patchSize, _soundOn);
+ else if (channelConfig[config][i] == 1)
+ _chan[i] = new SoundChannel_PC9801_FM2OP(i, _pc98a, _parts, _version, _patchSize, _soundOn);
+ else if (channelConfig[config][i] == 2)
+ _chan[i] = new SoundChannel_PC9801_SSG(numSSG++, _pc98a, _parts, _version, _ssgPatchOffset, _patchSize, _soundOn);
+ else
+ _chan[i] = 0;
+ }
+
+ for (int i = 0; i < 16; ++i)
+ _parts[i] = new MidiPart_PC9801(i, _chan, _numChan, _version);
+
+ _isOpen = true;
+
+ reset();
+
+ return 0;
+}
+
+void MidiDriver_PC9801::close() {
+ if (!_isOpen)
+ return;
+
+ bool ready = _ready;
+ _isOpen = _ready = false;
+
+ if (_parts) {
+ for (int i = 0; i < 16; ++i) {
+ delete _parts[i];
+ _parts[i] = 0;
+ }
+ delete[] _parts;
+ _parts = 0;
+ }
+
+ if (_chan) {
+ for (int i = 0; i < _numChan; ++i) {
+ delete _chan[i];
+ _chan[i] = 0;
+ }
+ delete[] _chan;
+ _chan = 0;
+ }
+
+ delete[] _instrumentData;
+ _instrumentData = 0;
+
+ _ready = ready;
+}
+
+void MidiDriver_PC9801::loadInstruments(const SciSpan<const uint8> &data) {
+ if (!data)
+ return;
+
+ SciSpan<const uint8> src = data;
+ delete[] _instrumentData;
+
+ if (_version == SCI_VERSION_0_LATE) {
+ _ssgPatchOffset = 48;
+ _patchSize = 52;
+
+ _instrumentData = new uint8[96 * _patchSize];
+ uint8 *dst = _instrumentData;
+
+ for (bool load = true; load; ) {
+ for (int i = 0; i < 48; ++i) {
+ src.subspan(0, _patchSize).unsafeCopyDataTo(dst);
+ src += 64;
+ dst += _patchSize;
+ }
+ uint16 id = (src.byteSize() >= 2) ? src.getInt16BEAt(0) : 0;
+ if (id == 0xABCD || id == 0xCDAB)
+ src += 2;
+ else
+ load = false;
+ }
+ } else if (_version == SCI_VERSION_1_LATE) {
+ int len = data.byteSize() - 1;
+ _instrumentData = new uint8[len];
+ uint8 *dst = _instrumentData;
+ (++src).unsafeCopyDataTo(dst);
+
+ _patchSize = len / 96;
+ _ssgPatchOffset = (_patchSize == 81) ? 58 : 37;
+ }
+
+ SoundChannel_PC9801::_instrumentData = _instrumentData;
+}
+
+void MidiDriver_PC9801::send(uint32 b) {
+ if (!_isOpen)
+ return;
+
+ byte para2 = (b >> 16) & 0xFF;
+ byte para1 = (b >> 8) & 0xFF;
+ byte cmd = b & 0xF0;
+
+ MidiPart_PC9801 *part = _parts[b & 0x0F];
+
+ switch (cmd) {
+ case 0x80:
+ part->noteOff(para1);
+ break;
+ case 0x90:
+ part->noteOn(para1, para2);
+ break;
+ case 0xb0:
+ switch (para1) {
+ case 7:
+ part->controlChangeVolume(para2);
+ break;
+ case 64:
+ part->controlChangeSustain(para2);
+ break;
+ case SCI_MIDI_SET_POLYPHONY:
+ part->controlChangePolyphony(para2);
+ break;
+ case SCI_MIDI_CHANNEL_NOTES_OFF:
+ part->controlChangeAllNotesOff();
+ break;
+ default:
+ break;
+ }
+ break;
+ case 0xc0:
+ part->programChange(para1);
+ break;
+ case 0xe0:
+ part->pitchBend(para1 | (para2 << 7));
+ break;
+ default:
+ break;
+ }
+}
+
+void MidiDriver_PC9801::assignFreeChannels(int num) {
+ assert(_parts);
+ for (int i = 0; i < 16; ++i) {
+ uint8 missing = _parts[i]->missingChannels();
+ if (!missing)
+ continue;
+ if (missing < num) {
+ num -= missing;
+ _parts[i]->addChannels(num, 0);
+ } else {
+ _parts[i]->addChannels(num, missing - num);
+ return;
+ }
+ }
+}
+
+uint32 MidiDriver_PC9801::property(int prop, uint32 param) {
+ if (!_isOpen)
+ return 0;
+
+ switch(prop) {
+ case MIDI_PROP_MASTER_VOLUME:
+ if (param != 0xffff && param != _masterVolume && param < 16) {
+ _masterVolume = param;
+ for (int i = 0; i < _numChan; ++i) {
+ _chan[i]->setVolume(_masterVolume);
+ if (_chan[i]->_note != 0xFF)
+ _chan[i]->processNoteEvent(_chan[i]->_note, true);
+ }
+ }
+ return _masterVolume;
+ case MIDI_PROP_PLAYSWITCH:
+ _soundOn = param;
+ break;
+ case MIDI_PROP_POLYPHONY:
+#ifdef SCI_PC98_AUDIO_EXTENDED
+ return _version < SCI_VERSION_1_LATE ? 9 : 12;
+#else
+ return _version < SCI_VERSION_1_LATE ? 6 : 9;
+#endif
+ case MIDI_PROP_CHANNEL_ID:
+ return _version < SCI_VERSION_1_LATE ? (_channelMask1 | _channelMask2) : _playID;
+ default:
+ break;
+ }
+ return 0;
+}
+
+void MidiDriver_PC9801::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
+ _timerProc = timer_proc;
+ _timerProcPara = timer_param;
+}
+
+uint32 MidiDriver_PC9801::getBaseTempo() {
+ return _baseTempo;
+}
+
+void MidiDriver_PC9801::timerCallbackB() {
+ if (!_isOpen)
+ return;
+ updateParser();
+ updateChannels();
+}
+
+void MidiDriver_PC9801::updateParser() {
+ if (_timerProc)
+ _timerProc(_timerProcPara);
+}
+
+void MidiDriver_PC9801::updateChannels() {
+ for (int i = 0; i < _numChan; ++i)
+ _chan[i]->update();
+}
+
+void MidiDriver_PC9801::reset() {
+ if (!_ready)
+ return;
+
+ for (int i = 0; i < 3; ++i) {
+ _pc98a->writeReg(0, 0x28, i);
+ _pc98a->writeReg(0, i, 0);
+ _pc98a->writeReg(0, 8 + i, 0);
+ }
+
+ uint8 flag = 0;
+ if (_internalVersion == 2) {
+ _pc98a->writeReg(0, 0xB2, 0x04);
+ flag = 0x40;
+ }
+
+ _pc98a->writeReg(0, 0x27, 0x38);
+ _pc98a->writeReg(0, 0x27, 0x3a | flag);
+
+ if (!_isOpen)
+ return;
+
+ for (int i = 0; i < _numChan; ++i)
+ _chan[i]->reset();
+}
+
+MidiPlayer_PC9801::MidiPlayer_PC9801(SciVersion version) : MidiPlayer(version) {
+ _driver = new MidiDriver_PC9801(g_system->getMixer(), version);
+}
+
+MidiPlayer_PC9801::~MidiPlayer_PC9801() {
+ delete _driver;
+}
+
+bool MidiPlayer_PC9801::hasRhythmChannel() const {
+ return false;
+}
+
+byte MidiPlayer_PC9801::getPlayId() const {
+ return _driver ? _driver->property(MIDI_PROP_CHANNEL_ID, 0) : 0;
+}
+
+int MidiPlayer_PC9801::getPolyphony() const {
+ return _driver ? _driver->property(MIDI_PROP_POLYPHONY, 0) : 0;
+}
+
+void MidiPlayer_PC9801::playSwitch(bool play) {
+ if (_driver)
+ _driver->property(MIDI_PROP_PLAYSWITCH, play ? 1 : 0);
+}
+
+MidiPlayer *MidiPlayer_PC9801_create(SciVersion _soundVersion) {
+ return new MidiPlayer_PC9801(_soundVersion);
+}
+
+} // End of namespace Sci
+
diff --git a/engines/sci/sound/music.cpp b/engines/sci/sound/music.cpp
index 0902ff8..700be3d 100644
--- a/engines/sci/sound/music.cpp
+++ b/engines/sci/sound/music.cpp
@@ -95,6 +95,9 @@ void SciMusic::init() {
deviceFlags |= MDT_TOWNS;
}
+ if (g_sci->getPlatform() == Common::kPlatformPC98)
+ deviceFlags |= MDT_PC98;
+
uint32 dev = MidiDriver::detectDevice(deviceFlags);
_musicType = MidiDriver::getMusicType(dev);
@@ -129,6 +132,9 @@ void SciMusic::init() {
case MT_TOWNS:
_pMidiDrv = MidiPlayer_FMTowns_create(_soundVersion);
break;
+ case MT_PC98:
+ _pMidiDrv = MidiPlayer_PC9801_create(_soundVersion);
+ break;
default:
if (ConfMan.getBool("native_fb01"))
_pMidiDrv = MidiPlayer_Fb01_create(_soundVersion);
Commit: 1fcefba4eedfd536ed6efd8c33cdc5c8fb404ac9
https://github.com/scummvm/scummvm/commit/1fcefba4eedfd536ed6efd8c33cdc5c8fb404ac9
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-02T20:45:35+03:00
Commit Message:
SCI: add SCI0 support to PC-98 sound driver
- this supports PQ2 and QFG1
- also add several other fixes
Changed paths:
engines/sci/sound/drivers/pc9801.cpp
diff --git a/engines/sci/sound/drivers/pc9801.cpp b/engines/sci/sound/drivers/pc9801.cpp
index 767779e..87a997b 100644
--- a/engines/sci/sound/drivers/pc9801.cpp
+++ b/engines/sci/sound/drivers/pc9801.cpp
@@ -39,15 +39,13 @@
King's Quest V SCI_VERSION_1_LATE latest driver type
*/
-//#define SCI_PC98_AUDIO_EXTENDED
-
namespace Sci {
class MidiPart_PC9801;
class SoundChannel_PC9801 {
public:
- SoundChannel_PC9801(PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, int type, bool &soundOn);
- virtual ~SoundChannel_PC9801() {}
+ SoundChannel_PC9801(PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, int type, SciSpan<const uint8> instrumentData, bool &soundOn);
+ virtual ~SoundChannel_PC9801();
void noteOff();
void noteOn(uint8 note, uint8 velo);
@@ -57,13 +55,14 @@ public:
void setVolume(uint8 volume);
virtual void reset();
+ virtual void toggleNoiseGenerator(bool) {}
+
+ int getType() const { return _type; }
uint8 _assign;
uint8 _note;
uint8 _sustain;
uint16 _duration;
-
- static const uint8 *_instrumentData;
protected:
enum ChannelStatusFlags {
@@ -80,7 +79,7 @@ protected:
int recalculateFrequency(uint16 note, uint16 modifier, uint8 *destOctaveBlock, uint16 *destFrequency, uint8 *destVbrFrequencyModifier);
uint8 getVolume();
virtual void processSounds();
- void programChangeInit(const uint8 *data);
+ void programChangeInit(SciSpan<const uint8> &data);
void writeReg(uint8 part, uint8 reg, uint8 val);
int8 _transpose;
@@ -100,13 +99,15 @@ protected:
uint16 _vbrIncrStep;
uint16 _vbrDecrStep;
uint8 _vbrModulationTimer;
- int16 _vbrFrequencyModifier;
+ uint16 _vbrFrequencyModifier;
uint8 _vbrCur;
uint8 _flags;
const uint16 *_noteFrequency;
const uint16 *_noteFrequencyModifier;
- const uint8 *_selectedInstrument;
+
+ SciSpan<const uint8> _instrumentData;
+ const SciVersion _version;
private:
virtual void programChange(uint8 program) = 0;
@@ -122,15 +123,12 @@ private:
MidiPart_PC9801 **_parts;
PC98AudioCore *_pc98a;
- const uint8 _noteRangeLow;
- const uint8 _noteRangeHigh;
const int _type;
- const SciVersion _version;
};
class SoundChannel_PC9801_FM4OP : public SoundChannel_PC9801 {
public:
- SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn);
+ SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchSize, bool &soundOn);
virtual ~SoundChannel_PC9801_FM4OP() {}
private:
@@ -141,6 +139,7 @@ private:
uint8 _operatorLevel[4];
uint8 _carrier;
+ uint8 _operatorFlags;
const uint8 _regPrt;
const uint8 _regOffs;
@@ -149,7 +148,7 @@ private:
class SoundChannel_PC9801_FM2OP : public SoundChannel_PC9801 {
public:
- SoundChannel_PC9801_FM2OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn);
+ SoundChannel_PC9801_FM2OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchSize, bool &soundOn);
virtual ~SoundChannel_PC9801_FM2OP() {}
void processNoteEvent(uint8 note, bool noteOn);
@@ -182,9 +181,10 @@ private:
class SoundChannel_PC9801_SSG : public SoundChannel_PC9801 {
public:
- SoundChannel_PC9801_SSG(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchOffset, uint8 patchSize, bool &soundOn);
+ SoundChannel_PC9801_SSG(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchOffset, uint8 patchSize, bool &soundOn);
virtual ~SoundChannel_PC9801_SSG() {}
void reset();
+ void toggleNoiseGenerator(bool enable);
private:
enum {
@@ -205,6 +205,8 @@ private:
uint8 _instrumentChanMask;
uint8 _chanDisableMask;
+ uint8 _chanEnableMask1;
+ uint8 _chanEnableMask2;
uint8 _currentLevel;
uint8 _ngPhaseStep;
uint8 _ngPhase;
@@ -214,8 +216,10 @@ private:
uint8 _ssgLevel;
uint8 _ssgSpeed;
uint8 _ssgEnvelopeState;
+ bool _ccngEnabled;
static uint8 _activeChannnelsStatus;
+ SciSpan<const uint8> _selectedInstrument;
const uint8 *_ngFreq;
const uint8 _regOffs;
@@ -233,17 +237,18 @@ public:
void controlChangeVolume(uint8 vol);
void controlChangeSustain(uint8 sus);
void controlChangePolyphony(uint8 numChan);
+ void controlChangeNoiseGenerator(uint8 status);
void controlChangeAllNotesOff();
void programChange(uint8 prg);
void pitchBend(int16 val);
- void addChannels(int num, int resetMissingChannels = -1);
+ void addChannels(int num, int resetMissingChannels = -1, int channelType = -1);
void dropChannels(int num);
- uint8 program() const { return _program; }
- uint8 volume() const { return _volume; }
- uint16 pitchBend() const { return _pitchBend; }
- uint8 missingChannels() const { return _chanMissing; }
+ uint8 getCurrentProgram() const { return _program; }
+ uint8 getCurrentVolume() const { return _volume; }
+ uint16 getCurrentPitchBend() const { return _pitchBend; }
+ uint8 getMissingChannels() const { return _chanMissing; }
private:
int allocateChannel();
@@ -282,6 +287,7 @@ public:
void send(uint32 b);
static void assignFreeChannels(int num);
uint32 property(int prop, uint32 param);
+ void initTrack(SciSpan<const byte> &header);
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc);
uint32 getBaseTempo();
@@ -291,7 +297,7 @@ public:
void timerCallbackB();
private:
- void loadInstruments(const SciSpan<const uint8> &data);
+ bool loadInstruments(const SciSpan<const uint8> &data);
void updateParser();
void updateChannels();
void reset();
@@ -301,19 +307,20 @@ private:
static MidiPart_PC9801 **_parts;
SoundChannel_PC9801 **_chan;
- uint8 *_instrumentData;
+ Common::SpanOwner<SciSpan<uint8> > _instrumentData;
uint8 _masterVolume;
bool _soundOn;
uint8 _numChan;
- uint8 _internalVersion;
uint8 _ssgPatchOffset;
uint8 _patchSize;
+ uint8 _internalVersion;
const uint8 _playID;
const uint8 _channelMask1;
- const uint8 _channelMask2;
+ uint8 _channelMask2;
+ uint8 _polyphony;
bool _isOpen;
bool _ready;
@@ -339,22 +346,29 @@ public:
byte getPlayId() const;
int getPolyphony() const;
void playSwitch(bool play);
+
+ void initTrack(SciSpan<const byte> &trackData);
};
-const uint8 *SoundChannel_PC9801::_instrumentData = 0;
+SoundChannel_PC9801::SoundChannel_PC9801(PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, int type, SciSpan<const uint8> instrumentData, bool &soundOn)
+ : _pc98a(pc98a), _parts(parts), _version(version), _type(type), _instrumentData(instrumentData), _soundOn(soundOn), _assign(0xff), _note(0xff), _velo(0),
+ _volume(0), _transpose(0), _sustain(0), _duration(0), _program(0xff), _vbrInitialDelay(0), _vbrEnvelopeTimer(0),
+ _vbrEnvelopeSpeed(0), _vbrDepthIncr(0), _vbrDecrTime(0), _vbrDepthDecr(0), _vbrIncrTime(0), _vbrSensitivity(0),
+ _vbrFrequencyModifier(0), _vbrIncrStep(0), _vbrDecrStep(0), _vbrModulationTimer(0), _flags(0), _vbrCur(0x80),
+ _frequencyBlock(0), _frequencyCourse(0), _frequencyNoteModifier(0) {
-SoundChannel_PC9801::SoundChannel_PC9801(PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, int type, bool &soundOn) : _pc98a(pc98a), _parts(parts), _version(version),
-_type(type), _soundOn(soundOn), _assign(0xff), _note(0xff), _velo(0x7F), _volume(0), _transpose(0), _sustain(0), _duration(0), _program(0xff), _vbrInitialDelay(0),
-_vbrEnvelopeTimer(0), _vbrEnvelopeSpeed(0), _vbrDepthIncr(0), _vbrDecrTime(0), _vbrDepthDecr(0), _vbrIncrTime(0), _vbrSensitivity(0), _vbrFrequencyModifier(0), _vbrIncrStep(0),
-_vbrDecrStep(0), _vbrModulationTimer(0), _flags(0), _vbrCur(0x80), _frequencyBlock(0), _frequencyCourse(0), _frequencyNoteModifier(0),
-_noteRangeLow(version > SCI_VERSION_0_LATE ? 12: 24), _noteRangeHigh(version > SCI_VERSION_0_LATE ? 107 : 119) {
static const uint16 courseV0[] = { 0x269, 0x28D, 0x2B5, 0x2DE, 0x30A, 0x339, 0x368, 0x39D, 0x3D4, 0x40E, 0x44A, 0x48C };
static const uint16 courseV1[] = { 0x26A, 0x28E, 0x2B5, 0x2DF, 0x30A, 0x339, 0x36A, 0x39E, 0x3D5, 0x40F, 0x44D, 0x48F };
static const uint16 fine[] = { 0x24, 0x27, 0x2A, 0x2B, 0x2F, 0x31, 0x34, 0x37, 0x3A, 0x3E, 0x42, 0x45 };
+
_noteFrequency = (version > SCI_VERSION_0_LATE) ? courseV1 : courseV0;
_noteFrequencyModifier = fine;
}
+SoundChannel_PC9801::~SoundChannel_PC9801() {
+ _instrumentData.clear();
+}
+
void SoundChannel_PC9801::noteOff() {
if (_sustain)
return;
@@ -368,8 +382,8 @@ void SoundChannel_PC9801::noteOff() {
void SoundChannel_PC9801::noteOn(uint8 note, uint8 velo) {
_duration = 0;
- if (_program != _parts[_assign]->program() && _soundOn) {
- _program = _parts[_assign]->program();
+ if (_program != _parts[_assign]->getCurrentProgram() && _soundOn) {
+ _program = _parts[_assign]->getCurrentProgram();
programChange(_program);
}
@@ -410,12 +424,13 @@ void SoundChannel_PC9801::setVolume(uint8 volume) {
void SoundChannel_PC9801::reset() {
_assign = 0xFF;
+ _note = 0xFF;
_volume = 0;
}
int SoundChannel_PC9801::recalculateFrequency(uint16 note, uint16 modifier, uint8 *destOctaveBlock, uint16 *destFrequency, uint8 *destVbrFrequencyModifier) {
note += _transpose;
- uint16 pb = _parts[_assign]->pitchBend();
+ uint16 pb = _parts[_assign]->getCurrentPitchBend();
if (pb == 0x2000) {
pb = 0;
@@ -445,12 +460,22 @@ int SoundChannel_PC9801::recalculateFrequency(uint16 note, uint16 modifier, uint
note++;
}
- if (note < _noteRangeLow || note > _noteRangeHigh)
+ if (_version == SCI_VERSION_0_LATE)
+ note -= 12;
+
+ if (note < 12 || note > 107)
return -1;
+ if (_version == SCI_VERSION_0_LATE && _type == 2) {
+ uint16 rs = _noteFrequency[note - 12];
+ if (destFrequency)
+ *destFrequency = rs;
+ return rs;
+ }
+
uint8 block = (note / 12) - 1;
note %= 12;
- uint16 res = _noteFrequency[note];
+ uint16 res =_noteFrequency[note];
uint16 pitchVbrMultiplier = _noteFrequencyModifier[note];
if (_type != 2)
@@ -487,7 +512,11 @@ uint8 SoundChannel_PC9801::getVolume() {
0x7b, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f
};
- uint16 partVolume = ((_assign != 0xFF) ? _parts[_assign]->volume() : 0) + 1;
+ uint16 partVolume = ((_assign != 0xFF) ? _parts[_assign]->getCurrentVolume() : 0);
+ if (_version == SCI_VERSION_0_LATE)
+ return partVolume;
+
+ partVolume += 1;
uint16 velocity = volumeTable1[_velo] + 1;
uint16 volume = _soundOn ? (partVolume * velocity) >> 6 : 0;
volume = volumeTable2[volume] - _volume;
@@ -540,9 +569,8 @@ void SoundChannel_PC9801::processSounds() {
sendFrequency();
}
-void SoundChannel_PC9801::programChangeInit(const uint8 *data) {
- _transpose = (int8)(data[0] + data[0]);
- _transpose >>= 1;
+void SoundChannel_PC9801::programChangeInit(SciSpan<const uint8> &data) {
+ _transpose = (int8)(data[0] & 0xC0 && data[0] < 0xC0 ? data[0] ^ 0x80 : data[0]);
_vbrInitialDelay = data[1];
_vbrDepthIncr = data[2];
_vbrDecrTime = data[3];
@@ -567,33 +595,67 @@ void SoundChannel_PC9801::prepareFrequencyAndVolume(bool updateVolume) {
sendVolume();
}
-SoundChannel_PC9801_FM4OP::SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn) : SoundChannel_PC9801(pc98a, parts, version, 0, soundOn),
-_carrier(0), _regPrt(id > 3 ? 1 : 0), _regOffs(id % 3), _patchSize(patchSize) {
+SoundChannel_PC9801_FM4OP::SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchSize, bool &soundOn)
+ : SoundChannel_PC9801(pc98a, parts, version, 0, instrumentData, soundOn), _carrier(0), _regPrt(id > 2 ? 1 : 0), _regOffs(id % 3),
+ _operatorFlags((id > 2 ? 4 : 0) | (id % 3) | 0xF0), _patchSize(patchSize) {
_operatorLevel[0] = _operatorLevel[1] = _operatorLevel[2] = _operatorLevel[3] = 0x7F;
}
void SoundChannel_PC9801_FM4OP::programChange(uint8 program) {
static const uint8 carrier[] = { 0x10, 0x10, 0x10, 0x10, 0x30, 0x70, 0x70, 0xF0 };
- const uint8 *pos = _instrumentData + program * _patchSize;
+ static const uint8 steps[] = { 0, 16, 8, 24 };
+ SciSpan<const uint8> data = _instrumentData.subspan(program * _patchSize);
- programChangeInit(pos);
- pos += 8;
+ if (_version == SCI_VERSION_1_LATE) {
+ programChangeInit(data);
+ uint8 pos = 8;
+
+ for (uint8 i = 0x40 + _regOffs; i < 0x50 + _regOffs; i += 4)
+ writeReg(_regPrt, i, 0xFF);
+ for (uint8 i = 0x30 + _regOffs; i < 0x40 + _regOffs; i += 4)
+ writeReg(_regPrt, i, data[pos++]);
+ for (int i = 0; i < 4; ++i)
+ _operatorLevel[i] = data[pos++];
+ for (uint8 i = 0x50 + _regOffs; i < 0xA0 + _regOffs; i += 4)
+ writeReg(_regPrt, i, data[pos++]);
+ writeReg(_regPrt, 0xB0 + _regOffs, data[pos]);
+ _carrier = carrier[data[pos] & 7];
- for (uint8 i = 0x40 + _regOffs; i < 0x50 + _regOffs; i += 4)
- writeReg(_regPrt, i, 0xFF);
- for (uint8 i = 0x30 + _regOffs; i < 0x40 + _regOffs; i += 4)
- writeReg(_regPrt, i, *pos++);
- for (int i = 0; i < 4; ++i)
- _operatorLevel[i] = *pos++;
- for (uint8 i = 0x50 + _regOffs; i < 0xA0 + _regOffs; i += 4)
- writeReg(_regPrt, i, *pos++);
- writeReg(_regPrt, 0xB0 + _regOffs, *pos);
- _carrier = carrier[*pos & 7];
+ } else {
+ uint8 pos = 11;
+ uint8 opFlags = data[pos];
+ uint8 fba = data[pos + 1] & 0x3F;
+ _carrier = carrier[fba & 7];
+
+ pos += 8;
+ uint8 reg = 0x30 + _regOffs;
+ for (int i = 0; i < 4; ++i)
+ writeReg(_regPrt, reg + (i << 2), data[pos + steps[i & 3]] & 0x7F);
+
+ pos -= 3;
+ _operatorLevel[0] = (opFlags & 0x08) ? data[pos + steps[0]] : 0x7F;
+ _operatorLevel[1] = (opFlags & 0x20) ? data[pos + steps[1]] : 0x7F;
+ _operatorLevel[2] = (opFlags & 0x10) ? data[pos + steps[2]] : 0x7F;
+ _operatorLevel[3] = (opFlags & 0x40) ? data[pos + steps[3]] : 0x7F;
+
+ pos += 4;
+ reg = 0x50 + _regOffs;
+ for (int i = 0; i < 16; ++i)
+ writeReg(_regPrt, reg + (i << 2), data[pos + steps[i & 3] + (i >> 2)]);
+
+ if (fba >= 24)
+ fba -= 24;
+ else
+ fba &= 7;
+
+ writeReg(_regPrt, 0xB0 + _regOffs, fba);
+ _operatorFlags = (_operatorFlags & 7) | (opFlags << 1);
+ }
}
void SoundChannel_PC9801_FM4OP::sendSoundOnOff(bool noteOn) {
_flags = noteOn ? (_flags | kChanKeyOn) : (_flags & ~kChanKeyOn);
- writeReg(_regPrt, 0x28, _regOffs | (noteOn ? 0xF0 : 0));
+ writeReg(0, 0x28, noteOn ? _operatorFlags : _operatorFlags & 7);
}
void SoundChannel_PC9801_FM4OP::sendVolume() {
@@ -604,10 +666,15 @@ void SoundChannel_PC9801_FM4OP::sendVolume() {
uint8 r = _operatorLevel[i];
c += c;
if (c & 0x100) {
- r = (((r ^ 0x7F) * vol) / 0x7F) * 2;
- r = ((r < 0x7F) ? 0x7F - r : 0) + 20;
- if (r > 0x7F)
- r = 0x7F;
+ c &= 0xFF;
+ if (_version == SCI_VERSION_1_LATE) {
+ r = (((r ^ 0x7F) * vol) / 0x7F) * 2;
+ r = ((r < 0x7F) ? 0x7F - r : 0) + 20;
+ if (r > 0x7F)
+ r = 0x7F;
+ } else {
+ r = 127 - ((127 - r) * vol / 128);
+ }
}
writeReg(_regPrt, 0x40 + (i << 2) + _regOffs, r);
}
@@ -621,9 +688,12 @@ void SoundChannel_PC9801_FM4OP::sendFrequency() {
uint8 SoundChannel_PC9801_FM2OP::_activeOperators = 0;
-SoundChannel_PC9801_FM2OP::SoundChannel_PC9801_FM2OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchSize, bool &soundOn) : SoundChannel_PC9801(pc98a, parts, version, 1, soundOn),
-_patchOffset(37), _patchSize(patchSize), _frequencyCourse2(0), _frequencyNoteModifier2(0), _vbrCur2(0x80), _vbrIncrStep2(0), _vbrDecrStep2(0), _regPrt(id > 3 ? 1 : 0), _regOffs(id & 1) {
+SoundChannel_PC9801_FM2OP::SoundChannel_PC9801_FM2OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchSize, bool &soundOn)
+ : SoundChannel_PC9801(pc98a, parts, version, 1, instrumentData, soundOn), _patchOffset(37), _patchSize(patchSize), _frequencyCourse2(0), _frequencyNoteModifier2(0),
+ _vbrFrequencyModifier2(0), _vbrCur2(0x80), _vbrIncrStep2(0), _vbrDecrStep2(0), _regPrt(id > 3 ? 1 : 0), _regOffs(id & 1) {
+
static const uint16 opFreqOffset[] = { 0x0000, 0x0600, 0x07CF, 0x0980 };
+
_operatorLevel[0] = _operatorLevel[1] = 0x7F;
_operatorFrqIndex[0] = _operatorFrqIndex[1] = 0;
_opFreqOffset = opFreqOffset;
@@ -643,23 +713,23 @@ void SoundChannel_PC9801_FM2OP::reset() {
}
void SoundChannel_PC9801_FM2OP::programChange(uint8 program) {
- const uint8 *pos = _instrumentData + program * _patchSize + _patchOffset;
+ SciSpan<const uint8> data = _instrumentData.subspan(program * _patchSize + _patchOffset);
- programChangeInit(pos);
- pos += 7;
+ programChangeInit(data);
+ uint8 pos = 7;
for (uint8 i = 0x42 + (_regOffs << 2); i < 0x52 + (_regOffs << 2); i += 8)
writeReg(_regPrt, i, 0xFF);
for (uint8 i = 0x32 + (_regOffs << 2); i < 0x42 + (_regOffs << 2); i += 8)
- writeReg(_regPrt, i, *pos++);
+ writeReg(_regPrt, i, data[pos++]);
for (int i = 0; i < 2; ++i)
- _operatorLevel[i] = *pos++;
+ _operatorLevel[i] = data[pos++];
for (uint8 i = 0x52 + (_regOffs << 2); i < 0x72 + (_regOffs << 2); i += 8)
- writeReg(_regPrt, i, *pos++);
- _operatorFrqIndex[0] = pos[0] >> 6;
- _operatorFrqIndex[1] = pos[1] >> 6;
+ writeReg(_regPrt, i, data[pos++]);
+ _operatorFrqIndex[0] = data[pos] >> 6;
+ _operatorFrqIndex[1] = data[pos + 1] >> 6;
for (uint8 i = 0x72 + (_regOffs << 2); i < 0xA2 + (_regOffs << 2); i += 8)
- writeReg(_regPrt, i, *pos++);
+ writeReg(_regPrt, i, data[pos++]);
}
void SoundChannel_PC9801_FM2OP::prepareFrequencyAndVolume(bool updateVolume) {
@@ -683,7 +753,7 @@ void SoundChannel_PC9801_FM2OP::sendSoundOnOff(bool noteOn) {
_activeOperators &= ~op;
}
- writeReg(_regPrt, 0x28, _activeOperators | 2);
+ writeReg(0, 0x28, _activeOperators | (_regPrt << 2) | 2);
}
void SoundChannel_PC9801_FM2OP::sendVolume() {
@@ -757,40 +827,70 @@ void SoundChannel_PC9801_FM2OP::sendFrequency() {
uint8 SoundChannel_PC9801_SSG::_activeChannnelsStatus = 0x3F;
-SoundChannel_PC9801_SSG::SoundChannel_PC9801_SSG(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, uint8 patchOffset, uint8 patchSize, bool &soundOn) : SoundChannel_PC9801(pc98a, parts, version, 2, soundOn),
-_patchOffset(patchOffset), _patchSize(patchSize), _regOffs(id & 3), _instrumentChanMask(0x3F), _ngPhaseStep(0), _currentLevel(0), _ssgEnvelopeState(kEnvSSG_silent), _ngEnvelopeTimer(0),
-_ngSpeed(0), _ssgEnvelopeTimer(0), _ssgLevel(0), _ssgSpeed(0) {
- static const uint16 courseV0[] = { 0x82D, 0x8A9, 0x92D, 0x9B6, 0xA4D, 0xAEA, 0xB90, 0xC40, 0xCFA, 0xDC0, 0xE90, 0xF6F };
+SoundChannel_PC9801_SSG::SoundChannel_PC9801_SSG(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchOffset, uint8 patchSize, bool &soundOn)
+ : SoundChannel_PC9801(pc98a, parts, version, 2, instrumentData, soundOn), _patchOffset(patchOffset), _patchSize(patchSize), _regOffs(id & 3),
+ _instrumentChanMask(0x3F), _ngPhaseStep(0), _currentLevel(0), _ssgEnvelopeState(kEnvSSG_silent), _ngEnvelopeTimer(0),
+ _ngSpeed(0), _ngPhase(0), _ssgEnvelopeTimer(0), _ssgLevel(0), _ssgSpeed(0), _ccngEnabled(false) {
+
+ static const uint16 courseV0[] = {
+ 0xfcf, 0xef1, 0xe16, 0xd59, 0xc87, 0xbcd, 0xb27, 0xa93, 0x9ee, 0x96a, 0x8da, 0x865, 0x778, 0x70d, 0x6a6, 0x643,
+ 0x5ec, 0x599, 0x545, 0x4f7, 0x4b5, 0x46d, 0x432, 0x3f4, 0x3c5, 0x386, 0x353, 0x325, 0x2f6, 0x2cc, 0x2a3, 0x27d,
+ 0x25a, 0x238, 0x218, 0x1fa, 0x1dd, 0x1c3, 0x1a9, 0x192, 0x17b, 0x166, 0x152, 0x13f, 0x12d, 0x11c, 0x10c, 0x0fd,
+ 0x0ef, 0x0e1, 0x0d5, 0x0c9, 0x0bd, 0x0b3, 0x0a9, 0x09f, 0x096, 0x08e, 0x086, 0x07e, 0x077, 0x071, 0x06a, 0x064,
+ 0x05f, 0x059, 0x054, 0x050, 0x04b, 0x047, 0x043, 0x03f, 0x03c, 0x038, 0x035, 0x032, 0x02f, 0x02d, 0x02a, 0x028,
+ 0x026, 0x023, 0x021, 0x020, 0x01e, 0x01c, 0x01b, 0x019, 0x018, 0x016, 0x015, 0x014, 0x013, 0x012, 0x011, 0x010
+ };
+
static const uint16 courseV1[] = { 0x82D, 0x8A9, 0x92D, 0x9B6, 0xA4D, 0xAEA, 0xB90, 0xC40, 0xCFA, 0xDC0, 0xE90, 0xF6F };
static const uint16 fine[] = { 0x7C, 0x84, 0x8B, 0x94, 0x9D, 0xA6, 0xB0, 0xBA, 0xC5, 0xD0, 0xDE, 0xEB };
static const uint8 noiseFrq[] = { 0x00, 0x01, 0x04, 0x07, 0x0A, 0x0D, 0x10, 0x13, 0x16, 0x19, 0x1C, 0x1F };
static const uint8 disableMask[] = { 0xF6, 0xED, 0xDB };
+ static const uint8 enableMask1[] = { 0xBE, 0xBD, 0xBB };
+ static const uint8 enableMask2[] = { 0xB7, 0xAF, 0x9F };
+
_noteFrequency = (version > SCI_VERSION_0_LATE) ? courseV1 : courseV0;
_noteFrequencyModifier = fine;
_ngFreq = noiseFrq;
_chanDisableMask = disableMask[id & 3];
+ _chanEnableMask1 = enableMask1[id & 3];
+ _chanEnableMask2 = enableMask2[id & 3];
}
void SoundChannel_PC9801_SSG::reset() {
SoundChannel_PC9801::reset();
- _activeChannnelsStatus = 0x3F;
+ _activeChannnelsStatus = (_version == SCI_VERSION_1_LATE) ? 0x3F : 0xFF;
sendActiveChannelsStatus();
}
+void SoundChannel_PC9801_SSG::toggleNoiseGenerator(bool enable) {
+ _ccngEnabled = enable;
+}
+
void SoundChannel_PC9801_SSG::programChange(uint8 program) {
- _selectedInstrument = _instrumentData + program * _patchSize + _patchOffset;
- programChangeInit(_selectedInstrument);
-
- _flags &= ~kChanNgEnable;
- if (_selectedInstrument[7])
- _flags |= (kChanNgEnable | kChanNgRestartEnv);
- _instrumentChanMask = _selectedInstrument[22];
- _activeChannnelsStatus = (_activeChannnelsStatus & _chanDisableMask) | (~_chanDisableMask & (_instrumentChanMask & 0x3F));
- if (!(_instrumentChanMask & 8)) {
- _ngPhaseStep = _selectedInstrument[21] & 0x1F;
- updateNg();
+ _selectedInstrument = _instrumentData.subspan(program * _patchSize + _patchOffset);
+
+ if (_version == SCI_VERSION_1_LATE) {
+ programChangeInit(_selectedInstrument);
+
+ _flags &= ~kChanNgEnable;
+ if (_selectedInstrument[7])
+ _flags |= (kChanNgEnable | kChanNgRestartEnv);
+ _instrumentChanMask = _selectedInstrument[22];
+ _activeChannnelsStatus = (_activeChannnelsStatus & _chanDisableMask) | (~_chanDisableMask & (_instrumentChanMask & 0x3F));
+ if (!(_instrumentChanMask & 8)) {
+ _ngPhaseStep = _selectedInstrument[21] & 0x1F;
+ updateNg();
+ }
+
+ sendActiveChannelsStatus();
+
+ } else {
+ writeReg(0, 13, _selectedInstrument[0]);
+ writeReg(0, 6, _selectedInstrument[1]);
+ writeReg(0, 11, _selectedInstrument[2]);
+ writeReg(0, 12, _selectedInstrument[3]);
}
- sendActiveChannelsStatus();
+
_currentLevel = 0;
_ssgEnvelopeState = kEnvSSG_silent;
}
@@ -872,7 +972,7 @@ void SoundChannel_PC9801_SSG::processSounds() {
}
void SoundChannel_PC9801_SSG::sendSoundOnOff(bool noteOn) {
- if (noteOn && !(_ssgEnvelopeState & kEnvSSG_keyOn)) {
+ if (_version == SCI_VERSION_1_LATE && noteOn && !(_ssgEnvelopeState & kEnvSSG_keyOn)) {
_currentLevel = _selectedInstrument[19] << 4;
_ssgEnvelopeState = (kEnvSSG_keyOn | kEnvSSG_attack);
_ssgLevel = _selectedInstrument[11];
@@ -886,41 +986,68 @@ void SoundChannel_PC9801_SSG::sendSoundOnOff(bool noteOn) {
updateNg();
_flags |= kChanNgRestartEnv;
}
- } else if (!noteOn) {
- int8 v = (int8)(_selectedInstrument[20] & 0xF0);
- int16 l = _currentLevel + v;
- _currentLevel = uint8(CLIP<int16>(l, 0, 255) & 0xFF);
+ } else if (_version == SCI_VERSION_1_LATE && !noteOn) {
+ int16 l = _currentLevel + (int8)(_selectedInstrument[20] & 0xF0);
+ _currentLevel = (uint8)CLIP<int16>(l, 0, 255);
_ssgEnvelopeState = kEnvSSG_decay;
_ssgLevel = _selectedInstrument[17];
_ssgSpeed = _selectedInstrument[18];
_note = 0xFF;
+ } else if (_version == SCI_VERSION_0_LATE && noteOn) {
+ _activeChannnelsStatus &= _chanEnableMask1;
+ if (_ccngEnabled)
+ _activeChannnelsStatus &= _chanEnableMask2;
+ _currentLevel = 1;
+ sendActiveChannelsStatus();
+ writeReg(0, 13, _selectedInstrument[0]);
+ } else if (_version == SCI_VERSION_0_LATE) {
+ _activeChannnelsStatus |= ~_chanEnableMask1;
+ if (_ccngEnabled)
+ _activeChannnelsStatus |= ~_chanEnableMask2;
+ _currentLevel = 0;
+ _note = 0xFF;
+ sendActiveChannelsStatus();
}
sendVolume();
}
void SoundChannel_PC9801_SSG::sendVolume() {
uint8 v1 = getVolume();
- uint8 vol = v1 + (((v1 >> 1) + v1) >> 2);
-
- if (vol > 0x7F)
- vol = 0x7F;
- vol = (vol >> 3) & 0x0F;
+ uint16 r = 0;
+
+ if (_version == SCI_VERSION_1_LATE) {
+ uint8 vol = v1 + (((v1 >> 1) + v1) >> 2);
+
+ if (vol > 0x7F)
+ vol = 0x7F;
+ vol = (vol >> 3) & 0x0F;
+
+ r = _currentLevel & 0xF0;
+ for (uint8 i = 0; i < 4; ++i) {
+ r += r;
+ if (r & 0x100)
+ r = (r + vol) & 0xFF;
+ }
+
+ r = (r + 15) >> 4;
- uint16 r = _currentLevel & 0xF0;
- for (uint8 i = 0; i < 4; ++i) {
- r += r;
- if (r & 0x100)
- r = (r + vol) & 0xFF;
+ } else {
+ r = _currentLevel * (0x10 | v1 >> 3);
}
- writeReg(0, 8 + _regOffs, (r + 15) >> 4);
+ writeReg(0, 8 + _regOffs, r);
}
void SoundChannel_PC9801_SSG::sendFrequency() {
- uint16 freq = (_frequencyCourse + _vbrFrequencyModifier) >> (8 - _frequencyBlock);
- if (!freq)
- return;
- freq = 62400 / freq;
+ uint16 freq = _frequencyCourse;
+
+ if (_version > SCI_VERSION_0_LATE) {
+ freq = (uint16)(freq + _vbrFrequencyModifier) >> (8 - _frequencyBlock);
+ if (!freq)
+ return;
+ freq = 62400 / freq;
+ }
+
writeReg(0, _regOffs << 1, freq & 0xFF);
writeReg(0, (_regOffs << 1) + 1, freq >> 8);
}
@@ -981,7 +1108,7 @@ void MidiPart_PC9801::noteOn(uint8 note, uint8 velo) {
}
void MidiPart_PC9801::controlChangeVolume(uint8 vol) {
- _volume = CLIP(vol >> 1, 0, 0x3f);
+ _volume = (_version < SCI_VERSION_1_LATE) ? vol : CLIP(vol >> 1, 0, 0x3f);
for (int i = 0; i < _numChan; ++i) {
if (_chan[i]->_assign == _id && _chan[i]->_note != 0xff)
_chan[i]->processNoteEvent(_chan[i]->_note, true);
@@ -989,6 +1116,9 @@ void MidiPart_PC9801::controlChangeVolume(uint8 vol) {
}
void MidiPart_PC9801::controlChangeSustain(uint8 sus) {
+ if (_version < SCI_VERSION_1_LATE)
+ return;
+
_sustain = sus;
if (_sustain)
return;
@@ -1002,6 +1132,9 @@ void MidiPart_PC9801::controlChangeSustain(uint8 sus) {
}
void MidiPart_PC9801::controlChangePolyphony(uint8 numChan) {
+ if (_version < SCI_VERSION_1_LATE)
+ return;
+
uint8 numAssigned = 0;
for (int i = 0; i < _numChan; ++i) {
if (_chan[i]->_assign == _id)
@@ -1017,6 +1150,16 @@ void MidiPart_PC9801::controlChangePolyphony(uint8 numChan) {
}
}
+void MidiPart_PC9801::controlChangeNoiseGenerator(uint8 enable) {
+ if (_version > SCI_VERSION_0_LATE)
+ return;
+
+ for (int i = 0; i < _numChan; ++i) {
+ if (_chan[i]->_assign == _id)
+ _chan[i]->toggleNoiseGenerator(enable);
+ }
+}
+
void MidiPart_PC9801::controlChangeAllNotesOff() {
for (int i = 0; i < _numChan; ++i) {
if (_chan[i]->_assign == _id && _chan[i]->_note != 0xff)
@@ -1029,6 +1172,9 @@ void MidiPart_PC9801::programChange(uint8 prg) {
}
void MidiPart_PC9801::pitchBend(int16 val) {
+ if (_version < SCI_VERSION_1_LATE)
+ return;
+
_pitchBend = val;
for (int i = 0; i < _numChan; ++i) {
if (_chan[i]->_assign == _id && _chan[i]->_note != 0xff)
@@ -1037,12 +1183,12 @@ void MidiPart_PC9801::pitchBend(int16 val) {
}
-void MidiPart_PC9801::addChannels(int num, int resetMissingChannels) {
+void MidiPart_PC9801::addChannels(int num, int resetMissingChannels, int channelType) {
if (resetMissingChannels != -1)
_chanMissing = resetMissingChannels;
for (int i = 0; i < _numChan; ++i) {
- if (_chan[i]->_assign != 0xFF)
+ if (_chan[i]->_assign != 0xFF || (channelType != -1 && _chan[i]->getType() != channelType))
continue;
_chan[i]->_assign = _id;
@@ -1148,9 +1294,10 @@ void MidiPart_PC9801::assignFreeChannels() {
MidiPart_PC9801 **MidiDriver_PC9801::_parts = 0;
-MidiDriver_PC9801::MidiDriver_PC9801(Audio::Mixer *mixer, SciVersion version) : _version(version), _pc98a(0), _chan(0), _numChan(6), _internalVersion(0xFF), _ssgPatchOffset(0xFF),
-_patchSize(0), _instrumentData(0), _timerProc(0), _timerProcPara(0), _baseTempo(10080), _ready(false), _isOpen(false), _masterVolume(0x0f) ,_soundOn(true),
-_playID(0), _channelMask1(0x10), _channelMask2(0x02) {
+MidiDriver_PC9801::MidiDriver_PC9801(Audio::Mixer *mixer, SciVersion version)
+ : _version(version), _pc98a(0), _chan(0), _numChan(6), _internalVersion(0xFF), _ssgPatchOffset(0xFF), _patchSize(0),
+ _timerProc(0), _timerProcPara(0), _baseTempo(10080), _ready(false), _isOpen(false), _masterVolume(0x0f) ,_soundOn(true), _playID(0),
+ _polyphony(9), _channelMask1(0x10), _channelMask2(0x02) {
_pc98a =
#ifdef SCI_PC98_AUDIO_EXTENDED
new PC98AudioCore(mixer, this, kType86, true);
@@ -1161,8 +1308,8 @@ _playID(0), _channelMask1(0x10), _channelMask2(0x02) {
MidiDriver_PC9801::~MidiDriver_PC9801() {
_ready = false;
+ close();
delete _pc98a;
- close();
}
int MidiDriver_PC9801::open() {
@@ -1178,53 +1325,51 @@ int MidiDriver_PC9801::open() {
}
ResourceManager *resMan = g_sci->getResMan();
- loadInstruments(*resMan->findResource(ResourceId(kResourceTypePatch, _version < SCI_VERSION_1_LATE ? 2 : 8), false));
-
- if (_ssgPatchOffset == 0xFF || _patchSize == 0)
+ if (!loadInstruments(*resMan->findResource(ResourceId(kResourceTypePatch, _version < SCI_VERSION_1_LATE ? 2 : 8), false)))
return MERR_CANNOT_CONNECT;
- if (_version < SCI_VERSION_1_LATE)
+ if (_version == SCI_VERSION_0_LATE && _channelMask2 == 0x00) {
_internalVersion = 0;
- else if (_patchSize == 60 && _ssgPatchOffset == 37)
+ _polyphony = 3;
+ } else if (_version == SCI_VERSION_0_LATE && _channelMask2 == 0x02) {
_internalVersion = 1;
+ _polyphony = 6;
+ } else if (_patchSize == 60 && _ssgPatchOffset == 37)
+ _internalVersion = 2;
else if (_patchSize == 81 && _ssgPatchOffset == 58)
- _internalVersion = 2;
+ _internalVersion = 3;
else
return MERR_CANNOT_CONNECT;
- if (_internalVersion == 2)
+ if (_internalVersion == 3)
_numChan++;
+ int config = _internalVersion;
+
#ifdef SCI_PC98_AUDIO_EXTENDED
- _numChan += 3;
- if (_internalVersion == 2)
- _numChan++;
+ _numChan = 9;
+ config = 4;
#endif
- static const int channelConfig[5][11] = {
+ static const int channelConfig[6][11] = {
+ { 0, 0, 0, 2, 2, 2, -1, -1, -1, -1, -1 },
{ 0, 0, 0, 2, 2, 2, -1, -1, -1, -1, -1 },
{ 0, 0, 0, 2, 2, 2, -1, -1, -1, -1, -1 },
{ 0, 0, 1, 1, 2, 2, 2, -1, -1, -1, -1 },
- { 0, 0, 0, 0, 0, 0, 2, 2, 2, -1, -1 },
- { 0, 0, 1, 1, 0, 0, 1, 1, 2, 2, 2 }
+ { 0, 0, 0, 0, 0, 0, 2, 2, 2, -1, -1 }
};
_parts = new MidiPart_PC9801*[16];
_chan = new SoundChannel_PC9801*[_numChan];
- int config = _internalVersion;
-
-#ifdef SCI_PC98_AUDIO_EXTENDED
- config = (_internalVersion >> 1) + 3;
-#endif
int numSSG = 0;
for (int i = 0; i < _numChan; ++i) {
if (channelConfig[config][i] == 0)
- _chan[i] = new SoundChannel_PC9801_FM4OP(i, _pc98a, _parts, _version, _patchSize, _soundOn);
+ _chan[i] = new SoundChannel_PC9801_FM4OP(i, _pc98a, _parts, _version, *_instrumentData, _patchSize, _soundOn);
else if (channelConfig[config][i] == 1)
- _chan[i] = new SoundChannel_PC9801_FM2OP(i, _pc98a, _parts, _version, _patchSize, _soundOn);
+ _chan[i] = new SoundChannel_PC9801_FM2OP(i, _pc98a, _parts, _version, *_instrumentData, _patchSize, _soundOn);
else if (channelConfig[config][i] == 2)
- _chan[i] = new SoundChannel_PC9801_SSG(numSSG++, _pc98a, _parts, _version, _ssgPatchOffset, _patchSize, _soundOn);
+ _chan[i] = new SoundChannel_PC9801_SSG(numSSG++, _pc98a, _parts, _version, *_instrumentData, _ssgPatchOffset, _patchSize, _soundOn);
else
_chan[i] = 0;
}
@@ -1246,6 +1391,8 @@ void MidiDriver_PC9801::close() {
bool ready = _ready;
_isOpen = _ready = false;
+ PC98AudioCore::MutexLock lock = _pc98a->stackLockMutex();
+
if (_parts) {
for (int i = 0; i < 16; ++i) {
delete _parts[i];
@@ -1264,51 +1411,11 @@ void MidiDriver_PC9801::close() {
_chan = 0;
}
- delete[] _instrumentData;
- _instrumentData = 0;
+ _instrumentData.clear();
_ready = ready;
}
-void MidiDriver_PC9801::loadInstruments(const SciSpan<const uint8> &data) {
- if (!data)
- return;
-
- SciSpan<const uint8> src = data;
- delete[] _instrumentData;
-
- if (_version == SCI_VERSION_0_LATE) {
- _ssgPatchOffset = 48;
- _patchSize = 52;
-
- _instrumentData = new uint8[96 * _patchSize];
- uint8 *dst = _instrumentData;
-
- for (bool load = true; load; ) {
- for (int i = 0; i < 48; ++i) {
- src.subspan(0, _patchSize).unsafeCopyDataTo(dst);
- src += 64;
- dst += _patchSize;
- }
- uint16 id = (src.byteSize() >= 2) ? src.getInt16BEAt(0) : 0;
- if (id == 0xABCD || id == 0xCDAB)
- src += 2;
- else
- load = false;
- }
- } else if (_version == SCI_VERSION_1_LATE) {
- int len = data.byteSize() - 1;
- _instrumentData = new uint8[len];
- uint8 *dst = _instrumentData;
- (++src).unsafeCopyDataTo(dst);
-
- _patchSize = len / 96;
- _ssgPatchOffset = (_patchSize == 81) ? 58 : 37;
- }
-
- SoundChannel_PC9801::_instrumentData = _instrumentData;
-}
-
void MidiDriver_PC9801::send(uint32 b) {
if (!_isOpen)
return;
@@ -1337,6 +1444,16 @@ void MidiDriver_PC9801::send(uint32 b) {
case SCI_MIDI_SET_POLYPHONY:
part->controlChangePolyphony(para2);
break;
+ case 76:
+ // This event (from the SCI0 driver) is parsing related and can't be handled here. Lets's see if this ever comes up.
+ warning("MidiDriver_PC9801: Midi Control Change '0x%2x' not implemented", para1);
+ break;
+ case 81:
+ part->controlChangeNoiseGenerator(para2);
+ break;
+ case 96:
+ // This event (from the SCI0 driver) is parsing related. It is handled in MidiParser_SCI::processEvent().
+ break;
case SCI_MIDI_CHANNEL_NOTES_OFF:
part->controlChangeAllNotesOff();
break;
@@ -1358,7 +1475,7 @@ void MidiDriver_PC9801::send(uint32 b) {
void MidiDriver_PC9801::assignFreeChannels(int num) {
assert(_parts);
for (int i = 0; i < 16; ++i) {
- uint8 missing = _parts[i]->missingChannels();
+ uint8 missing = _parts[i]->getMissingChannels();
if (!missing)
continue;
if (missing < num) {
@@ -1390,11 +1507,7 @@ uint32 MidiDriver_PC9801::property(int prop, uint32 param) {
_soundOn = param;
break;
case MIDI_PROP_POLYPHONY:
-#ifdef SCI_PC98_AUDIO_EXTENDED
- return _version < SCI_VERSION_1_LATE ? 9 : 12;
-#else
- return _version < SCI_VERSION_1_LATE ? 6 : 9;
-#endif
+ return _polyphony;
case MIDI_PROP_CHANNEL_ID:
return _version < SCI_VERSION_1_LATE ? (_channelMask1 | _channelMask2) : _playID;
default:
@@ -1403,6 +1516,36 @@ uint32 MidiDriver_PC9801::property(int prop, uint32 param) {
return 0;
}
+void MidiDriver_PC9801::initTrack(SciSpan<const byte> &header) {
+ if (!_isOpen || _version > SCI_VERSION_0_LATE)
+ return;
+
+ for (int i = 0; i < _numChan; ++i)
+ _chan[i]->reset();
+
+ uint8 caps = *header++;
+ int numChan = (caps == 2) ? 15 : 16;
+ if (caps != 0 && caps != 2)
+ return;
+
+ for (int i = 0; i < numChan; ++i) {
+ _parts[i]->controlChangeVolume(103);
+
+ uint8 num = (_internalVersion == 1) ? (*header & 0x7F): 1;
+ header++;
+ uint8 flags = *header++;
+
+ if (flags & _channelMask1 && num)
+ _parts[i]->addChannels(num, -1, 0);
+
+ if (flags & _channelMask2 && num)
+ _parts[i]->addChannels(num, -1, 2);
+
+ if (_internalVersion == 0)
+ _parts[i]->programChange(10);
+ }
+}
+
void MidiDriver_PC9801::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
_timerProc = timer_proc;
_timerProcPara = timer_param;
@@ -1419,6 +1562,43 @@ void MidiDriver_PC9801::timerCallbackB() {
updateChannels();
}
+bool MidiDriver_PC9801::loadInstruments(const SciSpan<const uint8> &data) {
+ if (!data)
+ return false;
+
+ SciSpan<const uint8> src = data;
+ _instrumentData.clear();
+
+ if (_version == SCI_VERSION_0_LATE) {
+ _ssgPatchOffset = 48;
+ _patchSize = 52;
+
+ _instrumentData->allocate(96 * _patchSize);
+ SciSpan<uint8> dst = *_instrumentData;
+
+ for (bool load = true; load; ) {
+ for (int i = 0; i < 48; ++i) {
+ src.subspan(0, _patchSize).copyDataTo(dst);
+ src += 64;
+ dst += _patchSize;
+ }
+ uint16 id = (src.byteSize() >= 2) ? src.getInt16BEAt(0) : 0;
+ if (id == 0xABCD || id == 0xCDAB) {
+ src += 2;
+ _channelMask2 = 0x00;
+ } else {
+ load = false;
+ }
+ }
+ } else if (_version == SCI_VERSION_1_LATE) {
+ _instrumentData->allocateFromSpan(++src);
+ _patchSize = (data.byteSize() - 1) / 96;
+ _ssgPatchOffset = (_patchSize == 81) ? 58 : 37;
+ }
+
+ return (_instrumentData->byteSize() && _patchSize && _ssgPatchOffset != 0xFF);
+}
+
void MidiDriver_PC9801::updateParser() {
if (_timerProc)
_timerProc(_timerProcPara);
@@ -1440,10 +1620,12 @@ void MidiDriver_PC9801::reset() {
}
uint8 flag = 0;
- if (_internalVersion == 2) {
+#ifndef SCI_PC98_AUDIO_EXTENDED
+ if (_internalVersion == 3) {
_pc98a->writeReg(0, 0xB2, 0x04);
flag = 0x40;
}
+#endif
_pc98a->writeReg(0, 0x27, 0x38);
_pc98a->writeReg(0, 0x27, 0x3a | flag);
@@ -1480,6 +1662,11 @@ void MidiPlayer_PC9801::playSwitch(bool play) {
_driver->property(MIDI_PROP_PLAYSWITCH, play ? 1 : 0);
}
+void MidiPlayer_PC9801::initTrack(SciSpan<const byte> &trackData) {
+ if (_driver)
+ static_cast<MidiDriver_PC9801*>(_driver)->initTrack(trackData);
+}
+
MidiPlayer *MidiPlayer_PC9801_create(SciVersion _soundVersion) {
return new MidiPlayer_PC9801(_soundVersion);
}
Commit: bc5ecb3b7c01e352c246f30789edd31d097f7c60
https://github.com/scummvm/scummvm/commit/bc5ecb3b7c01e352c246f30789edd31d097f7c60
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-02T20:45:35+03:00
Commit Message:
SCI: (PC98 sound driver) - bugfixes
This fixes two evalutation issues specific to the MSVC x64 release build (other builds worked fine).
This also fixes an analysis warning regarding my use of the span code.
Changed paths:
engines/sci/sound/drivers/pc9801.cpp
diff --git a/engines/sci/sound/drivers/pc9801.cpp b/engines/sci/sound/drivers/pc9801.cpp
index 87a997b..8a1521a 100644
--- a/engines/sci/sound/drivers/pc9801.cpp
+++ b/engines/sci/sound/drivers/pc9801.cpp
@@ -553,17 +553,21 @@ void SoundChannel_PC9801::processSounds() {
}
if (_flags & kChanVbrDecrease) {
- int16 s = _vbrCur - (_vbrDecrStep & 0xFF);
- _vbrCur = s & 0xFF;
- if (s >= 0)
+ uint8 sL = _vbrDecrStep & 0xFF;
+ uint8 sH = _vbrDecrStep >> 8;
+ bool ovrflow = (sL > _vbrCur);
+ _vbrCur -= sL;
+ if (!ovrflow)
return;
- _vbrFrequencyModifier -= ((_vbrDecrStep >> 8) + 1);
- } else {
- uint16 s = _vbrCur + (_vbrIncrStep & 0xFF);
- _vbrCur = s & 0xFF;
- if (s <= 255)
+ _vbrFrequencyModifier -= (sH + 1);
+ } else {
+ uint8 sL = _vbrDecrStep & 0xFF;
+ uint8 sH = _vbrDecrStep >> 8;
+ bool ovrflow = (sL + _vbrCur > 255);
+ _vbrCur += sL;
+ if (!ovrflow)
return;
- _vbrFrequencyModifier += ((_vbrIncrStep >> 8) + 1);
+ _vbrFrequencyModifier += (sH + 1);
}
sendFrequency();
@@ -794,23 +798,31 @@ void SoundChannel_PC9801_FM2OP::processSounds() {
}
if (_flags & kChanVbrDecrease) {
- int16 s = _vbrCur - (_vbrDecrStep & 0xFF);
- _vbrCur = s & 0xFF;
- if (s < 0)
- _vbrFrequencyModifier -= ((_vbrDecrStep >> 8) + 1);
- s = _vbrCur2 - (_vbrDecrStep2 & 0xFF);
- _vbrCur2 = s & 0xFF;
- if (s < 0)
- _vbrFrequencyModifier2 -= ((_vbrDecrStep2 >> 8) + 1);
+ uint8 sL = _vbrDecrStep & 0xFF;
+ uint8 sH = _vbrDecrStep >> 8;
+ bool ovrflow = (sL > _vbrCur);
+ _vbrCur -= sL;
+ if (ovrflow)
+ _vbrFrequencyModifier -= (sH + 1);
+ sL = _vbrDecrStep2 & 0xFF;
+ sH = _vbrDecrStep2 >> 8;
+ ovrflow = (sL > _vbrCur2);
+ _vbrCur2 -= sL;
+ if (ovrflow)
+ _vbrFrequencyModifier2 -= (sH + 1);
} else {
- uint16 s = _vbrCur + (_vbrIncrStep & 0xFF);
- _vbrCur = s & 0xFF;
- if (s > 255)
- _vbrFrequencyModifier += ((_vbrIncrStep >> 8) + 1);
- s = _vbrCur2 + (_vbrIncrStep2 & 0xFF);
- _vbrCur2 = s & 0xFF;
- if (s > 255)
- _vbrFrequencyModifier2 += ((_vbrIncrStep2 >> 8) + 1);
+ uint8 sL = _vbrDecrStep & 0xFF;
+ uint8 sH = _vbrDecrStep >> 8;
+ bool ovrflow = (sL + _vbrCur > 255);
+ _vbrCur += sL;
+ if (ovrflow)
+ _vbrFrequencyModifier += (sH + 1);
+ sL = _vbrDecrStep2 & 0xFF;
+ sH = _vbrDecrStep2 >> 8;
+ ovrflow = (sL + _vbrCur2 > 255);
+ _vbrCur2 += sL;
+ if (ovrflow)
+ _vbrFrequencyModifier2 += (sH + 1);
}
sendFrequency();
@@ -958,7 +970,7 @@ void SoundChannel_PC9801_SSG::processSounds() {
if (_ssgEnvelopeState == (kEnvSSG_keyOn | kEnvSSG_decay)) {
_ssgLevel = _selectedInstrument[15];
_ssgSpeed = _selectedInstrument[16];
- vol = (uint8)CLIP<int16>(vol + (int8)(_selectedInstrument[20] << 4), 0, 255);
+ vol = (uint8)CLIP<int16>(vol + (int8)((_selectedInstrument[20] & 0x0F) << 4), 0, 255);
}
}
@@ -1523,7 +1535,8 @@ void MidiDriver_PC9801::initTrack(SciSpan<const byte> &header) {
for (int i = 0; i < _numChan; ++i)
_chan[i]->reset();
- uint8 caps = *header++;
+ uint8 readPos = 0;
+ uint8 caps = header.getInt8At(readPos++);
int numChan = (caps == 2) ? 15 : 16;
if (caps != 0 && caps != 2)
return;
@@ -1531,9 +1544,10 @@ void MidiDriver_PC9801::initTrack(SciSpan<const byte> &header) {
for (int i = 0; i < numChan; ++i) {
_parts[i]->controlChangeVolume(103);
- uint8 num = (_internalVersion == 1) ? (*header & 0x7F): 1;
- header++;
- uint8 flags = *header++;
+ uint8 num = (_internalVersion == 1) ? (header.getInt8At(readPos) & 0x7F) : 1;
+ readPos++;
+ uint8 flags = header.getInt8At(readPos);
+ readPos++;
if (flags & _channelMask1 && num)
_parts[i]->addChannels(num, -1, 0);
Commit: 037eec31446b64ec56b24e31d5be392390a694ba
https://github.com/scummvm/scummvm/commit/037eec31446b64ec56b24e31d5be392390a694ba
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-02T20:45:35+03:00
Commit Message:
SCI: implement SCI0 midi driver track initialization
I put this in an separate commit to make it easier to review/revert. I've tried to make this as minimum invasive as possible. That's why I put this in place of the former call to onNewSound().
SCI_0_LATE sound drivers (probably also SCI_0_EARLY, but I don't know that) do some midi track initialization, mostly resetting certain values and assigning voices (hardware channels) to midi parts. The information for this comes from the track header.
The SCI0 version of the PC-98 sound driver relies on this code. The driver checks the channel flags with two different masks and assigns different sound channel types accordingly. This can't be done with the 0x4B event. Using the 0x4B event is sort of counter intuitive anyway, since only some of the SCI0 drivers even support that event.
It seems that the only driver making use of onNewSound() was MT-32. I've adapted the driver to my changes, although I am quite sure that the sound will be unaffected. The only thing that the MT-32 driver does with the header information is checking whether a midi part should play or not and assign exactly one timbre (with exactly the same number) to that part if required.
Changed paths:
engines/sci/resource.h
engines/sci/resource_audio.cpp
engines/sci/sound/drivers/midi.cpp
engines/sci/sound/drivers/mididriver.h
engines/sci/sound/midiparser_sci.cpp
diff --git a/engines/sci/resource.h b/engines/sci/resource.h
index 9fc9708..119d860 100644
--- a/engines/sci/resource.h
+++ b/engines/sci/resource.h
@@ -654,6 +654,7 @@ public:
struct Track {
byte type;
byte channelCount;
+ SciSpan<const byte> header;
Channel *channels;
int16 digitalChannelNr;
uint16 digitalSampleRate;
diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp
index dfc3d2f..58437a6 100644
--- a/engines/sci/resource_audio.cpp
+++ b/engines/sci/resource_audio.cpp
@@ -766,6 +766,8 @@ SoundResource::SoundResource(uint32 resourceNr, ResourceManager *resMan, SciVers
// Digital sample data included? -> Add an additional channel
if (resource->getUint8At(0) == 2)
_tracks->channelCount++;
+ // header information that can be passed to the SCI0 sound driver
+ _tracks->header = resource->subspan(0, _soundVersion == SCI_VERSION_0_EARLY ? 0x11 : 0x21);
_tracks->channels = new Channel[_tracks->channelCount];
channel = &_tracks->channels[0];
channel->flags |= 2; // don't remap (SCI0 doesn't have remapping)
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 889a875..4f3f0da 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -158,10 +158,10 @@ public:
int getFirstChannel() const override;
int getLastChannel() const override;
void setVolume(byte volume) override;
- virtual void onNewSound() override;
int getVolume() override;
void setReverb(int8 reverb) override;
void playSwitch(bool play) override;
+ virtual void initTrack(SciSpan<const byte> &) override;
private:
bool isMt32GmPatch(const SciSpan<const byte> &data);
@@ -479,14 +479,6 @@ int MidiPlayer_Midi::getVolume() {
return _masterVolume;
}
-void MidiPlayer_Midi::onNewSound() {
- if (_defaultReverb >= 0)
- // SCI0 in combination with MT-32 requires a reset of the reverb to
- // the default value that is present in either the MT-32 patch data
- // or MT32.DRV itself.
- setReverb(_defaultReverb);
-}
-
void MidiPlayer_Midi::setReverb(int8 reverb) {
assert(reverb < kReverbConfigNr);
@@ -507,6 +499,43 @@ void MidiPlayer_Midi::playSwitch(bool play) {
}
}
+void MidiPlayer_Midi::initTrack(SciSpan<const byte> &header) {
+ if (_version > SCI_VERSION_0_LATE)
+ return;
+
+ if (_defaultReverb >= 0)
+ // SCI0 in combination with MT-32 requires a reset of the reverb to
+ // the default value that is present in either the MT-32 patch data
+ // or MT32.DRV itself.
+ setReverb(_defaultReverb);
+
+ /* TODO: I have no idea what SCI_VERSION_0_EARLY games do here.
+ Therefore the extra code is restricted to SCI_VERSION_0_LATE for now.*/
+ if (_version == SCI_VERSION_0_EARLY)
+ return;
+
+ uint8 caps = header.getInt8At(0);
+ if (caps != 0 && caps != 2)
+ return;
+
+ uint8 readPos = 3;
+ byte msg[9];
+ uint8 flags = 0;
+
+ for (int i = 1; i < 9; ++i) {
+ readPos++;
+ flags = header.getInt8At(readPos++);
+ msg[i - 1] = (flags & 1) ? i : 0x10;
+ }
+
+ flags = header.getInt8At(readPos);
+ msg[8] = (flags & 0x80) ? 9 : 0x10;
+
+ // assign channels
+ Sci::SciSpan<const byte> s(msg, 9);
+ sendMt32SysEx(0x10000D, s, false);
+}
+
bool MidiPlayer_Midi::isMt32GmPatch(const SciSpan<const byte> &data) {
uint32 size = data.size();
diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h
index f51851b..73c2fd5 100644
--- a/engines/sci/sound/drivers/mididriver.h
+++ b/engines/sci/sound/drivers/mididriver.h
@@ -24,6 +24,7 @@
#define SCI_SFX_SOFTSEQ_MIDIDRIVER_H
#include "sci/sci.h"
+#include "sci/util.h"
#include "audio/mididrv.h"
#include "common/error.h"
@@ -106,8 +107,6 @@ public:
return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0;
}
- virtual void onNewSound() {}
-
// Returns the current reverb, or -1 when no reverb is active
int8 getReverb() const { return _reverb; }
// Sets the current reverb, used mainly in MT-32
@@ -121,6 +120,12 @@ public:
}
}
+ // Prepares the driver for the playback of SCI0 midi tracks.
+ // The main purpose is the assignment of voices ("hardware" sound channels) to the 16 midi parts.
+ // This is basically the predecessor of the 0x4B midi event.
+ // Some drivers also do other things in here.
+ virtual void initTrack(SciSpan<const byte> &) {}
+
protected:
SciVersion _version;
};
diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp
index ad4b0da..22a5c6d 100644
--- a/engines/sci/sound/midiparser_sci.cpp
+++ b/engines/sci/sound/midiparser_sci.cpp
@@ -392,15 +392,11 @@ void MidiParser_SCI::sendInitCommands() {
// Set initial voice count
if (_pSnd) {
if (_soundVersion <= SCI_VERSION_0_LATE) {
- static_cast<MidiPlayer *>(_driver)->onNewSound();
-
- for (int i = 0; i < 15; ++i) {
- byte voiceCount = 0;
- if (_channelUsed[i]) {
- voiceCount = _pSnd->soundRes->getInitialVoiceCount(i);
- sendToDriver(0xB0 | i, 0x4B, voiceCount);
- }
- }
+ // Send header data to SCI0 sound drivers. The driver function which parses the header (opcode 3)
+ // seems to be implemented at least in all SCI0_LATE drivers. The things that the individual drivers
+ // do in that init function varies.
+ if (_track->header.byteSize())
+ static_cast<MidiPlayer *>(_driver)->initTrack(_track->header);
} else {
for (int i = 0; i < _track->channelCount; ++i) {
byte voiceCount = _track->channels[i].poly;
Commit: 5d4e22ebc9f679cea041beead8269d626d7906f9
https://github.com/scummvm/scummvm/commit/5d4e22ebc9f679cea041beead8269d626d7906f9
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-02T20:45:35+03:00
Commit Message:
SCI: (PC-98 driver) - frequency bug workaround
The square sound channels will be out of spec when playing on the lowest octave. The result is a rather ugly noise. This bug makes a rather subtle appearance if the channel remapping is accurate, but it becomes quite obvious in the SQ4 intro with the current implementation. This commit turns off the channel when trying to play out of spec.
Changed paths:
engines/sci/sound/drivers/pc9801.cpp
diff --git a/engines/sci/sound/drivers/pc9801.cpp b/engines/sci/sound/drivers/pc9801.cpp
index 8a1521a..2b99d6b 100644
--- a/engines/sci/sound/drivers/pc9801.cpp
+++ b/engines/sci/sound/drivers/pc9801.cpp
@@ -111,7 +111,7 @@ protected:
private:
virtual void programChange(uint8 program) = 0;
- virtual void prepareFrequencyAndVolume(bool updateVolume);
+ virtual bool prepareFrequencyAndVolume(bool updateVolume);
virtual void sendSoundOnOff(bool noteOn) = 0;
virtual void sendFrequency() = 0;
virtual void sendVolume() = 0;
@@ -156,7 +156,7 @@ public:
private:
void programChange(uint8 program);
- void prepareFrequencyAndVolume(bool updateVolume);
+ bool prepareFrequencyAndVolume(bool updateVolume);
void processSounds();
void sendSoundOnOff(bool noteOn);
void sendVolume();
@@ -406,7 +406,9 @@ void SoundChannel_PC9801::processNoteEvent(uint8 note, bool noteOn) {
_flags |= kChanVbrRestartEnv;
}
- prepareFrequencyAndVolume(noteOn);
+ if (!prepareFrequencyAndVolume(noteOn))
+ noteOn = false;
+
sendSoundOnOff(noteOn);
}
@@ -480,6 +482,8 @@ int SoundChannel_PC9801::recalculateFrequency(uint16 note, uint16 modifier, uint
if (_type != 2)
res |= (block << 11);
+ else if (block == 0)
+ return -1;
if (pb)
res += (((pb * pitchVbrMultiplier) & 0x0FF0) >> 8);
@@ -591,12 +595,15 @@ void SoundChannel_PC9801::writeReg(uint8 part, uint8 reg, uint8 val) {
_pc98a->writeReg(part, reg, val);
}
-void SoundChannel_PC9801::prepareFrequencyAndVolume(bool updateVolume) {
+bool SoundChannel_PC9801::prepareFrequencyAndVolume(bool updateVolume) {
if (recalculateFrequency(_note, 0, &_frequencyBlock, &_frequencyCourse, &_frequencyNoteModifier) == -1)
- return;
+ return false;
+
sendFrequency();
if (updateVolume)
sendVolume();
+
+ return true;
}
SoundChannel_PC9801_FM4OP::SoundChannel_PC9801_FM4OP(uint8 id, PC98AudioCore *pc98a, MidiPart_PC9801 **parts, SciVersion version, SciSpan<const uint8> instrumentData, uint8 patchSize, bool &soundOn)
@@ -736,14 +743,17 @@ void SoundChannel_PC9801_FM2OP::programChange(uint8 program) {
writeReg(_regPrt, i, data[pos++]);
}
-void SoundChannel_PC9801_FM2OP::prepareFrequencyAndVolume(bool updateVolume) {
+bool SoundChannel_PC9801_FM2OP::prepareFrequencyAndVolume(bool updateVolume) {
if (recalculateFrequency(_note, _opFreqOffset[_operatorFrqIndex[0]], 0, &_frequencyCourse, &_frequencyNoteModifier) == -1)
- return;
+ return false;
if (recalculateFrequency(_note, _opFreqOffset[_operatorFrqIndex[1]], 0, &_frequencyCourse2, &_frequencyNoteModifier2) == -1)
- return;
+ return false;
+
sendFrequency();
if (updateVolume)
sendVolume();
+
+ return true;
}
void SoundChannel_PC9801_FM2OP::sendSoundOnOff(bool noteOn) {
Commit: bba5513a2d8a6bce05c8bb6289d1a8deff743e4b
https://github.com/scummvm/scummvm/commit/bba5513a2d8a6bce05c8bb6289d1a8deff743e4b
Author: athrxx (athrxx at scummvm.org)
Date: 2019-04-02T20:45:35+03:00
Commit Message:
SCI: - update MT32/GM driver initTrack method
This adds specific support for SCI0_EARLY targets.
Based on and tested with Christmas Cards 1988.
I've not added the volume reset (neither for SCI0_EARLY nor SCI0_LATE), since the ScummVM driver seems to handle volume differently on purpose (probably based on SCI1?).
Changed paths:
engines/sci/sound/drivers/midi.cpp
diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp
index 4f3f0da..eaa3258 100644
--- a/engines/sci/sound/drivers/midi.cpp
+++ b/engines/sci/sound/drivers/midi.cpp
@@ -509,30 +509,53 @@ void MidiPlayer_Midi::initTrack(SciSpan<const byte> &header) {
// or MT32.DRV itself.
setReverb(_defaultReverb);
- /* TODO: I have no idea what SCI_VERSION_0_EARLY games do here.
- Therefore the extra code is restricted to SCI_VERSION_0_LATE for now.*/
- if (_version == SCI_VERSION_0_EARLY)
- return;
-
uint8 caps = header.getInt8At(0);
- if (caps != 0 && caps != 2)
+ if (caps != 0 && (_version == SCI_VERSION_0_EARLY || caps != 2))
return;
- uint8 readPos = 3;
- byte msg[9];
+ uint8 readPos = 1;
uint8 flags = 0;
+ byte msg[9];
+ memset(msg, 0x10, 9);
- for (int i = 1; i < 9; ++i) {
- readPos++;
- flags = header.getInt8At(readPos++);
- msg[i - 1] = (flags & 1) ? i : 0x10;
- }
+ if (_version == SCI_VERSION_0_EARLY) {
+ uint8 writePos = 0;
+ for (int i = 0; i < 16; ++i) {
+ flags = header.getInt8At(readPos++);
+ if (flags & 8) {
+ // If both flags 1 and 8 are set this will make the driver assign that channel to MT32 part 9.
+ // This suggests that any one channel could be the rhythm channel. I don't know whether this has any practical relevance.
+ // A channel not flagged with 8 can also be assigned to MT-32 part 9 if it just happens to be the last channel. This is how
+ // it is done in the tracks that I have seen so far. Flag 8 without flag 1 is the control channel (not handled in the driver).
+ if (flags & 1) {
+ if (i < 11) {
+ msg[8] = i;
+ writePos++;
+ }
+ } else {
+ debugC(9, kDebugLevelSound, "MidiPlayer_Midi::initTrack(): Control channel found: 0x%.02x", i);
+ }
+ } else if (i < 11 && (flags & 1)) {
+ assert(writePos < 9);
+ msg[writePos++] = i;
+ }
+ }
- flags = header.getInt8At(readPos);
- msg[8] = (flags & 0x80) ? 9 : 0x10;
+ } else {
+ readPos = 3;
+ for (int i = 1; i < 9; ++i) {
+ readPos++;
+ flags = header.getInt8At(readPos++);
+ msg[i - 1] = (flags & 1) ? i : 0x10;
+ }
+
+ flags = header.getInt8At(readPos);
+ msg[8] = (flags & 0x80) ? 9 : 0x10;
+ }
// assign channels
- Sci::SciSpan<const byte> s(msg, 9);
+ debugC(5, kDebugLevelSound, "MidiPlayer_Midi::initTrack(): Channels assigned to MT-32 parts: 0x%.02x 0x%.02x 0x%.02x 0x%.02x 0x%.02x 0x%.02x 0x%.02x 0x%.02x 0x%.02x", msg[0], msg[1], msg[2], msg[3], msg[4], msg[5], msg[6], msg[7], msg[8]);
+ Sci::SciSpan<const byte> s(msg, 9);
sendMt32SysEx(0x10000D, s, false);
}
More information about the Scummvm-git-logs
mailing list