Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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