[Scummvm-git-logs] scummvm master -> a68577b9f207c17ea5c08f4fc53f1b7cf9f99803
elasota
noreply at scummvm.org
Sat Jul 2 17:12:33 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:
a68577b9f2 MTROPOLIS: Add single-note MIDI support
Commit: a68577b9f207c17ea5c08f4fc53f1b7cf9f99803
https://github.com/scummvm/scummvm/commit/a68577b9f207c17ea5c08f4fc53f1b7cf9f99803
Author: elasota (ejlasota at gmail.com)
Date: 2022-07-02T13:12:20-04:00
Commit Message:
MTROPOLIS: Add single-note MIDI 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 3ee768b29cd..1ad07a1c46c 100644
--- a/engines/mtropolis/plugin/standard.cpp
+++ b/engines/mtropolis/plugin/standard.cpp
@@ -164,6 +164,8 @@ public:
MidiFilePlayer *createFilePlayer(const Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> &file, bool hasTempoOverride, double tempoOverride, uint8 volume, bool loop, uint16 mutedTracks);
void deleteFilePlayer(MidiFilePlayer *player);
+ Common::SharedPtr<MidiCombinerSource> createSource();
+
void setPlayerVolume(MidiFilePlayer *player, uint8 volume);
void setPlayerLoop(MidiFilePlayer *player, bool loop);
void setPlayerTempo(MidiFilePlayer *player, double tempo);
@@ -1183,7 +1185,7 @@ void MultiMidiPlayer::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 = _combiner->createSource();
+ Common::SharedPtr<MidiCombinerSource> combinerSource = createSource();
combinerSource->setVolume(volume);
Common::SharedPtr<MidiFilePlayerImpl> filePlayer(new MidiFilePlayerImpl(combinerSource, file, getBaseTempo(), hasTempoOverride, tempoOverride, volume, loop, mutedTracks));
@@ -1196,6 +1198,10 @@ MidiFilePlayer *MultiMidiPlayer::createFilePlayer(const Common::SharedPtr<Data::
return filePlayer.get();
}
+Common::SharedPtr<MidiCombinerSource> MultiMidiPlayer::createSource() {
+ return _combiner->createSource();
+}
+
void MultiMidiPlayer::deleteFilePlayer(MidiFilePlayer *player) {
Common::SharedPtr<MidiFilePlayerImpl> ref;
@@ -1921,12 +1927,19 @@ bool ObjectReferenceVariableModifier::SaveLoad::loadInternal(Common::ReadStream
return true;
}
-MidiModifier::MidiModifier() : _plugIn(nullptr), _filePlayer(nullptr), _mutedTracks(0), _isActive(false) {
+MidiModifier::MidiModifier() : _plugIn(nullptr), _filePlayer(nullptr), _mutedTracks(0), _isSingleNoteActive(false),
+ _singleNoteChannel(0), _singleNoteNote(0), _runtime(nullptr), _volume(100) {
}
MidiModifier::~MidiModifier() {
if (_filePlayer)
_plugIn->getMidi()->deleteFilePlayer(_filePlayer);
+
+ if (_isSingleNoteActive)
+ stopSingleNote();
+
+ if (_singleNoteSource)
+ _singleNoteSource->detach();
}
bool MidiModifier::load(const PlugInModifierLoaderContext &context, const Data::Standard::MidiModifier &data) {
@@ -1992,6 +2005,8 @@ VThreadState MidiModifier::consumeMessage(Runtime *runtime, const Common::Shared
} 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);
}
}
if (_terminateWhen.respondsTo(msg->getEvent())) {
@@ -1999,6 +2014,9 @@ VThreadState MidiModifier::consumeMessage(Runtime *runtime, const Common::Shared
_plugIn->getMidi()->deleteFilePlayer(_filePlayer);
_filePlayer = nullptr;
}
+
+ if (_mode == kModeSingleNote)
+ stopSingleNote();
}
return kVThreadReturn;
@@ -2058,10 +2076,62 @@ 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();
+}
+
Common::SharedPtr<Modifier> MidiModifier::shallowClone() const {
Common::SharedPtr<MidiModifier> clone(new MidiModifier(*this));
- clone->_isActive = false;
+ clone->_isSingleNoteActive = false;
+ clone->_filePlayer = nullptr;
+ clone->_singleNoteSource.reset();
return clone;
}
@@ -2186,7 +2256,14 @@ MiniscriptInstructionOutcome MidiModifier::scriptSetTempo(MiniscriptThread *thre
}
MiniscriptInstructionOutcome MidiModifier::scriptSetPlayNote(MiniscriptThread *thread, const DynamicValue &value) {
- warning("MIDI PlayNote is not yet implemented!");
+ if (value.getType() != DynamicValueTypes::kBoolean)
+ return kMiniscriptInstructionOutcomeFailed;
+
+ if (value.getBool())
+ playSingleNote(thread->getRuntime());
+ else
+ stopSingleNote();
+
return kMiniscriptInstructionOutcomeContinue;
}
diff --git a/engines/mtropolis/plugin/standard.h b/engines/mtropolis/plugin/standard.h
index 4f6390a85bc..b17d5a1cbdd 100644
--- a/engines/mtropolis/plugin/standard.h
+++ b/engines/mtropolis/plugin/standard.h
@@ -38,6 +38,7 @@ namespace Standard {
class StandardPlugIn;
class MidiFilePlayer;
class MultiMidiPlayer;
+class MidiCombinerSource;
class CursorModifier : public Modifier {
public:
@@ -218,6 +219,9 @@ 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; }
@@ -243,6 +247,8 @@ private:
MiniscriptInstructionOutcome scriptSetMuteTrack(MiniscriptThread *thread, size_t trackIndex, bool muted);
+ void stopSingleNoteCallback(Runtime *runtime);
+
struct FilePart {
bool loop;
bool overrideTempo;
@@ -279,10 +285,16 @@ private:
Common::SharedPtr<Data::Standard::MidiModifier::EmbeddedFile> _embeddedFile;
uint16 _mutedTracks;
- bool _isActive;
+ bool _isSingleNoteActive;
+ uint8 _singleNoteChannel;
+ uint8 _singleNoteNote;
StandardPlugIn *_plugIn;
MidiFilePlayer *_filePlayer;
+
+ Common::SharedPtr<MidiCombinerSource> _singleNoteSource;
+ Common::SharedPtr<ScheduledEvent> _singleNodeScheduledOffEvent;
+ Runtime *_runtime;
};
class ListVariableModifier : public VariableModifier {
More information about the Scummvm-git-logs
mailing list