diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/plugins | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/plugins')
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(©Event, 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 |