From: <sv...@op...> - 2024-12-02 22:43:35
|
Author: sagamusix Date: Mon Dec 2 23:43:23 2024 New Revision: 22462 URL: https://source.openmpt.org/browse/openmpt/?op=revision&rev=22462 Log: [New] The MIDI I/O device can now also send its output to its output plugin. This way, the new macro mechanism can also be used together with plugins and not just MIDI devices. Modified: trunk/OpenMPT/mptrack/plugins/MidiInOut.cpp trunk/OpenMPT/mptrack/plugins/MidiInOut.h trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.cpp trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.h Modified: trunk/OpenMPT/mptrack/plugins/MidiInOut.cpp ============================================================================== --- trunk/OpenMPT/mptrack/plugins/MidiInOut.cpp Mon Dec 2 22:48:10 2024 (r22461) +++ trunk/OpenMPT/mptrack/plugins/MidiInOut.cpp Mon Dec 2 23:43:23 2024 (r22462) @@ -59,7 +59,7 @@ uint32 MidiInOut::GetLatency() const { // There is only a latency if the user-provided latency value is greater than the negative output latency. - return mpt::saturate_round<uint32>(std::min(0.0, m_latency + GetOutputLatency()) * m_SndFile.GetSampleRate()); + return mpt::saturate_round<uint32>(std::min(0.0, m_latency + (m_outputDevice.index == kInternalDevice ? 0.0 : GetOutputLatency())) * m_SndFile.GetSampleRate()); } @@ -85,6 +85,11 @@ uint32 type = memFile.ReadUint32LE(); if(type != 0) return; + + constexpr auto ParameterToDeviceID = [](float value) + { + return static_cast<MidiDevice::ID>(value * 65536.0f) - 1; + }; m_inputDevice.index = ParameterToDeviceID(memFile.ReadFloatLE()); m_outputDevice.index = ParameterToDeviceID(memFile.ReadFloatLE()); } else @@ -150,7 +155,7 @@ std::ostringstream s; mpt::IO::WriteRaw(s, "fEvN", 4); // VST program chunk magic mpt::IO::WriteIntLE< int32>(s, GetVersion()); - mpt::IO::WriteIntLE<uint32>(s, 1); // Number of programs + mpt::IO::WriteIntLE<uint32>(s, 1); // Number of programs mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(programName8.size())); mpt::IO::WriteIntLE<uint32>(s, m_inputDevice.index); mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(m_inputDevice.name.size())); @@ -199,6 +204,8 @@ // Try to match a port name against stored name or friendly name (preferred) static MidiDevice::ID FindPort(MidiDevice::ID id, unsigned int numPorts, const std::string &name, const mpt::ustring &friendlyName, MidiDevice &midiDevice, bool isInput) { + if(id == MidiDevice::NO_MIDI_DEVICE || id == MidiDevice::INTERNAL_MIDI_DEVICE) + return id; bool foundFriendly = false; for(unsigned int i = 0; i < numPorts; i++) { @@ -395,7 +402,7 @@ // Processing (we don't process any audio, only MIDI messages) void MidiInOut::Process(float *, float *, uint32 numFrames) { - if(m_midiOut.isPortOpen()) + if(m_outputDevice.index == kInternalDevice || m_midiOut.isPortOpen()) { mpt::lock_guard<mpt::mutex> lock(m_mutex); @@ -407,7 +414,7 @@ mpt::span<uint8> midiMsg; while(parser.NextMessage(midiMsg)) { - m_midiOut.sendMessage(mpt::byte_cast<unsigned char *>(midiMsg.data()), midiMsg.size()); + SendMessage(midiMsg); } } catch(const RtMidiError &) { @@ -448,7 +455,7 @@ { try { - m_midiOut.sendMessage(message->m_message, message->m_size); + SendMessage(*message); } catch(const RtMidiError &) { } @@ -460,6 +467,15 @@ } +void MidiInOut::SendMessage(mpt::span<const unsigned char> midiMsg) +{ + if(m_outputDevice.index == kInternalDevice) + ReceiveMidi(mpt::byte_cast<mpt::const_byte_span>(midiMsg)); + else + m_midiOut.sendMessage(midiMsg.data(), midiMsg.size()); +} + + void MidiInOut::InputCallback(double /*deltatime*/, std::vector<unsigned char> &message) { // We will check the bypass status before passing on the message, and not before entering the function, @@ -520,7 +536,7 @@ void MidiInOut::Suspend() { // Suspend MIDI I/O - if(m_midiOut.isPortOpen()) + if(m_outputDevice.index == kInternalDevice || m_midiOut.isPortOpen()) { try { @@ -529,13 +545,13 @@ // Need to flush remaining events from HardAllNotesOff for(const auto &message : m_outQueue) { - m_midiOut.sendMessage(message.m_message, message.m_size); + SendMessage(message); } m_outQueue.clear(); if(m_sendTimingInfo) { unsigned char message[1] = { 0xFC }; // Stop - m_midiOut.sendMessage(message, 1); + SendMessage(message); } } catch(const RtMidiError &) { @@ -577,7 +593,7 @@ bool MidiInOut::MidiSend(mpt::const_byte_span midiData) { - if(!m_midiOut.isPortOpen() || IsBypassed()) + if((m_outputDevice.index != kInternalDevice && !m_midiOut.isPortOpen()) || IsBypassed()) { // We need an output device to send MIDI messages to. return true; @@ -653,7 +669,18 @@ { // Dummy device if(updateName) + { device.name = "<none>"; + device.friendlyName.clear(); + } + return; + } else if(device.index == kInternalDevice) + { + if(updateName) + { + device.name = "<internal>"; + device.friendlyName.clear(); + } return; } @@ -703,7 +730,7 @@ // Calculate the current output timestamp double MidiInOut::GetOutputTimestamp() const { - return m_clock.Now() * (1.0 / 1000.0) + GetOutputLatency() + m_latency; + return m_clock.Now() * (1.0 / 1000.0) + (m_outputDevice.index == kInternalDevice ? 0.0 : GetOutputLatency()) + m_latency; } Modified: trunk/OpenMPT/mptrack/plugins/MidiInOut.h ============================================================================== --- trunk/OpenMPT/mptrack/plugins/MidiInOut.h Mon Dec 2 22:48:10 2024 (r22461) +++ trunk/OpenMPT/mptrack/plugins/MidiInOut.h Mon Dec 2 23:43:23 2024 (r22462) @@ -28,6 +28,7 @@ public: using ID = decltype(RtMidiIn().getPortCount()); static constexpr ID NO_MIDI_DEVICE = ID(-1); + static constexpr ID INTERNAL_MIDI_DEVICE = ID(-2); RtMidi &stream; std::string name; // Charset::UTF8 @@ -58,9 +59,8 @@ kNumParams = kMacroParamMax + 1, kNumVisibleParams = 0, - kNoDevice = MidiDevice::NO_MIDI_DEVICE, - //kDeviceInternal, - kMaxDevices = 65536, // Should be a power of 2 to avoid rounding errors. + kNoDevice = MidiDevice::NO_MIDI_DEVICE, + kInternalDevice = MidiDevice::INTERNAL_MIDI_DEVICE, }; // MIDI queue entry with small storage optimiziation. @@ -92,6 +92,8 @@ Message(double time, unsigned char msg) noexcept : Message(time, &msg, 1) { } + operator mpt::span<const unsigned char>() const { return mpt::as_span(m_message, m_size); } + Message(Message &&other) noexcept : m_time(other.m_time) , m_size(other.m_size) @@ -153,12 +155,6 @@ MidiInOut(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN &mixStruct); ~MidiInOut(); - // Translate a VST parameter to an RtMidi device ID (for restoring old plugin version chunks) - static MidiDevice::ID ParameterToDeviceID(float value) - { - return static_cast<MidiDevice::ID>(value * static_cast<float>(kMaxDevices)) - 1; - } - ///////////////////////////////////////////////// // Destroy the plugin int32 GetUID() const final { return 'MMID'; } @@ -237,6 +233,8 @@ // Calculate the current output timestamp double GetOutputTimestamp() const; + + void SendMessage(mpt::span<const unsigned char> midiMsg); }; Modified: trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.cpp ============================================================================== --- trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.cpp Mon Dec 2 22:48:10 2024 (r22461) +++ trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.cpp Mon Dec 2 23:43:23 2024 (r22462) @@ -15,6 +15,7 @@ #include "../FileDialog.h" #include "../Mptrack.h" #include "../resource.h" +#include "../UpdateHints.h" #include "../../soundlib/MIDIEvents.h" #include "../../soundlib/MIDIMacroParser.h" #include <rtmidi/RtMidi.h> @@ -71,6 +72,7 @@ m_latencySpin.SetRange32(mpt::saturate_round<int>(plugin.GetOutputLatency() * -1000.0), int32_max); PopulateList(m_inputCombo, plugin.m_midiIn, plugin.m_inputDevice, true); PopulateList(m_outputCombo, plugin.m_midiOut, plugin.m_outputDevice, false); + UpdateOutputPlugin(); CheckDlgButton(IDC_CHECK1, plugin.m_sendTimingInfo ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(IDC_CHECK2, plugin.m_alwaysSendInitialDump ? BST_UNCHECKED : BST_CHECKED); UpdateMidiDump(); @@ -88,6 +90,15 @@ } +void MidiInOutEditor::UpdateView(UpdateHint hint) +{ + CAbstractVstEditor::UpdateView(hint); + PluginHint pluginHint = hint.ToType<PluginHint>(); + if(pluginHint.GetType()[HINT_MODTYPE | HINT_PLUGINNAMES] || pluginHint.GetPlugin() == (m_VstPlugin.GetSlot() + 1)) + UpdateOutputPlugin(); +} + + void MidiInOutEditor::UpdateMidiDump() { MidiInOut &plugin = static_cast<MidiInOut &>(m_VstPlugin); @@ -121,6 +132,10 @@ // Add dummy device combo.SetItemData(combo.AddString(_T("<none>")), static_cast<DWORD_PTR>(MidiInOut::kNoDevice)); + if(!isInput) + { + combo.SetItemData(combo.AddString(_T("Internal OpenMPT Output")), static_cast<DWORD_PTR>(MidiInOut::kInternalDevice)); + } // Go through all RtMidi devices auto ports = rtDevice.getPortCount(); @@ -146,6 +161,21 @@ } +void MidiInOutEditor::UpdateOutputPlugin() +{ + MPT_ASSERT(m_outputCombo.GetItemData(1) == MidiInOut::kInternalDevice); + const int sel = m_outputCombo.GetCurSel(); + CString outputPlugin; + if(std::vector<IMixPlugin *> plug; m_VstPlugin.GetOutputPlugList(plug) && plug.front() != nullptr) + outputPlugin = MPT_CFORMAT("FX{}: {}")(mpt::cfmt::dec0<2>(plug.front()->GetSlot() + 1), mpt::ToCString(m_VstPlugin.GetSoundFile().m_MixPlugins[plug.front()->GetSlot()].GetName())); + else + outputPlugin = _T("No Plugin"); + m_outputCombo.DeleteString(1); + m_outputCombo.SetItemData(m_outputCombo.InsertString(1, MPT_CFORMAT("Internal OpenMPT Output ({})")(outputPlugin)), static_cast<DWORD_PTR>(MidiInOut::kInternalDevice)); + m_outputCombo.SetCurSel(sel); +} + + // Refresh current input / output device in GUI void MidiInOutEditor::SetCurrentDevice(CComboBox &combo, MidiDevice::ID device) { Modified: trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.h ============================================================================== --- trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.h Mon Dec 2 22:48:10 2024 (r22461) +++ trunk/OpenMPT/mptrack/plugins/MidiInOutEditor.h Mon Dec 2 23:43:23 2024 (r22462) @@ -42,6 +42,7 @@ } bool OpenEditor(CWnd *parent) override; + void UpdateView(UpdateHint hint) override; bool IsResizable() const override { return false; } bool SetSize(int, int) override { return false; } @@ -49,6 +50,7 @@ // Update lists of available input / output devices static void PopulateList(CComboBox &combo, RtMidi &rtDevice, MidiDevice &midiDevice, bool isInput); + void UpdateOutputPlugin(); // Refresh current input / output device in GUI void SetCurrentDevice(CComboBox &combo, MidiDevice::ID device); |