[Scummvm-git-logs] scummvm master -> 07599fff5afbd6aa649ea02e5962329091358968
elasota
noreply at scummvm.org
Sat Jul 9 04:29:52 UTC 2022
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
c2e764afe7 MTROPOLIS: Add hack to boost MIDI volume for Obsidian
07599fff5a MTROPOLIS: Refactor single-note behavior, fix thread safety, fix incorrect durations, fix expression being 0
Commit: c2e764afe7cffcabd9c206e026a3bf65c72ec857
https://github.com/scummvm/scummvm/commit/c2e764afe7cffcabd9c206e026a3bf65c72ec857
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-09T00:20:37-04:00
Commit Message:
MTROPOLIS: Add hack to boost MIDI volume for Obsidian
Changed paths:
engines/mtropolis/hacks.cpp
engines/mtropolis/hacks.h
diff --git a/engines/mtropolis/hacks.cpp b/engines/mtropolis/hacks.cpp
index 0dde0a13be9..31aa28bf538 100644
--- a/engines/mtropolis/hacks.cpp
+++ b/engines/mtropolis/hacks.cpp
@@ -33,6 +33,7 @@ namespace MTropolis {
Hacks::Hacks() {
ignoreMismatchedProjectNameInObjectLookups = false;
+ midiVolumeScale = 256;
}
Hacks::~Hacks() {
@@ -153,6 +154,9 @@ void addObsidianBugFixes(const MTropolisGameDescription &desc, Hacks &hacks) {
// the player leaves the journal without clicking Continue.
hacks.ignoreMismatchedProjectNameInObjectLookups = true;
+ // Bump 70% volume musics to 100%
+ hacks.midiVolumeScale = (100 * 256 / 70);
+
// Fix for corrupted frame in transition from the outer edge in Spider to the air puzzle tower.
// The data is corrupted in both Mac and Win retail versions.
hacks.addAssetHooks(Common::SharedPtr<AssetHooks>(new ObsidianCorruptedAirTowerTransitionFix()));
diff --git a/engines/mtropolis/hacks.h b/engines/mtropolis/hacks.h
index 3320f2d3087..110a55913fe 100644
--- a/engines/mtropolis/hacks.h
+++ b/engines/mtropolis/hacks.h
@@ -47,6 +47,7 @@ struct Hacks {
void addSaveLoadHooks(const Common::SharedPtr<SaveLoadHooks> &hooks);
bool ignoreMismatchedProjectNameInObjectLookups;
+ uint midiVolumeScale; // 256 = 1.0
Common::Point reportDisplaySize; // If X or Y is non-zero, report this as the display size
Common::Point mainWindowOffset; // Coordinate offset of the main window
Commit: 07599fff5afbd6aa649ea02e5962329091358968
https://github.com/scummvm/scummvm/commit/07599fff5afbd6aa649ea02e5962329091358968
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-09T00:20:38-04:00
Commit Message:
MTROPOLIS: Refactor single-note behavior, fix thread safety, fix incorrect durations, fix expression being 0
Changed paths:
engines/mtropolis/plugin/standard.cpp
engines/mtropolis/plugin/standard.h
diff --git a/engines/mtropolis/plugin/standard.cpp b/engines/mtropolis/plugin/standard.cpp
index 12754554c76..767f1d8205a 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -21,6 +21,7 @@
#include "common/random.h"
#include "common/config-manager.h"
+#include "common/file.h"
#include "audio/mididrv.h"
#include "audio/midiplayer.h"
@@ -53,16 +54,20 @@ public:
virtual ~MidiFilePlayer();
};
+class MidiNotePlayer {
+public:
+ virtual ~MidiNotePlayer();
+};
+
class MidiCombinerSource : public MidiDriver_BASE {
public:
virtual ~MidiCombinerSource();
-
+
// Do not call this directly, it's not thread-safe, expose via MultiMidiPlayer
virtual void setVolume(uint8 volume) = 0;
virtual void detach() = 0;
};
-
MidiCombinerSource::~MidiCombinerSource() {
}
@@ -94,7 +99,6 @@ private:
bool _hasTempoOverride;
};
-
MidiParser_MTropolis::MidiParser_MTropolis(bool hasTempoOverride, double tempoOverride, uint16 mutedTracks)
: _hasTempoOverride(hasTempoOverride), _tempoOverride(tempoOverride), _mutedTracks(mutedTracks) {
}
@@ -158,13 +162,38 @@ private:
bool _loop;
};
+class MidiNotePlayerImpl : public MidiNotePlayer {
+public:
+ explicit MidiNotePlayerImpl(const Common::SharedPtr<MidiCombinerSource> &outputDriver, uint32 timerRate);
+ ~MidiNotePlayerImpl();
+
+ // Do not call any of these directly since they're not thread-safe, expose them via MultiMidiPlayer
+ void onTimer();
+ void play(uint8 volume, uint8 channel, uint8 program, uint8 note, uint8 velocity, double duration);
+ void stop();
+ void detach();
+
+private:
+ Common::SharedPtr<MidiCombinerSource> _outputDriver;
+ uint64 _durationRemaining;
+ uint32 _timerRate;
+ uint8 _channel;
+ uint8 _note;
+ uint8 _volume;
+ uint8 _program;
+
+ bool _initialized;
+};
+
class MultiMidiPlayer : public Audio::MidiPlayer {
public:
explicit MultiMidiPlayer(bool useDynamicMidiMixer);
~MultiMidiPlayer();
MidiFilePlayer *createFilePlayer(const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, bool hasTempoOverride, double tempoOverride, uint8 volume, bool loop, uint16 mutedTracks);
+ MidiNotePlayer *createNotePlayer();
void deleteFilePlayer(MidiFilePlayer *player);
+ void deleteNotePlayer(MidiNotePlayer *player);
Common::SharedPtr<MidiCombinerSource> createSource();
@@ -177,6 +206,9 @@ public:
void pausePlayer(MidiFilePlayer *player);
void resumePlayer(MidiFilePlayer *player);
+ void playNote(MidiNotePlayer *player, uint8 volume, uint8 channel, uint8 program, uint8 note, uint8 velocity, double duration);
+ void stopNote(MidiNotePlayer *player);
+
uint32 getBaseTempo() const;
void send(uint32 b) override;
@@ -187,13 +219,17 @@ private:
static void timerCallback(void *refCon);
Common::Mutex _mutex;
- Common::Array<Common::SharedPtr<MidiFilePlayerImpl> > _players;
+ Common::Array<Common::SharedPtr<MidiFilePlayerImpl> > _filePlayers;
+ Common::Array<Common::SharedPtr<MidiNotePlayerImpl> > _notePlayers;
Common::SharedPtr<MidiCombiner> _combiner;
};
MidiFilePlayer::~MidiFilePlayer() {
}
+MidiNotePlayer::~MidiNotePlayer() {
+}
+
MidiFilePlayerImpl::MidiFilePlayerImpl(const Common::SharedPtr<MidiCombinerSource> &outputDriver, const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, uint32 baseTempo, bool hasTempoOverride, double tempo, uint8 volume, bool loop, uint16 mutedTracks)
: _file(file), _outputDriver(outputDriver), _parser(nullptr), _loop(loop), _mutedTracks(mutedTracks) {
Common::SharedPtr<MidiParser_MTropolis> parser(new MidiParser_MTropolis(hasTempoOverride, tempo, mutedTracks));
@@ -210,7 +246,7 @@ MidiFilePlayerImpl::MidiFilePlayerImpl(const Common::SharedPtr<MidiCombinerSourc
}
MidiFilePlayerImpl::~MidiFilePlayerImpl() {
- assert(!_parser); // Call detach first!
+ assert(!_parser); // Call detach first!
}
void MidiFilePlayerImpl::stop() {
@@ -264,6 +300,70 @@ void MidiFilePlayerImpl::onTimer() {
_parser->onTimer();
}
+MidiNotePlayerImpl::MidiNotePlayerImpl(const Common::SharedPtr<MidiCombinerSource> &outputDriver, uint32 timerRate)
+ : _timerRate(timerRate), _durationRemaining(0), _outputDriver(outputDriver), _channel(0), _note(0), _program(0), _initialized(false) {
+}
+
+MidiNotePlayerImpl::~MidiNotePlayerImpl() {
+}
+
+void MidiNotePlayerImpl::onTimer() {
+ if (_durationRemaining > 0) {
+ if (_durationRemaining <= _timerRate) {
+ stop();
+ assert(_durationRemaining == 0);
+ } else {
+ _durationRemaining -= _timerRate;
+ }
+ }
+}
+
+void MidiNotePlayerImpl::play(uint8 volume, uint8 channel, uint8 program, uint8 note, uint8 velocity, double duration) {
+ if (duration < 0.000001)
+ return;
+
+ if (_durationRemaining)
+ stop();
+
+ bool mustResetControllers = false;
+ if (!_initialized) {
+ mustResetControllers = true;
+ _initialized = true;
+ }
+
+ _durationRemaining = static_cast<uint64>(duration * 1000000);
+ _channel = channel;
+ _note = note;
+ _volume = volume;
+
+ const uint16 hpVolume = volume * 0x3fff / 100;
+
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_PROGRAM_CHANGE | _channel, program, 0);
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_CONTROL_CHANGE | _channel, MidiDriver_BASE::MIDI_CONTROLLER_EXPRESSION, 127);
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_CONTROL_CHANGE | _channel, MidiDriver_BASE::MIDI_CONTROLLER_REVERB, 0);
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_CONTROL_CHANGE | _channel, MidiDriver_BASE::MIDI_CONTROLLER_VOLUME, (hpVolume >> 7) & 0x7f);
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_CONTROL_CHANGE | _channel, MidiDriver_BASE::MIDI_CONTROLLER_VOLUME + 32, hpVolume & 0x7f);
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_NOTE_ON | _channel, note, velocity);
+}
+
+void MidiNotePlayerImpl::stop() {
+ if (!_durationRemaining)
+ return;
+
+ _durationRemaining = 0;
+ _outputDriver->send(MidiDriver_BASE::MIDI_COMMAND_NOTE_OFF | _channel, _note, 0);
+}
+
+void MidiNotePlayerImpl::detach() {
+ if (_outputDriver) {
+ if (_durationRemaining)
+ stop();
+
+ _outputDriver->detach();
+ _outputDriver.reset();
+ }
+}
+
// Simple combiner - Behaves "QuickTime-like" and all commands are passed through directly.
// This applies volume by modulating note velocity.
class MidiCombinerSimple;
@@ -464,6 +564,9 @@ private:
uint _noteOffCounter;
MidiDriver_BASE *_outputDriver;
+
+ Common::SharedPtr<Common::DumpFile> _dumpFile;
+ int _eventCounter;
};
MidiCombinerSourceDynamic::MidiCombinerSourceDynamic(MidiCombinerDynamic *combiner, uint sourceID) : _combiner(combiner), _sourceID(sourceID) {
@@ -487,6 +590,13 @@ void MidiCombinerSourceDynamic::send(uint32 b) {
}
MidiCombinerDynamic::MidiCombinerDynamic(MidiDriver_BASE *outputDriver) : _outputDriver(outputDriver), _noteOffCounter(1) {
+#if 0
+ _dumpFile.reset(new Common::DumpFile());
+ _dumpFile->open("mididump.csv");
+ _dumpFile->writeString("event\ttime\tchannel\tcmd\tparam1\tparam2\n");
+#endif
+
+ _eventCounter = 0;
}
Common::SharedPtr<MidiCombinerSource> MidiCombinerDynamic::createSource() {
@@ -993,6 +1103,131 @@ void MidiCombinerDynamic::doResetAllControllers(uint sourceID, uint8 channel, ui
void MidiCombinerDynamic::sendToOutput(uint8 command, uint8 channel, uint8 param1, uint8 param2) {
uint32 output = static_cast<uint32>(command) | static_cast<uint32>(channel) | static_cast<uint32>(param1 << 8) | static_cast<uint32>(param2 << 16);
+
+ if (_dumpFile) {
+ const int timestamp = g_system->getMillis(true);
+
+ const char *cmdName = "Unknown Command";
+ switch (command) {
+ case MidiDriver_BASE::MIDI_COMMAND_CHANNEL_AFTERTOUCH:
+ cmdName = "ChannelAftertouch";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_NOTE_OFF:
+ cmdName = "NoteOff";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_NOTE_ON:
+ cmdName = "NoteOn";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_POLYPHONIC_AFTERTOUCH:
+ cmdName = "PolyAftertouch";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_CONTROL_CHANGE:
+ cmdName = "ControlChange";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_PROGRAM_CHANGE:
+ cmdName = "ProgramChange";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_PITCH_BEND:
+ cmdName = "PitchBend";
+ break;
+ case MidiDriver_BASE::MIDI_COMMAND_SYSTEM:
+ cmdName = "System";
+ break;
+ default:
+ cmdName = "Unknown";
+ }
+
+ if (command == MidiDriver_BASE::MIDI_COMMAND_CONTROL_CHANGE) {
+ Common::String ctrlName = "Unknown";
+
+ switch (param1) {
+ case MidiDriver_BASE::MIDI_CONTROLLER_BANK_SELECT_MSB:
+ ctrlName = "BankSelect";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_MODULATION:
+ ctrlName = "Modulation";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_DATA_ENTRY_MSB:
+ ctrlName = "DataEntryMSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_VOLUME:
+ ctrlName = "VolumeMSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_VOLUME + 32:
+ ctrlName = "VolumeLSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_BALANCE:
+ ctrlName = "Balance";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_PANNING:
+ ctrlName = "Panning";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_EXPRESSION:
+ ctrlName = "Expression";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_BANK_SELECT_LSB:
+ ctrlName = "BankSelectLSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_DATA_ENTRY_LSB:
+ ctrlName = "DataEntryLSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_SUSTAIN:
+ ctrlName = "Sustain";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_PORTAMENTO:
+ ctrlName = "Portamento";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_SOSTENUTO:
+ ctrlName = "Sostenuto";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_SOFT:
+ ctrlName = "Soft";
+ break;
+
+ case MidiDriver_BASE::MIDI_CONTROLLER_REVERB:
+ ctrlName = "Reverb";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_CHORUS:
+ ctrlName = "Chorus";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_RPN_LSB:
+ ctrlName = "RPNLSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_RPN_MSB:
+ ctrlName = "RPNMSB";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_ALL_SOUND_OFF:
+ ctrlName = "AllSoundOff";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_RESET_ALL_CONTROLLERS:
+ ctrlName = "ResetAllControllers";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_ALL_NOTES_OFF:
+ ctrlName = "AllNotesOff";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_OMNI_ON:
+ ctrlName = "OmniOn";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_OMNI_OFF:
+ ctrlName = "OmniOff";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_MONO_ON:
+ ctrlName = "MonoOn";
+ break;
+ case MidiDriver_BASE::MIDI_CONTROLLER_POLY_ON:
+ ctrlName = "PolyOn";
+ break;
+ default:
+ ctrlName = Common::String::format("Unknown%02x", static_cast<int>(param1));
+ }
+
+ _dumpFile->writeString(Common::String::format("%i\t%i\t%i\t%s\t%s\t%i\n", _eventCounter, timestamp, static_cast<int>(channel), cmdName, ctrlName.c_str(), static_cast<int>(param2)));
+ } else
+ _dumpFile->writeString(Common::String::format("%i\t%i\t%i\t%s\t%i\t%i\n", _eventCounter, timestamp, static_cast<int>(channel), cmdName, static_cast<int>(param1), static_cast<int>(param2)));
+
+ _eventCounter++;
+ }
+
_outputDriver->send(output);
}
@@ -1173,7 +1408,8 @@ MultiMidiPlayer::MultiMidiPlayer(bool dynamicMidiMixer) {
MultiMidiPlayer::~MultiMidiPlayer() {
Common::StackLock lock(_mutex);
-
+ _filePlayers.clear();
+ _notePlayers.clear();
}
void MultiMidiPlayer::timerCallback(void *refCon) {
@@ -1183,38 +1419,71 @@ void MultiMidiPlayer::timerCallback(void *refCon) {
void MultiMidiPlayer::onTimer() {
Common::StackLock lock(_mutex);
- for (const Common::SharedPtr<MidiFilePlayerImpl> &player : _players)
+ for (const Common::SharedPtr<MidiFilePlayerImpl> &player : _filePlayers)
player->onTimer();
-}
+ for (const Common::SharedPtr<MidiNotePlayerImpl> &player : _notePlayers)
+ player->onTimer();
+}
MidiFilePlayer *MultiMidiPlayer::createFilePlayer(const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, bool hasTempoOverride, double tempoOverride, uint8 volume, bool loop, uint16 mutedTracks) {
Common::SharedPtr<MidiCombinerSource> combinerSource = createSource();
- combinerSource->setVolume(volume);
-
Common::SharedPtr<MidiFilePlayerImpl> filePlayer(new MidiFilePlayerImpl(combinerSource, file, getBaseTempo(), hasTempoOverride, tempoOverride, volume, loop, mutedTracks));
{
Common::StackLock lock(_mutex);
- _players.push_back(filePlayer);
+ combinerSource->setVolume(volume);
+ _filePlayers.push_back(filePlayer);
}
return filePlayer.get();
}
+MidiNotePlayer *MultiMidiPlayer::createNotePlayer() {
+ Common::SharedPtr<MidiCombinerSource> combinerSource = createSource();
+ Common::SharedPtr<MidiNotePlayerImpl> notePlayer(new MidiNotePlayerImpl(combinerSource, getBaseTempo()));
+
+ {
+ Common::StackLock lock(_mutex);
+ _notePlayers.push_back(notePlayer);
+ }
+
+ return notePlayer.get();
+}
+
Common::SharedPtr<MidiCombinerSource> MultiMidiPlayer::createSource() {
+ Common::StackLock lock(_mutex);
return _combiner->createSource();
}
void MultiMidiPlayer::deleteFilePlayer(MidiFilePlayer *player) {
Common::SharedPtr<MidiFilePlayerImpl> ref;
- for (Common::Array<Common::SharedPtr<MidiFilePlayerImpl> >::iterator it = _players.begin(), itEnd = _players.end(); it != itEnd; ++it) {
+ for (Common::Array<Common::SharedPtr<MidiFilePlayerImpl> >::iterator it = _filePlayers.begin(), itEnd = _filePlayers.end(); it != itEnd; ++it) {
if (it->get() == player) {
{
Common::StackLock lock(_mutex);
ref = *it;
- _players.erase(it);
+ _filePlayers.erase(it);
+ ref->stop();
+ }
+ break;
+ }
+ }
+
+ if (ref)
+ ref->detach();
+}
+
+void MultiMidiPlayer::deleteNotePlayer(MidiNotePlayer *player) {
+ Common::SharedPtr<MidiNotePlayerImpl> ref;
+
+ for (Common::Array<Common::SharedPtr<MidiNotePlayerImpl> >::iterator it = _notePlayers.begin(), itEnd = _notePlayers.end(); it != itEnd; ++it) {
+ if (it->get() == player) {
+ {
+ Common::StackLock lock(_mutex);
+ ref = *it;
+ _notePlayers.erase(it);
ref->stop();
}
break;
@@ -1265,6 +1534,16 @@ void MultiMidiPlayer::resumePlayer(MidiFilePlayer *player) {
static_cast<MidiFilePlayerImpl *>(player)->resume();
}
+void MultiMidiPlayer::playNote(MidiNotePlayer *player, uint8 volume, uint8 channel, uint8 program, uint8 note, uint8 velocity, double duration) {
+ Common::StackLock lock(_mutex);
+ static_cast<MidiNotePlayerImpl *>(player)->play(volume, channel, program, note, velocity, duration);
+}
+
+void MultiMidiPlayer::stopNote(MidiNotePlayer *player) {
+ Common::StackLock lock(_mutex);
+ static_cast<MidiNotePlayerImpl *>(player)->stop();
+}
+
uint32 MultiMidiPlayer::getBaseTempo() const {
return _driver->getBaseTempo();
}
@@ -1942,7 +2221,7 @@ bool ObjectReferenceVariableModifier::SaveLoad::loadInternal(Common::ReadStream
return true;
}
-MidiModifier::MidiModifier() : _plugIn(nullptr), _filePlayer(nullptr), _mutedTracks(0), _isSingleNoteActive(false),
+MidiModifier::MidiModifier() : _plugIn(nullptr), _filePlayer(nullptr), _notePlayer(nullptr), _mutedTracks(0),
_singleNoteChannel(0), _singleNoteNote(0), _runtime(nullptr), _volume(100) {
}
@@ -1950,11 +2229,8 @@ MidiModifier::~MidiModifier() {
if (_filePlayer)
_plugIn->getMidi()->deleteFilePlayer(_filePlayer);
- if (_isSingleNoteActive)
- stopSingleNote();
-
- if (_singleNoteSource)
- _singleNoteSource->detach();
+ if (_notePlayer)
+ _plugIn->getMidi()->deleteNotePlayer(_notePlayer);
}
bool MidiModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::MidiModifier &data) {
@@ -2018,13 +2294,13 @@ VThreadState MidiModifier::consumeMessage(Runtime *runtime, const Common::Shared
const double tempo = _modeSpecific.file.overrideTempo ? _modeSpecific.file.tempo : 120.0;
if (!_filePlayer)
- _filePlayer = _plugIn->getMidi()->createFilePlayer(_embeddedFile, _modeSpecific.file.overrideTempo, tempo, _volume * 255 / 100, _modeSpecific.file.loop, _mutedTracks);
+ _filePlayer = _plugIn->getMidi()->createFilePlayer(_embeddedFile, _modeSpecific.file.overrideTempo, tempo, getBoostedVolume(runtime) * 255 / 100, _modeSpecific.file.loop, _mutedTracks);
_plugIn->getMidi()->playPlayer(_filePlayer);
} else {
debug(2, "MIDI (%x '%s'): Digested execute event but don't have anything to play", getStaticGUID(), getName().c_str());
}
} else if (_mode == kModeSingleNote) {
- playSingleNote(runtime);
+ playSingleNote();
}
}
if (_terminateWhen.respondsTo(msg->getEvent())) {
@@ -2032,14 +2308,26 @@ VThreadState MidiModifier::consumeMessage(Runtime *runtime, const Common::Shared
_plugIn->getMidi()->deleteFilePlayer(_filePlayer);
_filePlayer = nullptr;
}
-
- if (_mode == kModeSingleNote)
- stopSingleNote();
+ if (_notePlayer) {
+ _plugIn->getMidi()->deleteNotePlayer(_notePlayer);
+ _notePlayer = nullptr;
+ }
}
return kVThreadReturn;
}
+void MidiModifier::playSingleNote() {
+ if (!_notePlayer)
+ _notePlayer = _plugIn->getMidi()->createNotePlayer();
+ _plugIn->getMidi()->playNote(_notePlayer, _volume, _modeSpecific.singleNote.channel, _modeSpecific.singleNote.program, _modeSpecific.singleNote.note, _modeSpecific.singleNote.velocity, _modeSpecific.singleNote.duration);
+}
+
+void MidiModifier::stopSingleNote() {
+ if (_notePlayer)
+ _plugIn->getMidi()->stopNote(_notePlayer);
+}
+
bool MidiModifier::readAttribute(MiniscriptThread *thread, DynamicValue &result, const Common::String &attrib) {
if (attrib == "volume") {
result.setInt(_volume);
@@ -2094,62 +2382,19 @@ MiniscriptInstructionOutcome MidiModifier::writeRefAttributeIndexed(MiniscriptTh
return Modifier::writeRefAttributeIndexed(thread, result, attrib, index);
}
-void MidiModifier::playSingleNote(Runtime *runtime) {
- if (_isSingleNoteActive)
- stopSingleNote();
-
- if (!_singleNoteSource)
- _singleNoteSource = _plugIn->getMidi()->createSource();
-
- const SingleNotePart &snPart = _modeSpecific.singleNote;
-
- _isSingleNoteActive = true;
- _singleNoteChannel = snPart.channel;
- _singleNoteNote = snPart.note;
- _runtime = runtime;
-
- _singleNoteSource->setVolume(_volume * 255 / 100);
-
- const uint32 changeProgramCmd = (MidiDriver_BASE::MIDI_COMMAND_PROGRAM_CHANGE | _singleNoteChannel) | (snPart.program << 8);
- const uint32 noteOnCmd = (MidiDriver_BASE::MIDI_COMMAND_NOTE_ON | _singleNoteChannel) | (snPart.note << 8) | (snPart.velocity << 16);
-
- // Should we reset controllers too?
- _singleNoteSource->send(changeProgramCmd);
- _singleNoteSource->send(noteOnCmd);
-
- double delayMS = snPart.duration * 1000.0;
- if (delayMS < 1.0)
- delayMS = 1.0;
-
- _singleNodeScheduledOffEvent = _runtime->getScheduler().scheduleMethod<MidiModifier, &MidiModifier::stopSingleNoteCallback>(_runtime->getPlayTime() + static_cast<uint64>(delayMS), this);
-}
-
-void MidiModifier::stopSingleNote() {
- if (_isSingleNoteActive) {
- // Should we include velocity?
- const uint32 noteOffCmd = (MidiDriver_BASE::MIDI_COMMAND_NOTE_OFF | _singleNoteChannel) | (_singleNoteNote << 8);
-
- _singleNoteSource->send(noteOffCmd);
-
- _isSingleNoteActive = false;
- if (_singleNodeScheduledOffEvent) {
- _singleNodeScheduledOffEvent->cancel();
- _singleNodeScheduledOffEvent.reset();
- }
- }
-}
-void MidiModifier::stopSingleNoteCallback(Runtime *runtime) {
- _singleNodeScheduledOffEvent.reset();
- stopSingleNote();
+uint MidiModifier::getBoostedVolume(Runtime *runtime) const {
+ uint boostedVolume = (_volume * runtime->getHacks().midiVolumeScale) >> 8;
+ if (boostedVolume > 100)
+ boostedVolume = 100;
+ return boostedVolume;
}
Common::SharedPtr<Modifier> MidiModifier::shallowClone() const {
Common::SharedPtr<MidiModifier> clone(new MidiModifier(*this));
- clone->_isSingleNoteActive = false;
+ clone->_notePlayer = nullptr;
clone->_filePlayer = nullptr;
- clone->_singleNoteSource.reset();
return clone;
}
@@ -2173,7 +2418,7 @@ MiniscriptInstructionOutcome MidiModifier::scriptSetVolume(MiniscriptThread *thr
if (_mode == kModeFile) {
debug(2, "MIDI (%x '%s'): Changing volume to %i", getStaticGUID(), getName().c_str(), _volume);
if (_filePlayer)
- _plugIn->getMidi()->setPlayerVolume(_filePlayer, _volume * 255 / 100);
+ _plugIn->getMidi()->setPlayerVolume(_filePlayer, getBoostedVolume(thread->getRuntime()) * 255 / 100);
}
return kMiniscriptInstructionOutcomeContinue;
@@ -2275,7 +2520,7 @@ MiniscriptInstructionOutcome MidiModifier::scriptSetTempo(MiniscriptThread *thre
MiniscriptInstructionOutcome MidiModifier::scriptSetPlayNote(MiniscriptThread *thread, const DynamicValue &value) {
if (miniscriptEvaluateTruth(value))
- playSingleNote(thread->getRuntime());
+ playSingleNote();
else
stopSingleNote();
diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h
index 8c655df95ae..ebb3242c652 100644
--- a/engines/mtropolis/plugin/standard.h
+++ b/engines/mtropolis/plugin/standard.h
@@ -37,6 +37,7 @@ namespace Standard {
class StandardPlugIn;
class MidiFilePlayer;
+class MidiNotePlayer;
class MultiMidiPlayer;
class MidiCombinerSource;
@@ -219,9 +220,6 @@ public:
MiniscriptInstructionOutcome writeRefAttribute(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib) override;
MiniscriptInstructionOutcome writeRefAttributeIndexed(MiniscriptThread *thread, DynamicValueWriteProxy &result, const Common::String &attrib, const DynamicValue &index) override;
- void playSingleNote(Runtime *runtime);
- void stopSingleNote();
-
#ifdef MTROPOLIS_DEBUG_ENABLE
const char *debugGetTypeName() const override { return "MIDI Modifier"; }
SupportStatus debugGetSupportStatus() const override { return kSupportStatusDone; }
@@ -247,7 +245,10 @@ private:
MiniscriptInstructionOutcome scriptSetMuteTrack(MiniscriptThread *thread, size_t trackIndex, bool muted);
- void stopSingleNoteCallback(Runtime *runtime);
+ uint getBoostedVolume(Runtime *runtime) const;
+
+ void playSingleNote();
+ void stopSingleNote();
struct FilePart {
bool loop;
@@ -285,15 +286,13 @@ private:
Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> _embeddedFile;
uint16 _mutedTracks;
- bool _isSingleNoteActive;
uint8 _singleNoteChannel;
uint8 _singleNoteNote;
StandardPlugIn *_plugIn;
MidiFilePlayer *_filePlayer;
+ MidiNotePlayer *_notePlayer;
- Common::SharedPtr<MidiCombinerSource> _singleNoteSource;
- Common::SharedPtr<ScheduledEvent> _singleNodeScheduledOffEvent;
Runtime *_runtime;
};
More information about the Scummvm-git-logs
mailing list