[Scummvm-git-logs] scummvm master -> fdadf65f4a524469a517346cf522eb48473f3775
elasota
noreply at scummvm.org
Sat Jul 2 16:10:09 UTC 2022
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
fdadf65f4a MTROPOLIS: Add MIDI tempo, loop, and mutetrack support
Commit: fdadf65f4a524469a517346cf522eb48473f3775
https://github.com/scummvm/scummvm/commit/fdadf65f4a524469a517346cf522eb48473f3775
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-02T12:09:51-04:00
Commit Message:
MTROPOLIS: Add MIDI tempo, loop, and mutetrack support
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 81c7feba0c2..bf88e1e8c91 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -22,6 +22,7 @@
#include "audio/mididrv.h"
#include "audio/midiplayer.h"
#include "audio/midiparser.h"
+#include "audio/midiparser_smf.h"
#include "common/random.h"
@@ -73,11 +74,68 @@ public:
MidiCombiner::~MidiCombiner() {
}
+class MidiParser_MTropolis : public MidiParser_SMF {
+public:
+ MidiParser_MTropolis(bool hasTempoOverride, double tempoOverride, uint16 mutedTracks);
+
+ void setTempo(uint32 tempo) override;
+
+ void setTempoOverride(double tempoOverride);
+ void setMutedTracks(uint16 mutedTracks);
+
+protected:
+ bool processEvent(const EventInfo &info, bool fireEvents) override;
+
+private:
+ double _tempoOverride;
+ uint16 _mutedTracks;
+ bool _hasTempoOverride;
+};
+
+
+MidiParser_MTropolis::MidiParser_MTropolis(bool hasTempoOverride, double tempoOverride, uint16 mutedTracks)
+ : _hasTempoOverride(hasTempoOverride), _tempoOverride(tempoOverride), _mutedTracks(mutedTracks) {
+}
+
+void MidiParser_MTropolis::setTempo(uint32 tempo) {
+ if (_hasTempoOverride)
+ return;
+
+ MidiParser_SMF::setTempo(tempo);
+}
+
+void MidiParser_MTropolis::setTempoOverride(double tempoOverride) {
+ _hasTempoOverride = true;
+
+ if (tempoOverride < 1.0)
+ tempoOverride = 1.0;
+
+ _tempoOverride = tempoOverride;
+
+ uint32 convertedTempo = static_cast<uint32>(60000000.0 / tempoOverride);
+
+ MidiParser_SMF::setTempo(convertedTempo);
+}
+
+void MidiParser_MTropolis::setMutedTracks(uint16 mutedTracks) {
+ _mutedTracks = mutedTracks;
+}
+
+bool MidiParser_MTropolis::processEvent(const EventInfo &info, bool fireEvents) {
+ if ((info.event & 0xf0) == MidiDriver_BASE::MIDI_COMMAND_NOTE_ON) {
+ int track = _noteChannelToTrack[info.event & 0xf];
+ if (track >= 0 && (_mutedTracks & (1 << track)))
+ return true;
+ }
+
+ return MidiParser_SMF::processEvent(info, fireEvents);
+}
+
// This extends MidiDriver_BASE because we need to intercept commands to modulate the volume
// separately for each input.
class MidiFilePlayerImpl : public MidiFilePlayer {
public:
- explicit MidiFilePlayerImpl(const Common::SharedPtr<MidiCombinerSource> &outputDriver, const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, uint32 baseTempo, uint8 volume, bool loop, uint16 mutedTracks);
+ explicit MidiFilePlayerImpl(const Common::SharedPtr<MidiCombinerSource> &outputDriver, const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, uint32 baseTempo, bool hasTempoOverride, double tempoOverride, uint8 volume, bool loop, uint16 mutedTracks);
~MidiFilePlayerImpl();
// Do not call any of these directly since they're not thread-safe, expose them via MultiMidiPlayer
@@ -87,14 +145,16 @@ public:
void resume();
void setVolume(uint8 volume);
void setLoop(bool loop);
+ void setTempoOverride(double tempo);
void setMutedTracks(uint16 mutedTracks);
void detach();
void onTimer();
private:
+ static double computeTempoScale(double tempo);
Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> _file;
- Common::SharedPtr<MidiParser> _parser;
+ Common::SharedPtr<MidiParser_MTropolis> _parser;
Common::SharedPtr<MidiCombinerSource> _outputDriver;
uint16 _mutedTracks;
bool _loop;
@@ -105,11 +165,12 @@ public:
MultiMidiPlayer();
~MultiMidiPlayer();
- MidiFilePlayer *createFilePlayer(const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, uint8 volume, bool loop, uint16 mutedTracks);
+ MidiFilePlayer *createFilePlayer(const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, bool hasTempoOverride, double tempoOverride, uint8 volume, bool loop, uint16 mutedTracks);
void deleteFilePlayer(MidiFilePlayer *player);
void setPlayerVolume(MidiFilePlayer *player, uint8 volume);
void setPlayerLoop(MidiFilePlayer *player, bool loop);
+ void setPlayerTempo(MidiFilePlayer *player, double tempo);
void setPlayerMutedTracks(MidiFilePlayer *player, uint16 mutedTracks);
void stopPlayer(MidiFilePlayer *player);
void playPlayer(MidiFilePlayer *player);
@@ -133,17 +194,18 @@ private:
MidiFilePlayer::~MidiFilePlayer() {
}
-MidiFilePlayerImpl::MidiFilePlayerImpl(const Common::SharedPtr<MidiCombinerSource> &outputDriver, const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, uint32 baseTempo, uint8 volume, bool loop, uint16 mutedTracks)
+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> parser(MidiParser::createParser_SMF());
+ Common::SharedPtr<MidiParser_MTropolis> parser(new MidiParser_MTropolis(hasTempoOverride, tempo, mutedTracks));
if (file->contents.size() != 0 && parser->loadMusic(&file->contents[0], file->contents.size())) {
+ _parser = parser;
+
parser->setTrack(0);
parser->startPlaying();
parser->setMidiDriver(outputDriver.get());
parser->setTimerRate(baseTempo);
-
- _parser = parser;
+ parser->property(MidiParser::mpAutoLoop, loop ? 1 : 0);
}
}
@@ -173,10 +235,16 @@ void MidiFilePlayerImpl::setVolume(uint8 volume) {
void MidiFilePlayerImpl::setMutedTracks(uint16 mutedTracks) {
_mutedTracks = mutedTracks;
+ _parser->setMutedTracks(mutedTracks);
}
void MidiFilePlayerImpl::setLoop(bool loop) {
_loop = loop;
+ _parser->property(MidiParser::mpAutoLoop, loop ? 1 : 0);
+}
+
+void MidiFilePlayerImpl::setTempoOverride(double tempo) {
+ _parser->setTempoOverride(tempo);
}
void MidiFilePlayerImpl::detach() {
@@ -196,6 +264,12 @@ void MidiFilePlayerImpl::onTimer() {
_parser->onTimer();
}
+double MidiFilePlayerImpl::computeTempoScale(double tempo) {
+ if (tempo <= 0.0)
+ return 1.0;
+ return 120.0 / tempo;
+}
+
// Simple combiner - Behaves "QuickTime-like" and all commands are passed through directly.
// This applies volume by modulating note velocity.
class MidiCombinerSimple;
@@ -1118,11 +1192,11 @@ void MultiMidiPlayer::onTimer() {
}
-MidiFilePlayer *MultiMidiPlayer::createFilePlayer(const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, uint8 volume, bool loop, uint16 mutedTracks) {
+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 = _combiner->createSource();
combinerSource->setVolume(volume);
- Common::SharedPtr<MidiFilePlayerImpl> filePlayer(new MidiFilePlayerImpl(combinerSource, file, getBaseTempo(), volume, loop, mutedTracks));
+ Common::SharedPtr<MidiFilePlayerImpl> filePlayer(new MidiFilePlayerImpl(combinerSource, file, getBaseTempo(), hasTempoOverride, tempoOverride, volume, loop, mutedTracks));
{
Common::StackLock lock(_mutex);
@@ -1161,6 +1235,11 @@ void MultiMidiPlayer::setPlayerLoop(MidiFilePlayer *player, bool loop) {
static_cast<MidiFilePlayerImpl *>(player)->setLoop(loop);
}
+void MultiMidiPlayer::setPlayerTempo(MidiFilePlayer *player, double tempo) {
+ Common::StackLock lock(_mutex);
+ static_cast<MidiFilePlayerImpl *>(player)->setTempoOverride(tempo);
+}
+
void MultiMidiPlayer::setPlayerMutedTracks(MidiFilePlayer *player, uint16 mutedTracks) {
Common::StackLock lock(_mutex);
static_cast<MidiFilePlayerImpl *>(player)->setMutedTracks(mutedTracks);
@@ -1915,8 +1994,10 @@ VThreadState MidiModifier::consumeMessage(Runtime *runtime, const Common::Shared
if (_mode == kModeFile) {
if (_embeddedFile) {
debug(2, "MIDI (%x '%s'): Playing embedded file", getStaticGUID(), getName().c_str());
+
+ const double tempo = _modeSpecific.file.overrideTempo ? _modeSpecific.file.tempo : 120.0;
if (!_filePlayer)
- _filePlayer = _plugIn->getMidi()->createFilePlayer(_embeddedFile, _volume * 255 / 100, _modeSpecific.file.loop, _mutedTracks);
+ _filePlayer = _plugIn->getMidi()->createFilePlayer(_embeddedFile, _modeSpecific.file.overrideTempo, tempo, _volume * 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());
@@ -1961,6 +2042,9 @@ MiniscriptInstructionOutcome MidiModifier::writeRefAttribute(MiniscriptThread *t
} else if (attrib == "playnote") {
DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetPlayNote>::create(this, result);
return kMiniscriptInstructionOutcomeContinue;
+ } else if (attrib == "tempo") {
+ DynamicValueWriteFuncHelper<MidiModifier, &MidiModifier::scriptSetTempo>::create(this, result);
+ return kMiniscriptInstructionOutcomeContinue;
}
return Modifier::writeRefAttribute(thread, result, attrib);
@@ -2091,6 +2175,26 @@ MiniscriptInstructionOutcome MidiModifier::scriptSetLoop(MiniscriptThread *threa
return kMiniscriptInstructionOutcomeContinue;
}
+MiniscriptInstructionOutcome MidiModifier::scriptSetTempo(MiniscriptThread *thread, const DynamicValue &value) {
+ double tempo = 0.0;
+ if (value.getType() == DynamicValueTypes::kInteger)
+ tempo = value.getInt();
+ else if (value.getType() == DynamicValueTypes::kFloat)
+ tempo = value.getFloat();
+ else
+ return kMiniscriptInstructionOutcomeFailed;
+
+ if (_mode == kModeFile) {
+ debug(2, "MIDI (%x '%s'): Changing tempo to %g", getStaticGUID(), getName().c_str(), tempo);
+
+ if (_filePlayer)
+ _plugIn->getMidi()->setPlayerTempo(_filePlayer, tempo);
+
+ }
+
+ return kMiniscriptInstructionOutcomeContinue;
+}
+
MiniscriptInstructionOutcome MidiModifier::scriptSetPlayNote(MiniscriptThread *thread, const DynamicValue &value) {
warning("MIDI PlayNote is not yet implemented!");
return kMiniscriptInstructionOutcomeContinue;
diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h
index 01cb047d20b..4f6390a85bc 100644
--- a/engines/mtropolis/plugin/standard.h
+++ b/engines/mtropolis/plugin/standard.h
@@ -239,6 +239,7 @@ private:
MiniscriptInstructionOutcome scriptSetNoteNum(MiniscriptThread *thread, const DynamicValue &value);
MiniscriptInstructionOutcome scriptSetLoop(MiniscriptThread *thread, const DynamicValue &value);
MiniscriptInstructionOutcome scriptSetPlayNote(MiniscriptThread *thread, const DynamicValue &value);
+ MiniscriptInstructionOutcome scriptSetTempo(MiniscriptThread *thread, const DynamicValue &value);
MiniscriptInstructionOutcome scriptSetMuteTrack(MiniscriptThread *thread, size_t trackIndex, bool muted);
More information about the Scummvm-git-logs
mailing list