aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/mptrack/plugins
diff options
context:
space:
mode:
authorJean-Francois Mauguit <jfmauguit@mac.com>2024-09-24 09:03:25 -0400
committerGitHub <noreply@github.com>2024-09-24 09:03:25 -0400
commitbab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/plugins
parent4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff)
parent20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff)
downloadwinamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/plugins')
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.cpp401
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.h69
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.cpp547
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.h232
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.cpp161
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.h66
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstDefinitions.h925
-rw-r--r--Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstEventQueue.h126
8 files changed, 2527 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.cpp
new file mode 100644
index 00000000..f840ecfa
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.cpp
@@ -0,0 +1,401 @@
+/*
+ * LFOPluginEditor.cpp
+ * -------------------
+ * Purpose: Editor interface for the LFO plugin.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (OpenMPT Devs)
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+#include "stdafx.h"
+
+#include "LFOPluginEditor.h"
+#include "../Mptrack.h"
+#include "../UpdateHints.h"
+#include "../../soundlib/Sndfile.h"
+#include "../../soundlib/MIDIEvents.h"
+#include "../../mptrack/resource.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+BEGIN_MESSAGE_MAP(LFOPluginEditor, CAbstractVstEditor)
+ //{{AFX_MSG_MAP(LFOPluginEditor)
+ ON_WM_HSCROLL()
+ ON_COMMAND(IDC_BUTTON1, &LFOPluginEditor::OnPluginEditor)
+ ON_COMMAND(IDC_CHECK1, &LFOPluginEditor::OnPolarityChanged)
+ ON_COMMAND(IDC_CHECK2, &LFOPluginEditor::OnTempoSyncChanged)
+ ON_COMMAND(IDC_CHECK3, &LFOPluginEditor::OnBypassChanged)
+ ON_COMMAND(IDC_CHECK4, &LFOPluginEditor::OnLoopModeChanged)
+ ON_COMMAND_RANGE(IDC_RADIO1, IDC_RADIO1 + LFOPlugin::kNumWaveforms - 1, &LFOPluginEditor::OnWaveformChanged)
+ ON_CBN_SELCHANGE(IDC_COMBO1, &LFOPluginEditor::OnPlugParameterChanged)
+ ON_CBN_SELCHANGE(IDC_COMBO2, &LFOPluginEditor::OnOutputPlugChanged)
+ ON_CBN_SELCHANGE(IDC_COMBO3, &LFOPluginEditor::OnMidiCCChanged)
+ ON_COMMAND(IDC_RADIO7, &LFOPluginEditor::OnParameterChanged)
+ ON_COMMAND(IDC_RADIO8, &LFOPluginEditor::OnParameterChanged)
+ ON_EN_UPDATE(IDC_EDIT1, &LFOPluginEditor::OnParameterChanged)
+ ON_MESSAGE(LFOPlugin::WM_PARAM_UDPATE, &LFOPluginEditor::OnUpdateParam)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+void LFOPluginEditor::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(LFOPluginEditor)
+ DDX_Control(pDX, IDC_COMBO1, m_plugParam);
+ DDX_Control(pDX, IDC_COMBO2, m_outPlug);
+ DDX_Control(pDX, IDC_COMBO3, m_midiCC);
+ DDX_Control(pDX, IDC_SLIDER1, m_amplitudeSlider);
+ DDX_Control(pDX, IDC_SLIDER2, m_offsetSlider);
+ DDX_Control(pDX, IDC_SLIDER3, m_frequencySlider);
+ DDX_Control(pDX, IDC_EDIT1, m_midiChnEdit);
+ DDX_Control(pDX, IDC_SPIN1, m_midiChnSpin);
+ //}}AFX_DATA_MAP
+}
+
+
+LFOPluginEditor::LFOPluginEditor(LFOPlugin &plugin)
+ : CAbstractVstEditor(plugin)
+ , m_lfoPlugin(plugin)
+ , m_locked(true)
+{
+}
+
+
+bool LFOPluginEditor::OpenEditor(CWnd *parent)
+{
+ m_locked = true;
+ Create(IDD_LFOPLUGIN, parent);
+
+ m_midiChnSpin.SetRange32(1, 16);
+ m_midiCC.SetRedraw(FALSE);
+ CString s;
+ for(unsigned int i = 0; i < 128; i++)
+ {
+ s.Format(_T("%3u: "), i);
+ s += mpt::ToCString(mpt::Charset::UTF8, MIDIEvents::MidiCCNames[i]);
+ m_midiCC.AddString(s);
+ }
+ if(m_lfoPlugin.m_outputToCC && m_lfoPlugin.m_outputParam != LFOPlugin::INVALID_OUTPUT_PARAM)
+ {
+ m_midiCC.SetCurSel(m_lfoPlugin.m_outputParam & 0x7F);
+ SetDlgItemInt(IDC_EDIT1, 1 + ((m_lfoPlugin.m_outputParam & 0xF00) >> 8));
+ } else
+ {
+ SetDlgItemInt(IDC_EDIT1, 1);
+ }
+ m_midiCC.SetRedraw(TRUE);
+
+ UpdateView(PluginHint().Info().Names());
+
+ for(int32 i = 0; i < LFOPlugin::kLFONumParameters; i++)
+ {
+ UpdateParam(i);
+ }
+ UpdateParamDisplays();
+
+ m_locked = false;
+ // Avoid weird WM_COMMAND message (following a WM_ACTIVATE message) activating a wrong waveform when closing the plugin editor while the pattern editor is open
+ GetDlgItem(IDC_RADIO1 + m_lfoPlugin.m_waveForm)->SetFocus();
+ return CAbstractVstEditor::OpenEditor(parent);
+}
+
+
+void LFOPluginEditor::UpdateParamDisplays()
+{
+ CAbstractVstEditor::UpdateParamDisplays();
+ m_locked = true;
+ CheckRadioButton(IDC_RADIO7, IDC_RADIO8, m_lfoPlugin.m_outputToCC ? IDC_RADIO8 : IDC_RADIO7);
+ m_plugParam.SetRedraw(FALSE);
+ IMixPlugin *outPlug = m_lfoPlugin.GetOutputPlugin();
+ if(outPlug != nullptr)
+ {
+ if(LONG_PTR(outPlug) != GetWindowLongPtr(m_plugParam, GWLP_USERDATA))
+ {
+ m_plugParam.ResetContent();
+ AddPluginParameternamesToCombobox(m_plugParam, *outPlug);
+ if(!m_lfoPlugin.m_outputToCC)
+ m_plugParam.SetCurSel(m_lfoPlugin.m_outputParam);
+ SetWindowLongPtr(m_plugParam, GWLP_USERDATA, LONG_PTR(outPlug));
+ }
+ GetDlgItem(IDC_BUTTON1)->EnableWindow(outPlug ? TRUE :FALSE);
+ } else
+ {
+ m_plugParam.ResetContent();
+ SetWindowLongPtr(m_plugParam, GWLP_USERDATA, 0);
+ }
+ m_plugParam.SetRedraw(TRUE);
+ m_locked = false;
+}
+
+
+void LFOPluginEditor::UpdateParam(int32 p)
+{
+ LFOPlugin::Parameters param = static_cast<LFOPlugin::Parameters>(p);
+ CAbstractVstEditor::UpdateParam(p);
+ m_locked = true;
+ switch(param)
+ {
+ case LFOPlugin::kAmplitude:
+ InitSlider(m_amplitudeSlider, LFOPlugin::kAmplitude);
+ break;
+ case LFOPlugin::kOffset:
+ InitSlider(m_offsetSlider, LFOPlugin::kOffset);
+ break;
+ case LFOPlugin::kFrequency:
+ InitSlider(m_frequencySlider, LFOPlugin::kFrequency);
+ break;
+ case LFOPlugin::kTempoSync:
+ CheckDlgButton(IDC_CHECK2, m_lfoPlugin.m_tempoSync ? BST_CHECKED : BST_UNCHECKED);
+ InitSlider(m_frequencySlider, LFOPlugin::kFrequency);
+ break;
+ case LFOPlugin::kWaveform:
+ CheckRadioButton(IDC_RADIO1, IDC_RADIO1 + LFOPlugin::kNumWaveforms - 1, IDC_RADIO1 + m_lfoPlugin.m_waveForm);
+ break;
+ case LFOPlugin::kPolarity:
+ CheckDlgButton(IDC_CHECK1, m_lfoPlugin.m_polarity ? BST_CHECKED : BST_UNCHECKED);
+ break;
+ case LFOPlugin::kBypassed:
+ CheckDlgButton(IDC_CHECK3, m_lfoPlugin.m_bypassed ? BST_CHECKED : BST_UNCHECKED);
+ break;
+ case LFOPlugin::kLoopMode:
+ CheckDlgButton(IDC_CHECK4, m_lfoPlugin.m_oneshot ? BST_CHECKED : BST_UNCHECKED);
+ break;
+ default:
+ break;
+ }
+ m_locked = false;
+}
+
+
+void LFOPluginEditor::UpdateView(UpdateHint hint)
+{
+ CAbstractVstEditor::UpdateView(hint);
+ if(hint.GetType()[HINT_PLUGINNAMES | HINT_MIXPLUGINS])
+ {
+ PLUGINDEX hintPlug = hint.ToType<PluginHint>().GetPlugin();
+ if(hintPlug > 0 && hintPlug <= m_lfoPlugin.GetSlot())
+ {
+ return;
+ }
+
+ CString s;
+ IMixPlugin *outPlugin = m_lfoPlugin.GetOutputPlugin();
+ m_outPlug.SetRedraw(FALSE);
+ m_outPlug.ResetContent();
+ for(PLUGINDEX out = m_lfoPlugin.GetSlot() + 1; out < MAX_MIXPLUGINS; out++)
+ {
+ const SNDMIXPLUGIN &outPlug = m_lfoPlugin.GetSoundFile().m_MixPlugins[out];
+ if(outPlug.IsValidPlugin())
+ {
+ mpt::ustring libName = outPlug.GetLibraryName();
+ s.Format(_T("FX%d: "), out + 1);
+ s += mpt::ToCString(libName);
+ if(outPlug.GetName() != U_("") && libName != outPlug.GetName())
+ {
+ s += _T(" (");
+ s += mpt::ToCString(outPlug.GetName());
+ s += _T(")");
+ }
+
+ int n = m_outPlug.AddString(s);
+ m_outPlug.SetItemData(n, out);
+ if(outPlugin == outPlug.pMixPlugin)
+ {
+ m_outPlug.SetCurSel(n);
+ }
+ }
+ }
+ m_outPlug.SetRedraw(TRUE);
+ m_outPlug.Invalidate(FALSE);
+ m_plugParam.Invalidate(FALSE);
+ }
+}
+
+
+void LFOPluginEditor::InitSlider(CSliderCtrl &slider, LFOPlugin::Parameters param)
+{
+ slider.SetRange(0, SLIDER_GRANULARITY);
+ slider.SetTicFreq(SLIDER_GRANULARITY / 10);
+ SetSliderValue(slider, m_lfoPlugin.GetParameter(param));
+ SetSliderText(param);
+}
+
+
+void LFOPluginEditor::SetSliderText(LFOPlugin::Parameters param)
+{
+ CString s = m_lfoPlugin.GetParamName(param) + _T(": ") + m_lfoPlugin.GetFormattedParamValue(param);
+ SetDlgItemText(IDC_STATIC1 + param, s);
+}
+
+
+void LFOPluginEditor::SetSliderValue(CSliderCtrl &slider, float value)
+{
+ slider.SetPos(mpt::saturate_round<int>(value * SLIDER_GRANULARITY));
+}
+
+
+float LFOPluginEditor::GetSliderValue(CSliderCtrl &slider)
+{
+ float value = slider.GetPos() / static_cast<float>(SLIDER_GRANULARITY);
+ return value;
+}
+
+
+void LFOPluginEditor::OnHScroll(UINT nCode, UINT nPos, CScrollBar *pSB)
+{
+ CAbstractVstEditor::OnHScroll(nCode, nPos, pSB);
+ if(!m_locked && nCode != SB_ENDSCROLL && pSB != nullptr)
+ {
+ auto slider = reinterpret_cast<CSliderCtrl *>(pSB);
+ LFOPlugin::Parameters param;
+ if(slider == &m_amplitudeSlider)
+ param = LFOPlugin::kAmplitude;
+ else if(slider == &m_offsetSlider)
+ param = LFOPlugin::kOffset;
+ else if(slider == &m_frequencySlider)
+ param = LFOPlugin::kFrequency;
+ else
+ return;
+
+ float value = GetSliderValue(*slider);
+ m_lfoPlugin.SetParameter(param, value);
+ m_lfoPlugin.AutomateParameter(param);
+ SetSliderText(param);
+ }
+}
+
+
+void LFOPluginEditor::OnPolarityChanged()
+{
+ if(!m_locked)
+ {
+ m_lfoPlugin.m_polarity = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED;
+ m_lfoPlugin.AutomateParameter(LFOPlugin::kPolarity);
+ }
+}
+
+
+void LFOPluginEditor::OnTempoSyncChanged()
+{
+ if(!m_locked)
+ {
+ m_lfoPlugin.m_tempoSync = IsDlgButtonChecked(IDC_CHECK2) != BST_UNCHECKED;
+ m_lfoPlugin.RecalculateFrequency();
+ m_lfoPlugin.AutomateParameter(LFOPlugin::kTempoSync);
+ InitSlider(m_frequencySlider, LFOPlugin::kFrequency);
+ }
+}
+
+
+void LFOPluginEditor::OnBypassChanged()
+{
+ if(!m_locked)
+ {
+ m_lfoPlugin.m_bypassed = IsDlgButtonChecked(IDC_CHECK3) != BST_UNCHECKED;
+ m_lfoPlugin.AutomateParameter(LFOPlugin::kBypassed);
+ }
+}
+
+
+void LFOPluginEditor::OnLoopModeChanged()
+{
+ if(!m_locked)
+ {
+ m_lfoPlugin.m_oneshot = IsDlgButtonChecked(IDC_CHECK4) != BST_UNCHECKED;
+ m_lfoPlugin.AutomateParameter(LFOPlugin::kLoopMode);
+ }
+}
+
+
+void LFOPluginEditor::OnWaveformChanged(UINT nID)
+{
+ if(!m_locked)
+ {
+ m_lfoPlugin.m_waveForm = static_cast<LFOPlugin::LFOWaveform>(nID - IDC_RADIO1);
+ m_lfoPlugin.AutomateParameter(LFOPlugin::kWaveform);
+ }
+}
+
+
+void LFOPluginEditor::OnPlugParameterChanged()
+{
+ if(!m_locked)
+ {
+ CheckRadioButton(IDC_RADIO7, IDC_RADIO8, IDC_RADIO7);
+ OnParameterChanged();
+ }
+}
+
+
+void LFOPluginEditor::OnMidiCCChanged()
+{
+ if(!m_locked)
+ {
+ CheckRadioButton(IDC_RADIO7, IDC_RADIO8, IDC_RADIO8);
+ OnParameterChanged();
+ }
+}
+
+
+void LFOPluginEditor::OnParameterChanged()
+{
+ if(!m_locked)
+ {
+ m_locked = true;
+ PlugParamIndex param = 0;
+ bool outputToCC = IsDlgButtonChecked(IDC_RADIO8) != BST_UNCHECKED;
+ if(outputToCC)
+ {
+ param = ((Clamp(GetDlgItemInt(IDC_EDIT1), 1u, 16u) - 1) << 8);
+ if(m_lfoPlugin.m_outputToCC || m_midiCC.GetCurSel() >= 0)
+ param |= (m_midiCC.GetCurSel() & 0x7F);
+ } else
+ {
+ if(!m_lfoPlugin.m_outputToCC || m_plugParam.GetCurSel() >= 0)
+ param = static_cast<PlugParamIndex>(m_plugParam.GetItemData(m_plugParam.GetCurSel()));
+ }
+ m_lfoPlugin.m_outputToCC = outputToCC;
+ m_lfoPlugin.m_outputParam = param;
+ m_lfoPlugin.SetModified();
+ m_locked = false;
+ }
+}
+
+
+void LFOPluginEditor::OnOutputPlugChanged()
+{
+ if(!m_locked)
+ {
+ PLUGINDEX plug = static_cast<PLUGINDEX>(m_outPlug.GetItemData(m_outPlug.GetCurSel()));
+ if(plug > m_lfoPlugin.GetSlot())
+ {
+ m_lfoPlugin.GetSoundFile().m_MixPlugins[m_lfoPlugin.GetSlot()].SetOutputPlugin(plug);
+ m_lfoPlugin.SetModified();
+ UpdateParamDisplays();
+ }
+ }
+}
+
+
+void LFOPluginEditor::OnPluginEditor()
+{
+ std::vector<IMixPlugin *> plug;
+ if(m_lfoPlugin.GetOutputPlugList(plug) && plug.front() != nullptr)
+ {
+ plug.front()->ToggleEditor();
+ }
+}
+
+
+LRESULT LFOPluginEditor::OnUpdateParam(WPARAM wParam, LPARAM lParam)
+{
+ if(wParam == m_lfoPlugin.GetSlot())
+ {
+ UpdateParam(static_cast<int32>(lParam));
+ }
+ return 0;
+}
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.h b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.h
new file mode 100644
index 00000000..bb6fb2b5
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/LFOPluginEditor.h
@@ -0,0 +1,69 @@
+/*
+ * LFOPluginEditor.h
+ * -----------------
+ * Purpose: Editor interface for the LFO plugin.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (OpenMPT Devs)
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include "../AbstractVstEditor.h"
+#include "../../soundlib/plugins/LFOPlugin.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+struct UpdateHint;
+
+class LFOPluginEditor : public CAbstractVstEditor
+{
+protected:
+ CComboBox m_plugParam, m_outPlug, m_midiCC;
+ CSliderCtrl m_amplitudeSlider, m_offsetSlider, m_frequencySlider;
+ CEdit m_midiChnEdit;
+ CSpinButtonCtrl m_midiChnSpin;
+ LFOPlugin &m_lfoPlugin;
+ bool m_locked : 1;
+ static constexpr int SLIDER_GRANULARITY = 1000;
+
+public:
+
+ LFOPluginEditor(LFOPlugin &plugin);
+
+ bool OpenEditor(CWnd *parent) override;
+ bool IsResizable() const override { return false; }
+ bool SetSize(int, int) override { return false; }
+
+ void UpdateParamDisplays() override;
+ void UpdateParam(int32 param) override;
+ void UpdateView(UpdateHint hint) override;
+
+protected:
+ void DoDataExchange(CDataExchange* pDX) override;
+ void OnHScroll(UINT nCode, UINT nPos, CScrollBar *pSB);
+
+ void InitSlider(CSliderCtrl &slider, LFOPlugin::Parameters param);
+ void SetSliderText(LFOPlugin::Parameters param);
+ void SetSliderValue(CSliderCtrl &slider, float value);
+ float GetSliderValue(CSliderCtrl &slider);
+
+ void OnPolarityChanged();
+ void OnTempoSyncChanged();
+ void OnBypassChanged();
+ void OnLoopModeChanged();
+ void OnWaveformChanged(UINT nID);
+ void OnPlugParameterChanged();
+ void OnMidiCCChanged();
+ void OnParameterChanged();
+ void OnOutputPlugChanged();
+ void OnPluginEditor();
+ LRESULT OnUpdateParam(WPARAM wParam, LPARAM lParam);
+
+ DECLARE_MESSAGE_MAP()
+};
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.cpp
new file mode 100644
index 00000000..fd5900de
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.cpp
@@ -0,0 +1,547 @@
+/*
+ * MidiInOut.cpp
+ * -------------
+ * Purpose: A plugin for sending and receiving MIDI data.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (OpenMPT Devs)
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+#include "stdafx.h"
+#include "MidiInOut.h"
+#include "MidiInOutEditor.h"
+#include "../../common/FileReader.h"
+#include "../../soundlib/Sndfile.h"
+#include "../Reporting.h"
+#include <algorithm>
+#include <sstream>
+#ifdef MODPLUG_TRACKER
+#include "../Mptrack.h"
+#endif
+#include "mpt/io/io.hpp"
+#include "mpt/io/io_stdstream.hpp"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+IMixPlugin* MidiInOut::Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
+{
+ try
+ {
+ return new (std::nothrow) MidiInOut(factory, sndFile, mixStruct);
+ } catch(RtMidiError &)
+ {
+ return nullptr;
+ }
+}
+
+
+MidiInOut::MidiInOut(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct)
+ : IMidiPlugin(factory, sndFile, mixStruct)
+ , m_inputDevice(m_midiIn)
+ , m_outputDevice(m_midiOut)
+#ifdef MODPLUG_TRACKER
+ , m_programName(_T("Default"))
+#endif // MODPLUG_TRACKER
+{
+ m_mixBuffer.Initialize(2, 2);
+ InsertIntoFactoryList();
+}
+
+
+MidiInOut::~MidiInOut()
+{
+ MidiInOut::Suspend();
+}
+
+
+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());
+}
+
+
+void MidiInOut::SaveAllParameters()
+{
+ auto chunk = GetChunk(false);
+ if(chunk.empty())
+ return;
+
+ m_pMixStruct->defaultProgram = -1;
+ m_pMixStruct->pluginData.assign(chunk.begin(), chunk.end());
+}
+
+
+void MidiInOut::RestoreAllParameters(int32 program)
+{
+ IMixPlugin::RestoreAllParameters(program); // First plugin version didn't use chunks.
+ SetChunk(mpt::as_span(m_pMixStruct->pluginData), false);
+}
+
+
+enum ChunkFlags
+{
+ kLatencyCompensation = 0x01, // Implicit in current plugin version
+ kLatencyPresent = 0x02, // Latency value is present as double-precision float
+ kIgnoreTiming = 0x04, // Do not send timing and sequencing information
+ kFriendlyInputName = 0x08, // Preset also stores friendly name of input device
+ kFriendlyOutputName = 0x10, // Preset also stores friendly name of output device
+};
+
+IMixPlugin::ChunkData MidiInOut::GetChunk(bool /*isBank*/)
+{
+ const std::string programName8 = mpt::ToCharset(mpt::Charset::UTF8, m_programName);
+ uint32 flags = kLatencyCompensation | kLatencyPresent | (m_sendTimingInfo ? 0 : kIgnoreTiming);
+#ifdef MODPLUG_TRACKER
+ const std::string inFriendlyName = (m_inputDevice.index == MidiDevice::NO_MIDI_DEVICE) ? m_inputDevice.name : mpt::ToCharset(mpt::Charset::UTF8, theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, m_inputDevice.name), true, false));
+ const std::string outFriendlyName = (m_outputDevice.index == MidiDevice::NO_MIDI_DEVICE) ? m_outputDevice.name : mpt::ToCharset(mpt::Charset::UTF8, theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, m_outputDevice.name), false, false));
+ if(inFriendlyName != m_inputDevice.name)
+ {
+ flags |= kFriendlyInputName;
+ }
+ if(outFriendlyName != m_outputDevice.name)
+ {
+ flags |= kFriendlyOutputName;
+ }
+#endif
+
+ 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, 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()));
+ mpt::IO::WriteIntLE<uint32>(s, m_outputDevice.index);
+ mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(m_outputDevice.name.size()));
+ mpt::IO::WriteIntLE<uint32>(s, flags);
+ mpt::IO::WriteRaw(s, programName8.c_str(), programName8.size());
+ mpt::IO::WriteRaw(s, m_inputDevice.name.c_str(), m_inputDevice.name.size());
+ mpt::IO::WriteRaw(s, m_outputDevice.name.c_str(), m_outputDevice.name.size());
+ mpt::IO::WriteIntLE<uint64>(s, IEEE754binary64LE(m_latency).GetInt64());
+#ifdef MODPLUG_TRACKER
+ if(flags & kFriendlyInputName)
+ {
+ mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(inFriendlyName.size()));
+ mpt::IO::WriteRaw(s, inFriendlyName.c_str(), inFriendlyName.size());
+ }
+ if(flags & kFriendlyOutputName)
+ {
+ mpt::IO::WriteIntLE<uint32>(s, static_cast<uint32>(outFriendlyName.size()));
+ mpt::IO::WriteRaw(s, outFriendlyName.c_str(), outFriendlyName.size());
+ }
+#endif
+ m_chunkData = s.str();
+ return mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(m_chunkData));
+}
+
+
+// Try to match a port name against stored name or friendly name (preferred)
+static void FindPort(MidiDevice::ID &id, unsigned int numPorts, const std::string &name, const std::string &friendlyName, MidiDevice &midiDevice, bool isInput)
+{
+ bool foundFriendly = false;
+ for(unsigned int i = 0; i < numPorts; i++)
+ {
+ try
+ {
+ auto portName = midiDevice.GetPortName(i);
+ bool deviceNameMatches = (portName == name);
+#ifdef MODPLUG_TRACKER
+ if(!friendlyName.empty() && friendlyName == mpt::ToCharset(mpt::Charset::UTF8, theApp.GetFriendlyMIDIPortName(mpt::ToUnicode(mpt::Charset::UTF8, portName), isInput, false)))
+ {
+ // Preferred match
+ id = i;
+ foundFriendly = true;
+ if(deviceNameMatches)
+ {
+ return;
+ }
+ }
+#else
+ MPT_UNREFERENCED_PARAMETER(friendlyName)
+#endif
+ if(deviceNameMatches && !foundFriendly)
+ {
+ id = i;
+ }
+ } catch(const RtMidiError &)
+ {
+ }
+ }
+}
+
+
+void MidiInOut::SetChunk(const ChunkData &chunk, bool /*isBank*/)
+{
+ FileReader file(chunk);
+ if(!file.CanRead(9 * sizeof(uint32))
+ || !file.ReadMagic("fEvN") // VST program chunk magic
+ || file.ReadInt32LE() > GetVersion() // Plugin version
+ || file.ReadUint32LE() < 1) // Number of programs
+ return;
+
+ uint32 nameStrSize = file.ReadUint32LE();
+ MidiDevice::ID inID = file.ReadUint32LE();
+ uint32 inStrSize = file.ReadUint32LE();
+ MidiDevice::ID outID = file.ReadUint32LE();
+ uint32 outStrSize = file.ReadUint32LE();
+ uint32 flags = file.ReadUint32LE();
+
+ std::string progName, inName, outName, inFriendlyName, outFriendlyName;
+ file.ReadString<mpt::String::maybeNullTerminated>(progName, nameStrSize);
+ m_programName = mpt::ToCString(mpt::Charset::UTF8, progName);
+
+ file.ReadString<mpt::String::maybeNullTerminated>(inName, inStrSize);
+ file.ReadString<mpt::String::maybeNullTerminated>(outName, outStrSize);
+
+ if(flags & kLatencyPresent)
+ m_latency = file.ReadDoubleLE();
+ else
+ m_latency = 0.0f;
+ m_sendTimingInfo = !(flags & kIgnoreTiming);
+
+ if(flags & kFriendlyInputName)
+ file.ReadString<mpt::String::maybeNullTerminated>(inFriendlyName, file.ReadUint32LE());
+ if(flags & kFriendlyOutputName)
+ file.ReadString<mpt::String::maybeNullTerminated>(outFriendlyName, file.ReadUint32LE());
+
+ // Try to match an input port name against stored name or friendly name (preferred)
+ FindPort(inID, m_midiIn.getPortCount(), inName, inFriendlyName, m_inputDevice, true);
+ FindPort(outID, m_midiOut.getPortCount(), outName, outFriendlyName, m_outputDevice, false);
+
+ SetParameter(MidiInOut::kInputParameter, DeviceIDToParameter(inID));
+ SetParameter(MidiInOut::kOutputParameter, DeviceIDToParameter(outID));
+}
+
+
+void MidiInOut::SetParameter(PlugParamIndex index, PlugParamValue value)
+{
+ value = mpt::safe_clamp(value, 0.0f, 1.0f);
+ MidiDevice::ID newDevice = ParameterToDeviceID(value);
+ OpenDevice(newDevice, (index == kInputParameter));
+
+ // Update selection in editor
+ MidiInOutEditor *editor = dynamic_cast<MidiInOutEditor *>(GetEditor());
+ if(editor != nullptr)
+ editor->SetCurrentDevice((index == kInputParameter), newDevice);
+}
+
+
+float MidiInOut::GetParameter(PlugParamIndex index)
+{
+ const MidiDevice &device = (index == kInputParameter) ? m_inputDevice : m_outputDevice;
+ return DeviceIDToParameter(device.index);
+}
+
+
+#ifdef MODPLUG_TRACKER
+
+CString MidiInOut::GetParamName(PlugParamIndex param)
+{
+ if(param == kInputParameter)
+ return _T("MIDI In");
+ else
+ return _T("MIDI Out");
+}
+
+
+// Parameter value as text
+CString MidiInOut::GetParamDisplay(PlugParamIndex param)
+{
+ const MidiDevice &device = (param == kInputParameter) ? m_inputDevice : m_outputDevice;
+ return mpt::ToCString(mpt::Charset::UTF8, device.name);
+}
+
+
+CAbstractVstEditor *MidiInOut::OpenEditor()
+{
+ try
+ {
+ return new MidiInOutEditor(*this);
+ } catch(mpt::out_of_memory e)
+ {
+ mpt::delete_out_of_memory(e);
+ return nullptr;
+ }
+}
+
+#endif // MODPLUG_TRACKER
+
+
+// Processing (we don't process any audio, only MIDI messages)
+void MidiInOut::Process(float *, float *, uint32 numFrames)
+{
+ if(m_midiOut.isPortOpen())
+ {
+ mpt::lock_guard<mpt::mutex> lock(m_mutex);
+
+ // Send MIDI clock
+ if(m_nextClock < 1)
+ {
+ if(m_sendTimingInfo)
+ {
+ m_outQueue.push_back(Message(GetOutputTimestamp(), 0xF8));
+ }
+
+ double bpm = m_SndFile.GetCurrentBPM();
+ if(bpm > 0.0)
+ {
+ m_nextClock += 2.5 * m_SndFile.GetSampleRate() / bpm;
+ }
+ }
+ m_nextClock -= numFrames;
+
+ double now = m_clock.Now() * (1.0 / 1000.0);
+ auto message = m_outQueue.begin();
+ while(message != m_outQueue.end() && message->m_time <= now)
+ {
+ try
+ {
+ m_midiOut.sendMessage(message->m_message, message->m_size);
+ } catch(const RtMidiError &)
+ {
+ }
+ message++;
+ }
+ m_outQueue.erase(m_outQueue.begin(), message);
+ }
+}
+
+
+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,
+ // because otherwise we might read garbage if we toggle bypass status in the middle of a SysEx message.
+ bool isBypassed = IsBypassed();
+ if(message.empty())
+ {
+ return;
+ } else if(!m_bufferedInput.empty())
+ {
+ // SysEx message (continued)
+ m_bufferedInput.insert(m_bufferedInput.end(), message.begin(), message.end());
+ if(message.back() == 0xF7)
+ {
+ // End of message found!
+ if(!isBypassed)
+ ReceiveSysex(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(m_bufferedInput)));
+ m_bufferedInput.clear();
+ }
+ } else if(message.front() == 0xF0)
+ {
+ // Start of SysEx message...
+ if(message.back() != 0xF7)
+ m_bufferedInput.insert(m_bufferedInput.end(), message.begin(), message.end()); // ...but not the end!
+ else if(!isBypassed)
+ ReceiveSysex(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(message)));
+ } else if(!isBypassed)
+ {
+ // Regular message
+ uint32 msg = 0;
+ memcpy(&msg, message.data(), std::min(message.size(), sizeof(msg)));
+ ReceiveMidi(msg);
+ }
+}
+
+
+// Resume playback
+void MidiInOut::Resume()
+{
+ // Resume MIDI I/O
+ m_isResumed = true;
+ m_nextClock = 0;
+ m_outQueue.clear();
+ m_clock.SetResolution(1);
+ OpenDevice(m_inputDevice.index, true);
+ OpenDevice(m_outputDevice.index, false);
+ if(m_midiOut.isPortOpen() && m_sendTimingInfo)
+ {
+ MidiSend(0xFA); // Start
+ }
+}
+
+
+// Stop playback
+void MidiInOut::Suspend()
+{
+ // Suspend MIDI I/O
+ if(m_midiOut.isPortOpen() && m_sendTimingInfo)
+ {
+ try
+ {
+ unsigned char message[1] = { 0xFC }; // Stop
+ m_midiOut.sendMessage(message, 1);
+ } catch(const RtMidiError &)
+ {
+ }
+ }
+ //CloseDevice(inputDevice);
+ CloseDevice(m_outputDevice);
+ m_clock.SetResolution(0);
+ m_isResumed = false;
+}
+
+
+// Playback discontinuity
+void MidiInOut::PositionChanged()
+{
+ if(m_sendTimingInfo)
+ {
+ MidiSend(0xFC); // Stop
+ MidiSend(0xFA); // Start
+ }
+}
+
+
+void MidiInOut::Bypass(bool bypass)
+{
+ if(bypass)
+ {
+ mpt::lock_guard<mpt::mutex> lock(m_mutex);
+ m_outQueue.clear();
+ }
+ IMidiPlugin::Bypass(bypass);
+}
+
+
+bool MidiInOut::MidiSend(uint32 midiCode)
+{
+ if(!m_midiOut.isPortOpen() || IsBypassed())
+ {
+ // We need an output device to send MIDI messages to.
+ return true;
+ }
+
+ mpt::lock_guard<mpt::mutex> lock(m_mutex);
+ m_outQueue.push_back(Message(GetOutputTimestamp(), &midiCode, 3));
+ return true;
+}
+
+
+bool MidiInOut::MidiSysexSend(mpt::const_byte_span sysex)
+{
+ if(!m_midiOut.isPortOpen() || IsBypassed())
+ {
+ // We need an output device to send MIDI messages to.
+ return true;
+ }
+
+ mpt::lock_guard<mpt::mutex> lock(m_mutex);
+ m_outQueue.push_back(Message(GetOutputTimestamp(), sysex.data(), sysex.size()));
+ return true;
+}
+
+
+void MidiInOut::HardAllNotesOff()
+{
+ const bool wasSuspended = !IsResumed();
+ if(wasSuspended)
+ {
+ Resume();
+ }
+
+ for(uint8 mc = 0; mc < std::size(m_MidiCh); mc++) //all midi chans
+ {
+ PlugInstrChannel &channel = m_MidiCh[mc];
+ channel.ResetProgram();
+
+ SendMidiPitchBend(mc, EncodePitchBendParam(MIDIEvents::pitchBendCentre)); // centre pitch bend
+ MidiSend(MIDIEvents::CC(MIDIEvents::MIDICC_AllSoundOff, mc, 0)); // all sounds off
+
+ for(size_t i = 0; i < std::size(channel.noteOnMap); i++)
+ {
+ for(auto &c : channel.noteOnMap[i])
+ {
+ while(c != 0)
+ {
+ MidiSend(MIDIEvents::NoteOff(mc, static_cast<uint8>(i), 0));
+ c--;
+ }
+ }
+ }
+ }
+
+ if(wasSuspended)
+ {
+ Suspend();
+ }
+}
+
+
+// Open a device for input or output.
+void MidiInOut::OpenDevice(MidiDevice::ID newDevice, bool asInputDevice)
+{
+ MidiDevice &device = asInputDevice ? m_inputDevice : m_outputDevice;
+
+ if(device.index == newDevice && device.stream.isPortOpen())
+ {
+ // No need to re-open this device.
+ return;
+ }
+
+ CloseDevice(device);
+
+ device.index = newDevice;
+ device.stream.closePort();
+
+ if(device.index == kNoDevice)
+ {
+ // Dummy device
+ device.name = "<none>";
+ return;
+ }
+
+ device.name = device.GetPortName(newDevice);
+ //if(m_isResumed)
+ {
+ mpt::lock_guard<mpt::mutex> lock(m_mutex);
+
+ try
+ {
+ device.stream.openPort(newDevice);
+ if(asInputDevice)
+ {
+ m_midiIn.setCallback(InputCallback, this);
+ m_midiIn.ignoreTypes(false, true, true);
+ }
+ } catch(RtMidiError &error)
+ {
+ device.name = "Unavailable";
+ MidiInOutEditor *editor = dynamic_cast<MidiInOutEditor *>(GetEditor());
+ if(editor != nullptr)
+ {
+ Reporting::Error("MIDI device cannot be opened. Is it open in another application?\n\n" + error.getMessage(), "MIDI Input / Output", editor);
+ }
+ }
+ }
+}
+
+
+// Close an active device.
+void MidiInOut::CloseDevice(MidiDevice &device)
+{
+ if(device.stream.isPortOpen())
+ {
+ mpt::lock_guard<mpt::mutex> lock(m_mutex);
+ device.stream.closePort();
+ }
+}
+
+
+// Calculate the current output timestamp
+double MidiInOut::GetOutputTimestamp() const
+{
+ return m_clock.Now() * (1.0 / 1000.0) + GetOutputLatency() + m_latency;
+}
+
+
+// Get a device name
+std::string MidiDevice::GetPortName(MidiDevice::ID port)
+{
+ return stream.getPortName(port);
+}
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.h b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.h
new file mode 100644
index 00000000..cf8aa86c
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOut.h
@@ -0,0 +1,232 @@
+/*
+ * MidiInOut.h
+ * -----------
+ * Purpose: A plugin for sending and receiving MIDI data.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (OpenMPT Devs)
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include "mpt/mutex/mutex.hpp"
+#include "../../common/mptTime.h"
+#include "../../soundlib/plugins/PlugInterface.h"
+#include <rtmidi/RtMidi.h>
+#include <array>
+#include <deque>
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+class MidiDevice
+{
+public:
+ using ID = decltype(RtMidiIn().getPortCount());
+ static constexpr ID NO_MIDI_DEVICE = ID(-1);
+
+ RtMidi &stream;
+ std::string name; // Charset::UTF8
+ ID index = NO_MIDI_DEVICE;
+
+public:
+ MidiDevice(RtMidi &stream)
+ : stream(stream)
+ , name("<none>")
+ { }
+
+ std::string GetPortName(ID port); // Charset::UTF8
+};
+
+
+class MidiInOut final : public IMidiPlugin
+{
+ friend class MidiInOutEditor;
+
+protected:
+ enum
+ {
+ kInputParameter = 0,
+ kOutputParameter = 1,
+
+ kNumPrograms = 1,
+ kNumParams = 2,
+
+ kNoDevice = MidiDevice::NO_MIDI_DEVICE,
+ kMaxDevices = 65536, // Should be a power of 2 to avoid rounding errors.
+ };
+
+ // MIDI queue entry with small storage optimiziation.
+ // This optimiziation is going to be used for all messages that OpenMPT can send internally,
+ // but SysEx messages received from other plugins may be longer.
+ class Message
+ {
+ public:
+ double m_time;
+ size_t m_size;
+ unsigned char *m_message = nullptr;
+ protected:
+ std::array<unsigned char, 32> m_msgSmall;
+
+ public:
+ Message(double time, const void *data, size_t size)
+ : m_time(time)
+ , m_size(size)
+ {
+ if(size > m_msgSmall.size())
+ m_message = new unsigned char[size];
+ else
+ m_message = m_msgSmall.data();
+ std::memcpy(m_message, data, size);
+ }
+
+ Message(const Message &) = delete;
+ Message & operator=(const Message &) = delete;
+
+ Message(double time, unsigned char msg) noexcept : Message(time, &msg, 1) { }
+
+ Message(Message &&other) noexcept
+ : m_time(other.m_time)
+ , m_size(other.m_size)
+ , m_message(other.m_message)
+ , m_msgSmall(other.m_msgSmall)
+ {
+ other.m_message = nullptr;
+ if(m_size <= m_msgSmall.size())
+ m_message = m_msgSmall.data();
+
+ }
+
+ ~Message()
+ {
+ if(m_size > m_msgSmall.size())
+ delete[] m_message;
+ }
+
+ Message& operator= (Message &&other) noexcept
+ {
+ m_time = other.m_time;
+ m_size = other.m_size;
+ m_message = (m_size <= m_msgSmall.size()) ? m_msgSmall.data() : other.m_message;
+ m_msgSmall = other.m_msgSmall;
+ other.m_message = nullptr;
+ return *this;
+ }
+ };
+
+ std::string m_chunkData; // Storage for GetChunk
+ std::deque<Message> m_outQueue; // Latency-compensated output
+ std::vector<unsigned char> m_bufferedInput; // For receiving long SysEx messages
+ mpt::mutex m_mutex;
+ double m_nextClock = 0.0; // Remaining samples until next MIDI clock tick should be sent
+ double m_latency = 0.0; // User-adjusted latency in seconds
+
+ // I/O device settings
+ Util::MultimediaClock m_clock;
+ RtMidiIn m_midiIn;
+ RtMidiOut m_midiOut;
+ MidiDevice m_inputDevice;
+ MidiDevice m_outputDevice;
+ bool m_sendTimingInfo = true;
+
+#ifdef MODPLUG_TRACKER
+ CString m_programName;
+#endif
+
+public:
+ static IMixPlugin* Create(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
+ MidiInOut(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
+ ~MidiInOut();
+
+ // Translate a VST parameter to an RtMidi device ID
+ static MidiDevice::ID ParameterToDeviceID(float value)
+ {
+ return static_cast<MidiDevice::ID>(value * static_cast<float>(kMaxDevices)) - 1;
+ }
+
+ // Translate a RtMidi device ID to a VST parameter
+ static float DeviceIDToParameter(MidiDevice::ID index)
+ {
+ return static_cast<float>(index + 1) / static_cast<float>(kMaxDevices);
+ }
+
+ /////////////////////////////////////////////////
+ // Destroy the plugin
+ void Release() final { delete this; }
+ int32 GetUID() const final { return 'MMID'; }
+ int32 GetVersion() const final { return 2; }
+ void Idle() final { }
+ uint32 GetLatency() const final;
+
+ int32 GetNumPrograms() const final { return kNumPrograms; }
+ int32 GetCurrentProgram() final { return 0; }
+ void SetCurrentProgram(int32) final { }
+
+ PlugParamIndex GetNumParameters() const final { return kNumParams; }
+ void SetParameter(PlugParamIndex paramindex, PlugParamValue paramvalue) final;
+ PlugParamValue GetParameter(PlugParamIndex nIndex) final;
+
+ // Save parameters for storing them in a module file
+ void SaveAllParameters() final;
+ // Restore parameters from module file
+ void RestoreAllParameters(int32 program) final;
+ void Process(float *pOutL, float *pOutR, uint32 numFrames) final;
+ // Render silence and return the highest resulting output level
+ float RenderSilence(uint32) final{ return 0; }
+ bool MidiSend(uint32 midiCode) final;
+ bool MidiSysexSend(mpt::const_byte_span sysex) final;
+ void HardAllNotesOff() final;
+ // Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically.
+ void Resume() final;
+ void Suspend() final;
+ // Tell the plugin that there is a discontinuity between the previous and next render call (e.g. aftert jumping around in the module)
+ void PositionChanged() final;
+ void Bypass(bool bypass = true) final;
+ bool IsInstrument() const final { return true; }
+ bool CanRecieveMidiEvents() final { return true; }
+ // If false is returned, mixing this plugin can be skipped if its input are currently completely silent.
+ bool ShouldProcessSilence() final { return true; }
+
+#ifdef MODPLUG_TRACKER
+ CString GetDefaultEffectName() final { return _T("MIDI Input / Output"); }
+
+ CString GetParamName(PlugParamIndex param) final;
+ CString GetParamLabel(PlugParamIndex) final{ return CString(); }
+ CString GetParamDisplay(PlugParamIndex param) final;
+ CString GetCurrentProgramName() final { return m_programName; }
+ void SetCurrentProgramName(const CString &name) final { m_programName = name; }
+ CString GetProgramName(int32) final { return m_programName; }
+ virtual CString GetPluginVendor() { return _T("OpenMPT Project"); }
+
+ bool HasEditor() const final { return true; }
+protected:
+ CAbstractVstEditor *OpenEditor() final;
+#endif
+
+public:
+ int GetNumInputChannels() const final { return 0; }
+ int GetNumOutputChannels() const final { return 0; }
+
+ bool ProgramsAreChunks() const final { return true; }
+ ChunkData GetChunk(bool isBank) final;
+ void SetChunk(const ChunkData &chunk, bool isBank) final;
+
+protected:
+ // Open a device for input or output.
+ void OpenDevice(MidiDevice::ID newDevice, bool asInputDevice);
+ // Close an active device.
+ void CloseDevice(MidiDevice &device);
+
+ static void InputCallback(double deltatime, std::vector<unsigned char> *message, void *userData) { static_cast<MidiInOut *>(userData)->InputCallback(deltatime, *message); }
+ void InputCallback(double deltatime, std::vector<unsigned char> &message);
+
+ // Calculate the current output timestamp
+ double GetOutputTimestamp() const;
+};
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.cpp
new file mode 100644
index 00000000..858050f8
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.cpp
@@ -0,0 +1,161 @@
+/*
+ * MidiInOutEditor.cpp
+ * -------------------
+ * Purpose: Editor interface for the MidiInOut plugin.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (OpenMPT Devs)
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+#include "stdafx.h"
+
+#ifdef MODPLUG_TRACKER
+#include "MidiInOut.h"
+#include "MidiInOutEditor.h"
+#include "../Mptrack.h"
+#include "../resource.h"
+#include <rtmidi/RtMidi.h>
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+BEGIN_MESSAGE_MAP(MidiInOutEditor, CAbstractVstEditor)
+ //{{AFX_MSG_MAP(MidiInOutEditor)
+ ON_CBN_SELCHANGE(IDC_COMBO1, &MidiInOutEditor::OnInputChanged)
+ ON_CBN_SELCHANGE(IDC_COMBO2, &MidiInOutEditor::OnOutputChanged)
+ ON_EN_CHANGE(IDC_EDIT1, &MidiInOutEditor::OnLatencyChanged)
+ ON_COMMAND(IDC_CHECK1, &MidiInOutEditor::OnTimingMessagesChanged)
+ //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+
+void MidiInOutEditor::DoDataExchange(CDataExchange* pDX)
+{
+ CDialog::DoDataExchange(pDX);
+ //{{AFX_DATA_MAP(MidiInOutEditor)
+ DDX_Control(pDX, IDC_COMBO1, m_inputCombo);
+ DDX_Control(pDX, IDC_COMBO2, m_outputCombo);
+ DDX_Control(pDX, IDC_EDIT1, m_latencyEdit);
+ DDX_Control(pDX, IDC_SPIN1, m_latencySpin);
+ //}}AFX_DATA_MAP
+}
+
+
+MidiInOutEditor::MidiInOutEditor(MidiInOut &plugin)
+ : CAbstractVstEditor(plugin)
+{
+}
+
+
+bool MidiInOutEditor::OpenEditor(CWnd *parent)
+{
+ Create(IDD_MIDI_IO_PLUGIN, parent);
+ MidiInOut &plugin = static_cast<MidiInOut &>(m_VstPlugin);
+ m_latencyEdit.AllowFractions(true);
+ m_latencyEdit.AllowNegative(true);
+ m_latencyEdit.SetDecimalValue(plugin.m_latency * 1000.0, 4);
+ 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);
+ CheckDlgButton(IDC_CHECK1, plugin.m_sendTimingInfo ? BST_CHECKED : BST_UNCHECKED);
+ m_locked = false;
+ return CAbstractVstEditor::OpenEditor(parent);
+}
+
+
+// Update lists of available input / output devices
+void MidiInOutEditor::PopulateList(CComboBox &combo, RtMidi &rtDevice, MidiDevice &midiDevice, bool isInput)
+{
+ combo.SetRedraw(FALSE);
+ combo.ResetContent();
+
+ // Add dummy device
+ combo.SetItemData(combo.AddString(_T("<none>")), static_cast<DWORD_PTR>(MidiInOut::kNoDevice));
+
+ // Go through all RtMidi devices
+ auto ports = rtDevice.getPortCount();
+ int selectedItem = 0;
+ CString portName;
+ for(unsigned int i = 0; i < ports; i++)
+ {
+ try
+ {
+ portName = theApp.GetFriendlyMIDIPortName(mpt::ToCString(mpt::Charset::UTF8, midiDevice.GetPortName(i)), isInput);
+ int result = combo.AddString(portName);
+ combo.SetItemData(result, i);
+
+ if(result != CB_ERR && i == midiDevice.index)
+ selectedItem = result;
+ } catch(RtMidiError &)
+ {
+ }
+ }
+
+ combo.SetCurSel(selectedItem);
+ combo.SetRedraw(TRUE);
+}
+
+
+// Refresh current input / output device in GUI
+void MidiInOutEditor::SetCurrentDevice(CComboBox &combo, MidiDevice::ID device)
+{
+ int items = combo.GetCount();
+ for(int i = 0; i < items; i++)
+ {
+ if(static_cast<MidiDevice::ID>(combo.GetItemData(i)) == device)
+ {
+ combo.SetCurSel(i);
+ break;
+ }
+ }
+}
+
+
+static void IOChanged(MidiInOut &plugin, CComboBox &combo, PlugParamIndex param)
+{
+ // Update device ID and notify plugin.
+ MidiDevice::ID newDevice = static_cast<MidiDevice::ID>(combo.GetItemData(combo.GetCurSel()));
+ plugin.SetParameter(param, MidiInOut::DeviceIDToParameter(newDevice));
+ plugin.AutomateParameter(param);
+}
+
+
+void MidiInOutEditor::OnInputChanged()
+{
+ IOChanged(static_cast<MidiInOut &>(m_VstPlugin), m_inputCombo, MidiInOut::kInputParameter);
+}
+
+
+void MidiInOutEditor::OnOutputChanged()
+{
+ IOChanged(static_cast<MidiInOut &>(m_VstPlugin), m_outputCombo, MidiInOut::kOutputParameter);
+}
+
+
+void MidiInOutEditor::OnLatencyChanged()
+{
+ MidiInOut &plugin = static_cast<MidiInOut &>(m_VstPlugin);
+ double latency = 0.0;
+ if(!m_locked && m_latencyEdit.GetDecimalValue(latency))
+ {
+ plugin.m_latency = latency * (1.0 / 1000.0);
+ plugin.SetModified();
+ }
+}
+
+
+void MidiInOutEditor::OnTimingMessagesChanged()
+{
+ if(!m_locked)
+ {
+ MidiInOut &plugin = static_cast<MidiInOut &>(m_VstPlugin);
+ plugin.m_sendTimingInfo = IsDlgButtonChecked(IDC_CHECK1) != BST_UNCHECKED;
+ plugin.SetModified();
+ }
+}
+
+
+OPENMPT_NAMESPACE_END
+
+#endif // MODPLUG_TRACKER
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.h b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.h
new file mode 100644
index 00000000..1ac7cdf9
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/MidiInOutEditor.h
@@ -0,0 +1,66 @@
+/*
+ * MidiInOutEditor.h
+ * -----------------
+ * Purpose: Editor interface for the MidiInOut plugin.
+ * Notes : (currently none)
+ * Authors: Johannes Schultz (OpenMPT Devs)
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#ifdef MODPLUG_TRACKER
+
+#include "../AbstractVstEditor.h"
+#include "../CDecimalSupport.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+class MidiInOut;
+
+class MidiInOutEditor : public CAbstractVstEditor
+{
+protected:
+ CComboBox m_inputCombo, m_outputCombo;
+ CNumberEdit m_latencyEdit;
+ CSpinButtonCtrl m_latencySpin;
+ bool m_locked = true;
+
+public:
+
+ MidiInOutEditor(MidiInOut &plugin);
+
+ // Refresh current input / output device in GUI
+ void SetCurrentDevice(bool asInputDevice, MidiDevice::ID device)
+ {
+ CComboBox &combo = asInputDevice ? m_inputCombo : m_outputCombo;
+ SetCurrentDevice(combo, device);
+ }
+
+ bool OpenEditor(CWnd *parent) override;
+ bool IsResizable() const override { return false; }
+ bool SetSize(int, int) override { return false; }
+
+protected:
+
+ // Update lists of available input / output devices
+ static void PopulateList(CComboBox &combo, RtMidi &rtDevice, MidiDevice &midiDevice, bool isInput);
+ // Refresh current input / output device in GUI
+ void SetCurrentDevice(CComboBox &combo, MidiDevice::ID device);
+
+ void DoDataExchange(CDataExchange* pDX) override;
+
+ afx_msg void OnInputChanged();
+ afx_msg void OnOutputChanged();
+ afx_msg void OnLatencyChanged();
+ afx_msg void OnTimingMessagesChanged();
+
+ DECLARE_MESSAGE_MAP()
+};
+
+OPENMPT_NAMESPACE_END
+
+#endif // MODPLUG_TRACKER
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstDefinitions.h b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstDefinitions.h
new file mode 100644
index 00000000..974672d1
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstDefinitions.h
@@ -0,0 +1,925 @@
+/*
+ * VstDefinitions.h
+ * ----------------
+ * Purpose: Definition of all VST-related constants, function prototypes and structures.
+ * Notes : Based on BeRo's independent VST header, a clean-room implementation based
+ * on several third-party information sources.
+ * The original header, licensed under the Zlib license, can be found at
+ * https://github.com/BeRo1985/br808/blob/master/VSTi/VST/VST.pas
+ * Authors: OpenMPT Devs
+ * Benjamin "BeRo" Rosseaux
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#if MPT_OS_WINDOWS
+#define VSTCALLBACK __cdecl
+#else
+#define VSTCALLBACK
+#endif
+
+namespace Vst
+{
+
+inline constexpr int32 kVstVersion = 2400;
+
+enum VstStringLengths
+{
+ kVstMaxProgNameLen = 24,
+ kVstMaxParamStrLen = 8,
+ kVstMaxVendorStrLen = 64,
+ kVstMaxProductStrLen = 64,
+ kVstMaxEffectNameLen = 32,
+
+ kVstMaxNameLen = 64,
+ kVstMaxLabelLen = 64,
+ kVstMaxShortLabelLen = 8,
+ kVstMaxCategLabelLen = 24,
+ kVstMaxFileNameLen = 100,
+};
+
+enum VstFlags : int32
+{
+ effFlagsHasEditor = 1 << 0,
+ effFlagsHasClip = 1 << 1,
+ effFlagsHasVu = 1 << 2,
+ effFlagsCanMono = 1 << 3,
+ effFlagsCanReplacing = 1 << 4,
+ effFlagsProgramChunks = 1 << 5,
+ effFlagsIsSynth = 1 << 8,
+ effFlagsNoSoundInStop = 1 << 9,
+ effFlagsExtIsAsync = 1 << 10,
+ effFlagsExtHasBuffer = 1 << 11,
+ effFlagsCanDoubleReplacing = 1 << 12,
+};
+
+enum VstOpcodeToPlugin : int32
+{
+ effOpen = 0,
+ effClose = 1,
+ effSetProgram = 2,
+ effGetProgram = 3,
+ effSetProgramName = 4,
+ effGetProgramName = 5,
+ effGetParamLabel = 6,
+ effGetParamDisplay = 7,
+ effGetParamName = 8,
+ effGetVu = 9,
+ effSetSampleRate = 10,
+ effSetBlockSize = 11,
+ effMainsChanged = 12,
+ effEditGetRect = 13,
+ effEditOpen = 14,
+ effEditClose = 15,
+ effEditDraw = 16,
+ effEditMouse = 17,
+ effEditKey = 18,
+ effEditIdle = 19,
+ effEditTop = 20,
+ effEditSleep = 21,
+ effIdentify = 22,
+ effGetChunk = 23,
+ effSetChunk = 24,
+ effProcessEvents = 25,
+ effCanBeAutomated = 26,
+ effString2Parameter = 27,
+ effGetNumProgramCategories = 28,
+ effGetProgramNameIndexed = 29,
+ effCopyProgram = 30,
+ effConnectInput = 31,
+ effConnectOutput = 32,
+ effGetInputProperties = 33,
+ effGetOutputProperties = 34,
+ effGetPlugCategory = 35,
+ effGetCurrentPosition = 36,
+ effGetDestinationBuffer = 37,
+ effOfflineNotify = 38,
+ effOfflinePrepare = 39,
+ effOfflineRun = 40,
+ effProcessVarIo = 41,
+ effSetSpeakerArrangement = 42,
+ effSetBlockSizeAndSampleRate = 43,
+ effSetBypass = 44,
+ effGetEffectName = 45,
+ effGetErrorText = 46,
+ effGetVendorString = 47,
+ effGetProductString = 48,
+ effGetVendorVersion = 49,
+ effVendorSpecific = 50,
+ effCanDo = 51,
+ effGetTailSize = 52,
+ effIdle = 53,
+ effGetIcon = 54,
+ effSetViewPosition = 55,
+ effGetParameterProperties = 56,
+ effKeysRequired = 57,
+ effGetVstVersion = 58,
+ effEditKeyDown = 59,
+ effEditKeyUp = 60,
+ effSetEditKnobMode = 61,
+ effGetMidiProgramName = 62,
+ effGetCurrentMidiProgram = 63,
+ effGetMidiProgramCategory = 64,
+ effHasMidiProgramsChanged = 65,
+ effGetMidiKeyName = 66,
+ effBeginSetProgram = 67,
+ effEndSetProgram = 68,
+ effGetSpeakerArrangement = 69,
+ effShellGetNextPlugin = 70,
+ effStartProcess = 71,
+ effStopProcess = 72,
+ effSetTotalSampleToProcess = 73,
+ effSetPanLaw = 74,
+ effBeginLoadBank = 75,
+ effBeginLoadProgram = 76,
+ effSetProcessPrecision = 77,
+ effGetNumMidiInputChannels = 78,
+ effGetNumMidiOutputChannels = 79,
+};
+
+enum VstOpcodeToHost : int32
+{
+ audioMasterAutomate = 0,
+ audioMasterVersion = 1,
+ audioMasterCurrentId = 2,
+ audioMasterIdle = 3,
+ audioMasterPinConnected = 4,
+ audioMasterWantMidi = 6,
+ audioMasterGetTime = 7,
+ audioMasterProcessEvents = 8,
+ audioMasterSetTime = 9,
+ audioMasterTempoAt = 10,
+ audioMasterGetNumAutomatableParameters = 11,
+ audioMasterGetParameterQuantization = 12,
+ audioMasterIOChanged = 13,
+ audioMasterNeedIdle = 14,
+ audioMasterSizeWindow = 15,
+ audioMasterGetSampleRate = 16,
+ audioMasterGetBlockSize = 17,
+ audioMasterGetInputLatency = 18,
+ audioMasterGetOutputLatency = 19,
+ audioMasterGetPreviousPlug = 20,
+ audioMasterGetNextPlug = 21,
+ audioMasterWillReplaceOrAccumulate = 22,
+ audioMasterGetCurrentProcessLevel = 23,
+ audioMasterGetAutomationState = 24,
+ audioMasterOfflineStart = 25,
+ audioMasterOfflineRead = 26,
+ audioMasterOfflineWrite = 27,
+ audioMasterOfflineGetCurrentPass = 28,
+ audioMasterOfflineGetCurrentMetaPass = 29,
+ audioMasterSetOutputSampleRate = 30,
+ audioMasterGetOutputSpeakerArrangement = 31,
+ audioMasterGetVendorString = 32,
+ audioMasterGetProductString = 33,
+ audioMasterGetVendorVersion = 34,
+ audioMasterVendorSpecific = 35,
+ audioMasterSetIcon = 36,
+ audioMasterCanDo = 37,
+ audioMasterGetLanguage = 38,
+ audioMasterOpenWindow = 39,
+ audioMasterCloseWindow = 40,
+ audioMasterGetDirectory = 41,
+ audioMasterUpdateDisplay = 42,
+ audioMasterBeginEdit = 43,
+ audioMasterEndEdit = 44,
+ audioMasterOpenFileSelector = 45,
+ audioMasterCloseFileSelector = 46,
+ audioMasterEditFile = 47,
+ audioMasterGetChunkFile = 48,
+ audioMasterGetInputSpeakerArrangement = 49,
+};
+
+enum VstEventTypes : int32
+{
+ kVstMidiType = 1,
+ kVstAudioType = 2,
+ kVstVideoType = 3,
+ kVstParameterType = 4,
+ kVstTriggerType = 5,
+ kVstSysExType = 6,
+};
+
+enum VstEventFlags : int32
+{
+ kVstMidiEventIsRealtime = 1 << 0,
+};
+
+enum VstTimeInfoFlags : int32
+{
+ kVstTransportChanged = 1,
+ kVstTransportPlaying = 1 << 1,
+ kVstTransportCycleActive = 1 << 2,
+ kVstTransportRecording = 1 << 3,
+ kVstAutomationWriting = 1 << 6,
+ kVstAutomationReading = 1 << 7,
+ kVstNanosValid = 1 << 8,
+ kVstPpqPosValid = 1 << 9,
+ kVstTempoValid = 1 << 10,
+ kVstBarsValid = 1 << 11,
+ kVstCyclePosValid = 1 << 12,
+ kVstTimeSigValid = 1 << 13,
+ kVstSmpteValid = 1 << 14,
+ kVstClockValid = 1 << 15,
+};
+
+enum VstSmpteFrameRate : int32
+{
+ kVstSmpte24fps = 0,
+ kVstSmpte25fps = 1,
+ kVstSmpte2997fps = 2,
+ kVstSmpte30fps = 3,
+ kVstSmpte2997dfps = 4,
+ kVstSmpte30dfps = 5,
+
+ kVstSmpteFilm16mm = 6,
+ kVstSmpteFilm35mm = 7,
+ kVstSmpte239fps = 10,
+ kVstSmpte249fps = 11,
+ kVstSmpte599fps = 12,
+ kVstSmpte60fps = 13,
+};
+
+enum VstLanguage : int32
+{
+ kVstLangEnglish = 1,
+ kVstLangGerman = 2,
+ kVstLangFrench = 3,
+ kVstLangItalian = 4,
+ kVstLangSpanish = 5,
+ kVstLangJapanese = 6,
+};
+
+enum VstProcessPrecision : int32
+{
+ kVstProcessPrecision32 = 0,
+ kVstProcessPrecision64 = 1,
+};
+
+enum VstParameterFlags : int32
+{
+ kVstParameterIsSwitch = 1 << 0,
+ kVstParameterUsesIntegerMinMax = 1 << 1,
+ kVstParameterUsesFloatStep = 1 << 2,
+ kVstParameterUsesIntStep = 1 << 3,
+ kVstParameterSupportsDisplayIndex = 1 << 4,
+ kVstParameterSupportsDisplayCategory = 1 << 5,
+ kVstParameterCanRamp = 1 << 6,
+};
+
+enum VstPinPropertiesFlags : int32
+{
+ kVstPinIsActive = 1 << 0,
+ kVstPinIsStereo = 1 << 1,
+ kVstPinUseSpeaker = 1 << 2,
+};
+
+enum VstPlugCategory : int32
+{
+ kPlugCategUnknown = 0,
+ kPlugCategEffect = 1,
+ kPlugCategSynth = 2,
+ kPlugCategAnalysis = 3,
+ kPlugCategMastering = 4,
+ kPlugCategSpacializer = 5,
+ kPlugCategRoomFx = 6,
+ kPlugSurroundFx = 7,
+ kPlugCategRestoration = 8,
+ kPlugCategOfflineProcess = 9,
+ kPlugCategShell = 10,
+ kPlugCategGenerator = 11,
+ kPlugCategMaxCount = 12,
+};
+
+enum VstMidiProgramNameFlags : int32
+{
+ kMidiIsOmni = 1,
+};
+
+enum VstSpeakerType : int32
+{
+ kSpeakerUndefined = 0x7FFFFFFF,
+ kSpeakerM = 0,
+ kSpeakerL = 1,
+ kSpeakerR = 2,
+ kSpeakerC = 3,
+ kSpeakerLfe = 4,
+ kSpeakerLs = 5,
+ kSpeakerRs = 6,
+ kSpeakerLc = 7,
+ kSpeakerRc = 8,
+ kSpeakerS = 9,
+ kSpeakerCs = kSpeakerS,
+ kSpeakerSl = 10,
+ kSpeakerSr = 11,
+ kSpeakerTm = 12,
+ kSpeakerTfl = 13,
+ kSpeakerTfc = 14,
+ kSpeakerTfr = 15,
+ kSpeakerTrl = 16,
+ kSpeakerTrc = 17,
+ kSpeakerTrr = 18,
+ kSpeakerLfe2 = 19,
+
+ kSpeakerU32 = -32,
+ kSpeakerU31 = -31,
+ kSpeakerU30 = -30,
+ kSpeakerU29 = -29,
+ kSpeakerU28 = -28,
+ kSpeakerU27 = -27,
+ kSpeakerU26 = -26,
+ kSpeakerU25 = -25,
+ kSpeakerU24 = -24,
+ kSpeakerU23 = -23,
+ kSpeakerU22 = -22,
+ kSpeakerU21 = -21,
+ kSpeakerU20 = -20,
+ kSpeakerU19 = -19,
+ kSpeakerU18 = -18,
+ kSpeakerU17 = -17,
+ kSpeakerU16 = -16,
+ kSpeakerU15 = -15,
+ kSpeakerU14 = -14,
+ kSpeakerU13 = -13,
+ kSpeakerU12 = -12,
+ kSpeakerU11 = -11,
+ kSpeakerU10 = -10,
+ kSpeakerU9 = -9,
+ kSpeakerU8 = -8,
+ kSpeakerU7 = -7,
+ kSpeakerU6 = -6,
+ kSpeakerU5 = -5,
+ kSpeakerU4 = -4,
+ kSpeakerU3 = -3,
+ kSpeakerU2 = -2,
+ kSpeakerU1 = -1,
+};
+
+enum VstSpeakerArrangementType : int32
+{
+ kSpeakerArrUserDefined = -2,
+ kSpeakerArrEmpty = -1,
+ kSpeakerArrMono = 0,
+ kSpeakerArrStereo = 1,
+ kSpeakerArrStereoSurround = 2,
+ kSpeakerArrStereoCenter = 3,
+ kSpeakerArrStereoSide = 4,
+ kSpeakerArrStereoCLfe = 5,
+ kSpeakerArr30Cine = 6,
+ kSpeakerArr30Music = 7,
+ kSpeakerArr31Cine = 8,
+ kSpeakerArr31Music = 9,
+ kSpeakerArr40Cine = 10,
+ kSpeakerArr40Music = 11,
+ kSpeakerArr41Cine = 12,
+ kSpeakerArr41Music = 13,
+ kSpeakerArr50 = 14,
+ kSpeakerArr51 = 15,
+ kSpeakerArr60Cine = 16,
+ kSpeakerArr60Music = 17,
+ kSpeakerArr61Cine = 18,
+ kSpeakerArr61Music = 19,
+ kSpeakerArr70Cine = 20,
+ kSpeakerArr70Music = 21,
+ kSpeakerArr71Cine = 22,
+ kSpeakerArr71Music = 23,
+ kSpeakerArr80Cine = 24,
+ kSpeakerArr80Music = 25,
+ kSpeakerArr81Cine = 26,
+ kSpeakerArr81Music = 27,
+ kSpeakerArr102 = 28,
+ kNumSpeakerArr = 29,
+};
+
+enum VstOfflineTaskFlags : int32
+{
+ kVstOfflineUnvalidParameter = 1 << 0,
+ kVstOfflineNewFile = 1 << 1,
+
+ kVstOfflinePlugError = 1 << 10,
+ kVstOfflineInterleavedAudio = 1 << 11,
+ kVstOfflineTempOutputFile = 1 << 12,
+ kVstOfflineFloatOutputFile = 1 << 13,
+ kVstOfflineRandomWrite = 1 << 14,
+ kVstOfflineStretch = 1 << 15,
+ kVstOfflineNoThread = 1 << 16,
+};
+
+enum VstOfflineOption : int32
+{
+ kVstOfflineAudio = 0,
+ kVstOfflinePeaks = 1,
+ kVstOfflineParameter = 2,
+ kVstOfflineMarker = 3,
+ kVstOfflineCursor = 4,
+ kVstOfflineSelection = 5,
+ kVstOfflineQueryFiles = 6,
+};
+
+enum VstAudioFileFlags : int32
+{
+ kVstOfflineReadOnly = 1 << 0,
+ kVstOfflineNoRateConversion = 1 << 1,
+ kVstOfflineNoChannelChange = 1 << 2,
+
+ kVstOfflineCanProcessSelection = 1 << 10,
+ kVstOfflineNoCrossfade = 1 << 11,
+ kVstOfflineWantRead = 1 << 12,
+ kVstOfflineWantWrite = 1 << 13,
+ kVstOfflineWantWriteMarker = 1 << 14,
+ kVstOfflineWantMoveCursor = 1 << 15,
+ kVstOfflineWantSelect = 1 << 16,
+};
+
+enum VstVirtualKey : uint8
+{
+ VKEY_BACK = 1,
+ VKEY_TAB = 2,
+ VKEY_CLEAR = 3,
+ VKEY_RETURN = 4,
+ VKEY_PAUSE = 5,
+ VKEY_ESCAPE = 6,
+ VKEY_SPACE = 7,
+ VKEY_NEXT = 8,
+ VKEY_END = 9,
+ VKEY_HOME = 10,
+
+ VKEY_LEFT = 11,
+ VKEY_UP = 12,
+ VKEY_RIGHT = 13,
+ VKEY_DOWN = 14,
+ VKEY_PAGEUP = 15,
+ VKEY_PAGEDOWN = 16,
+
+ VKEY_SELECT = 17,
+ VKEY_PRINT = 18,
+ VKEY_ENTER = 19,
+ VKEY_SNAPSHOT = 20,
+ VKEY_INSERT = 21,
+ VKEY_DELETE = 22,
+ VKEY_HELP = 23,
+ VKEY_NUMPAD0 = 24,
+ VKEY_NUMPAD1 = 25,
+ VKEY_NUMPAD2 = 26,
+ VKEY_NUMPAD3 = 27,
+ VKEY_NUMPAD4 = 28,
+ VKEY_NUMPAD5 = 29,
+ VKEY_NUMPAD6 = 30,
+ VKEY_NUMPAD7 = 31,
+ VKEY_NUMPAD8 = 32,
+ VKEY_NUMPAD9 = 33,
+ VKEY_MULTIPLY = 34,
+ VKEY_ADD = 35,
+ VKEY_SEPARATOR = 36,
+ VKEY_SUBTRACT = 37,
+ VKEY_DECIMAL = 38,
+ VKEY_DIVIDE = 39,
+ VKEY_F1 = 40,
+ VKEY_F2 = 41,
+ VKEY_F3 = 42,
+ VKEY_F4 = 43,
+ VKEY_F5 = 44,
+ VKEY_F6 = 45,
+ VKEY_F7 = 46,
+ VKEY_F8 = 47,
+ VKEY_F9 = 48,
+ VKEY_F10 = 49,
+ VKEY_F11 = 50,
+ VKEY_F12 = 51,
+ VKEY_NUMLOCK = 52,
+ VKEY_SCROLL = 53,
+
+ VKEY_SHIFT = 54,
+ VKEY_CONTROL = 55,
+ VKEY_ALT = 56,
+
+ VKEY_EQUALS = 57,
+};
+
+enum VstModifierKey : uint8
+{
+ MODIFIER_SHIFT = 1 << 0,
+ MODIFIER_ALTERNATE = 1 << 1,
+ MODIFIER_COMMAND = 1 << 2,
+ MODIFIER_CONTROL = 1 << 3,
+};
+
+enum VstFileSelectCommand : int32
+{
+ kVstFileLoad = 0,
+ kVstFileSave = 1,
+ kVstMultipleFilesLoad = 2,
+ kVstDirectorySelect = 3,
+};
+
+enum VstFileSelectType : int32
+{
+ kVstFileType = 0,
+};
+
+enum VstPanLaw : int32
+{
+ kLinearPanLaw = 0,
+ kEqualPowerPanLaw = 1,
+};
+
+enum VstProcessLevel : int32
+{
+ kVstProcessLevelUnknown = 0,
+ kVstProcessLevelUser = 1,
+ kVstProcessLevelRealtime = 2,
+ kVstProcessLevelPrefetch = 3,
+ kVstProcessLevelOffline = 4,
+};
+
+enum VstAutomationState : int32
+{
+ kVstAutomationUnsupported = 0,
+ kVstAutomationOff = 1,
+ kVstAutomationRead = 2,
+ kVstAutomationWrite = 3,
+ kVstAutomationReadWrite = 4,
+};
+
+
+namespace HostCanDo
+{
+inline constexpr char sendVstEvents[] = "sendVstEvents";
+inline constexpr char sendVstMidiEvent[] = "sendVstMidiEvent";
+inline constexpr char sendVstTimeInfo[] = "sendVstTimeInfo";
+inline constexpr char receiveVstEvents[] = "receiveVstEvents";
+inline constexpr char receiveVstMidiEvent[] = "receiveVstMidiEvent";
+inline constexpr char reportConnectionChanges[] = "reportConnectionChanges";
+inline constexpr char acceptIOChanges[] = "acceptIOChanges";
+inline constexpr char sizeWindow[] = "sizeWindow";
+inline constexpr char asyncProcessing[] = "asyncProcessing";
+inline constexpr char ofline[] = "offline";
+inline constexpr char supplyIdle[] = "supplyIdle";
+inline constexpr char supportShell[] = "supportShell";
+inline constexpr char openFileSelector[] = "openFileSelector";
+inline constexpr char closeFileSelector[] = "closeFileSelector";
+inline constexpr char startStopProcess[] = "startStopProcess";
+inline constexpr char shellCategory[] = "shellCategory";
+inline constexpr char editFile[] = "editFile";
+inline constexpr char sendVstMidiEventFlagIsRealtime[] = "sendVstMidiEventFlagIsRealtime";
+} // namespace HostCanDo
+
+namespace PluginCanDo
+{
+inline constexpr char sendVstEvents[] = "sendVstEvents";
+inline constexpr char sendVstMidiEvent[] = "sendVstMidiEvent";
+inline constexpr char receiveVstEvents[] = "receiveVstEvents";
+inline constexpr char receiveVstMidiEvent[] = "receiveVstMidiEvent";
+inline constexpr char receiveVstTimeInfo[] = "receiveVstTimeInfo";
+inline constexpr char offline[] = "offline";
+inline constexpr char midiProgramNames[] = "midiProgramNames";
+inline constexpr char bypass[] = "bypass";
+inline constexpr char MPE[] = "MPE";
+} // namespace PluginCanDo
+
+
+struct AEffect;
+typedef intptr_t(VSTCALLBACK *AudioMasterCallbackFunc)(AEffect *effect, VstOpcodeToHost opcode, int32 index, intptr_t value, void *ptr, float opt);
+typedef intptr_t(VSTCALLBACK *DispatcherFunc)(AEffect *effect, VstOpcodeToPlugin opcode, int32 index, intptr_t value, void *ptr, float opt);
+typedef void(VSTCALLBACK *ProcessProc)(AEffect *effect, float **inputs, float **outputs, int32 sampleFrames);
+typedef void(VSTCALLBACK *ProcessDoubleProc)(AEffect *effect, double **inputs, double **outputs, int32 sampleFrames);
+typedef void(VSTCALLBACK *SetParameterProc)(AEffect *effect, int32 index, float parameter);
+typedef float(VSTCALLBACK *GetParameterFunc)(AEffect *effect, int32 index);
+typedef AEffect *(VSTCALLBACK *MainProc)(AudioMasterCallbackFunc audioMaster);
+
+#pragma pack(push, 8)
+
+struct AEffect
+{
+ int32 magic;
+ DispatcherFunc dispatcher;
+ ProcessProc process;
+ SetParameterProc setParameter;
+ GetParameterFunc getParameter;
+ int32 numPrograms;
+ uint32 numParams;
+ int32 numInputs;
+ int32 numOutputs;
+ VstFlags flags;
+ void *reservedForHost1;
+ void *reservedForHost2;
+ int32 initialDelay;
+ int32 realQualities;
+ int32 offQualities;
+ float ioRatio;
+ void *object;
+ void *user;
+ int32 uniqueID;
+ int32 version;
+ ProcessProc processReplacing;
+ ProcessDoubleProc processDoubleReplacing;
+};
+
+struct ERect
+{
+ int16 top, left, bottom, right;
+
+ int16 Width() const { return right - left; }
+ int16 Height() const { return bottom - top; }
+};
+
+struct VstEvent
+{
+ VstEventTypes type;
+ int32 byteSize;
+ int32 deltaFrames;
+ VstEventFlags flags;
+};
+
+struct VstEvents
+{
+ static constexpr size_t MAX_EVENTS = 256;
+
+ int32 numEvents;
+ intptr_t reserved;
+ VstEvent *events[MAX_EVENTS];
+
+ size_t size() { return numEvents; }
+ auto begin() { return std::begin(events); }
+ auto end() { return std::begin(events) + numEvents; }
+ auto begin() const { return std::begin(events); }
+ auto end() const { return std::begin(events) + numEvents; }
+ auto cbegin() const { return std::cbegin(events); }
+ auto cend() const { return std::cbegin(events) + numEvents; }
+};
+
+struct VstMidiEvent : public VstEvent
+{
+ int32 noteLength;
+ int32 noteOffset;
+ uint32 midiData;
+ int8 detune;
+ int8 noteOffVelocity;
+ int8 reserved1;
+ int8 reserved2;
+};
+
+struct VstMidiSysexEvent : public VstEvent
+{
+ int32 dumpBytes;
+ intptr_t reserved1;
+ const std::byte *sysexDump;
+ intptr_t reserved2;
+};
+
+struct VstTimeInfo
+{
+ double samplePos;
+ double sampleRate;
+ double nanoSeconds;
+ double ppqPos;
+ double tempo;
+ double barStartPos;
+ double cycleStartPos;
+ double cycleEndPos;
+ int32 timeSigNumerator;
+ int32 timeSigDenominator;
+ int32 smpteOffset;
+ VstSmpteFrameRate smpteFrameRate;
+ int32 samplesToNextClock;
+ VstTimeInfoFlags flags;
+};
+
+struct VstVariableIo
+{
+ float **inputs;
+ float **outputs;
+ int32 numSamplesInput;
+ int32 numSamplesOutput;
+ int32 *numSamplesInputProcessed;
+ int32 *numSamplesOutputProcessed;
+};
+
+struct VstParameterProperties
+{
+ float stepFloat;
+ float smallStepFloat;
+ float largeStepFloat;
+ char label[kVstMaxLabelLen];
+ VstParameterFlags flags;
+ int32 minInteger;
+ int32 maxInteger;
+ int32 stepInteger;
+ int32 largeStepInteger;
+ char shortLabel[kVstMaxShortLabelLen];
+ int16 displayIndex;
+ int16 category;
+ int16 numParametersInCategory;
+ int16 reserved;
+ char categoryLabel[kVstMaxCategLabelLen];
+ int8 reserved2[16];
+};
+
+struct VstPinProperties
+{
+ char label[kVstMaxLabelLen];
+ VstPinPropertiesFlags flags;
+ VstSpeakerArrangementType arragementType;
+ char shortLabel[kVstMaxShortLabelLen];
+ int8 reserved[48];
+};
+
+struct MidiProgramName
+{
+ int32 thisProgramIndex;
+ char name[kVstMaxNameLen];
+ int8 midiProgram;
+ int8 midiBankMSB;
+ int8 midiBankLSB;
+ int8 reserved;
+ int32 parentCategoryIndex;
+ VstMidiProgramNameFlags flags;
+};
+
+struct MidiProgramCategory
+{
+ int32 thisCategoryIndex;
+ char name[kVstMaxNameLen];
+ int32 parentCategoryIndex;
+ int32 flags;
+};
+
+struct MidiKeyName
+{
+ int32 thisProgramIndex;
+ int32 thisKeyNumber;
+ char keyName[kVstMaxNameLen];
+ int32 reserved;
+ int32 flags;
+};
+
+struct VstSpeakerProperties
+{
+ float azimuth;
+ float elevation;
+ float radius;
+ float reserved1;
+ char name[kVstMaxNameLen];
+ VstSpeakerType type;
+ int8 reserved2[28];
+};
+
+struct VstSpeakerArrangement
+{
+ VstSpeakerArrangementType type;
+ int32 numChannels;
+ VstSpeakerProperties speakers[8];
+};
+
+struct VstOfflineTask
+{
+ char processName[96];
+ double readPosition;
+ double writePosition;
+ int32 readCount;
+ int32 writeCount;
+ int32 sizeInputBuffer;
+ int32 sizeOutputBuffer;
+ void *inputBuffer;
+ void *outputBuffer;
+ double positionToProcessFrom;
+ double numFramesToProcess;
+ double maxFramesToWrite;
+ void *extraBuffer;
+ int32 value;
+ int32 index;
+ double numFramesInSourceFile;
+ double sourceSampleRate;
+ double destinationSampleRate;
+ int32 numSourceChannels;
+ int32 numDestinationChannels;
+ int32 sourceFormat;
+ int32 destinationFormat;
+ char outputText[512];
+ double progress;
+ int32 progressMode;
+ char progressText[100];
+ VstOfflineTaskFlags flags;
+ int32 returnValue;
+ void *hostOwned;
+ void *plugOwned;
+ int8 reserved[1024];
+};
+
+struct VstAudioFile
+{
+ VstAudioFileFlags flags;
+ void *hostOwned;
+ void *plugOwned;
+ char name[kVstMaxFileNameLen];
+ int32 uniqueID;
+ double sampleRate;
+ int32 numChannels;
+ double numFrames;
+ int32 format;
+ double editCursorPosition;
+ double selectionStart;
+ double selectionSize;
+ int32 selectedChannelsMask;
+ int32 numMarkers;
+ int32 timeRulerUnit;
+ double timeRulerOffset;
+ double tempo;
+ int32 timeSigNumerator;
+ int32 timeSigDenominator;
+ int32 ticksPerBlackNote;
+ int32 smtpeFrameRate;
+ int8 reserved[64];
+};
+
+struct VstAudioFileMarker
+{
+ double position;
+ char name[32];
+ int32 type;
+ int32 id;
+ int32 reserved;
+};
+
+struct VstWindow
+{
+ char title[128];
+ int16 xPos;
+ int16 yPos;
+ int16 width;
+ int16 height;
+ int32 style;
+ void *parent;
+ void *userHandle;
+ void *windowHandle;
+ int8 reserved[104];
+};
+
+struct VstKeyCode
+{
+ int32 characterCode;
+ VstVirtualKey virtualCode;
+ VstModifierKey modifierCode;
+};
+
+struct VstFileType
+{
+ char name[128];
+ char macType[8];
+ char dosType[8];
+ char unixType[8];
+ char mimeType1[128];
+ char mimeType2[128];
+};
+
+struct VstFileSelect
+{
+ VstFileSelectCommand command;
+ VstFileSelectType type;
+ int32 macCreator;
+ int32 numFileTypes;
+ VstFileType *fileTypes;
+ char title[1024];
+ char *initialPath;
+ char *returnPath;
+ int32 sizeReturnPath;
+ char **returnMultiplePaths;
+ int32 numReturnPaths;
+ intptr_t reserved;
+ int8 reserved2[116];
+};
+
+struct VstPatchChunkInfo
+{
+ int32 version;
+ int32 pluginUniqueID;
+ int32 pluginVersion;
+ int32 numElements;
+ int8 reserved[48];
+};
+
+#pragma pack(pop)
+
+int32 constexpr FourCC(const char (&cc)[5])
+{
+ return static_cast<int32>(static_cast<uint32>(cc[3]) | (static_cast<uint32>(cc[2]) << 8) | (static_cast<uint32>(cc[1]) << 16) | (static_cast<uint32>(cc[0]) << 24));
+}
+
+inline constexpr int32 kEffectMagic = FourCC("VstP");
+
+template <typename T>
+constexpr T *FromIntPtr(intptr_t v)
+{
+ return reinterpret_cast<T *>(v);
+}
+
+template <typename T>
+constexpr intptr_t ToIntPtr(T *v)
+{
+ return reinterpret_cast<intptr_t>(v);
+}
+
+} // namespace Vst
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstEventQueue.h b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstEventQueue.h
new file mode 100644
index 00000000..4989da27
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/mptrack/plugins/VstEventQueue.h
@@ -0,0 +1,126 @@
+/*
+ * VstEventQueue.h
+ * ---------------
+ * Purpose: Event queue for VST events.
+ * Notes : Modelled after an idea from https://www.kvraudio.com/forum/viewtopic.php?p=3043807#p3043807
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include <deque>
+#include "mpt/mutex/mutex.hpp"
+#include "VstDefinitions.h"
+
+OPENMPT_NAMESPACE_BEGIN
+
+class VstEventQueue : public Vst::VstEvents
+{
+protected:
+
+ // Although originally all event types were apparently supposed to be of the same size, this is not the case on 64-Bit systems.
+ // VstMidiSysexEvent contains 3 pointers, so the struct size differs between 32-Bit and 64-Bit systems.
+ union Event
+ {
+ Vst::VstEvent event;
+ Vst::VstMidiEvent midi;
+ Vst::VstMidiSysexEvent sysex;
+ };
+
+ // Here we store our events which are then inserted into the fixed-size event buffer sent to the plugin.
+ std::deque<Event> eventQueue;
+ // Since plugins can also add events to the queue (even from a different thread than the processing thread),
+ // we need to ensure that reading and writing is never done in parallel.
+ mpt::mutex criticalSection;
+
+public:
+
+ VstEventQueue()
+ {
+ numEvents = 0;
+ reserved = 0;
+ std::fill(std::begin(events), std::end(events), nullptr);
+ }
+
+ // Get the number of events that are currently queued, but not in the output buffer.
+ size_t GetNumQueuedEvents()
+ {
+ return eventQueue.size() - numEvents;
+ }
+
+ // Add a VST event to the queue. Returns true on success.
+ // Set insertFront to true to prioritise this event (i.e. add it at the front of the queue instead of the back)
+ bool Enqueue(const Vst::VstEvent *event, bool insertFront = false)
+ {
+ MPT_ASSERT(event->type != Vst::kVstSysExType || event->byteSize == sizeof(Vst::VstMidiSysexEvent));
+ MPT_ASSERT(event->type != Vst::kVstMidiType || event->byteSize == sizeof(Vst::VstMidiEvent));
+
+ Event copyEvent;
+ size_t copySize;
+ // randomid by Insert Piz Here sends events of type kVstMidiType, but with a claimed size of 24 bytes instead of 32.
+ // Hence, we enforce the size of known events.
+ if(event->type == Vst::kVstSysExType)
+ copySize = sizeof(Vst::VstMidiSysexEvent);
+ else if(event->type == Vst::kVstMidiType)
+ copySize = sizeof(Vst::VstMidiEvent);
+ else
+ copySize = std::min(size_t(event->byteSize), sizeof(copyEvent));
+ memcpy(&copyEvent, event, copySize);
+
+ if(event->type == Vst::kVstSysExType)
+ {
+ // SysEx messages need to be copied, as the space used for the dump might be freed in the meantime.
+ auto &e = copyEvent.sysex;
+ auto sysexDump = new (std::nothrow) std::byte[e.dumpBytes];
+ if(sysexDump == nullptr)
+ return false;
+ memcpy(sysexDump, e.sysexDump, e.dumpBytes);
+ e.sysexDump = sysexDump;
+ }
+
+ mpt::lock_guard<mpt::mutex> lock(criticalSection);
+ if(insertFront)
+ eventQueue.push_front(copyEvent);
+ else
+ eventQueue.push_back(copyEvent);
+ return true;
+ }
+
+ // Set up the queue for transmitting to the plugin. Returns number of elements that are going to be transmitted.
+ int32 Finalise()
+ {
+ mpt::lock_guard<mpt::mutex> lock(criticalSection);
+ numEvents = static_cast<int32>(std::min(eventQueue.size(), MAX_EVENTS));
+ for(int32 i = 0; i < numEvents; i++)
+ {
+ events[i] = &eventQueue[i].event;
+ }
+ return numEvents;
+ }
+
+ // Remove transmitted events from the queue
+ void Clear()
+ {
+ mpt::lock_guard<mpt::mutex> lock(criticalSection);
+ if(numEvents)
+ {
+ // Release temporarily allocated buffer for SysEx messages
+ for(auto e = eventQueue.begin(); e != eventQueue.begin() + numEvents; ++e)
+ {
+ if(e->event.type == Vst::kVstSysExType)
+ {
+ delete[] e->sysex.sysexDump;
+ }
+ }
+ eventQueue.erase(eventQueue.begin(), eventQueue.begin() + numEvents);
+ numEvents = 0;
+ }
+ }
+
+};
+
+OPENMPT_NAMESPACE_END