Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue