1 /*
2 * PlugInterface.h
3 * ---------------
4 * Purpose: Interface class for plugin handling
5 * Notes : (currently none)
6 * Authors: OpenMPT Devs
7 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8 */
9
10
11 #pragma once
12
13 #include "openmpt/all/BuildSettings.hpp"
14
15 #ifndef NO_PLUGINS
16
17 #include "../../soundlib/Snd_defs.h"
18 #include "../../soundlib/MIDIEvents.h"
19 #include "../../soundlib/Mixer.h"
20 #include "PluginMixBuffer.h"
21 #include "PluginStructs.h"
22
23 OPENMPT_NAMESPACE_BEGIN
24
25 struct VSTPluginLib;
26 struct SNDMIXPLUGIN;
27 struct ModInstrument;
28 class CSoundFile;
29 class CModDoc;
30 class CAbstractVstEditor;
31
32 struct SNDMIXPLUGINSTATE
33 {
34 // dwFlags flags
35 enum PluginStateFlags
36 {
37 psfMixReady = 0x01, // Set when cleared
38 psfHasInput = 0x02, // Set when plugin has non-silent input
39 psfSilenceBypass = 0x04, // Bypass because of silence detection
40 };
41
42 mixsample_t *pMixBuffer = nullptr; // Stereo effect send buffer
43 uint32 dwFlags = 0; // PluginStateFlags
44 uint32 inputSilenceCount = 0; // How much silence has been processed? (for plugin auto-turnoff)
45 mixsample_t nVolDecayL = 0, nVolDecayR = 0; // End of sample click removal
46
ResetSilenceSNDMIXPLUGINSTATE47 void ResetSilence()
48 {
49 dwFlags |= psfHasInput;
50 dwFlags &= ~psfSilenceBypass;
51 inputSilenceCount = 0;
52 }
53 };
54
55
56 class IMixPlugin
57 {
58 friend class CAbstractVstEditor;
59
60 protected:
61 IMixPlugin *m_pNext = nullptr, *m_pPrev = nullptr;
62 VSTPluginLib &m_Factory;
63 CSoundFile &m_SndFile;
64 SNDMIXPLUGIN *m_pMixStruct;
65 #ifdef MODPLUG_TRACKER
66 CAbstractVstEditor *m_pEditor = nullptr;
67 #endif // MODPLUG_TRACKER
68
69 public:
70 SNDMIXPLUGINSTATE m_MixState;
71 PluginMixBuffer<float, MIXBUFFERSIZE> m_mixBuffer; // Float buffers (input and output) for plugins
72
73 protected:
74 mixsample_t m_MixBuffer[MIXBUFFERSIZE * 2 + 2]; // Stereo interleaved input (sample mixer renders here)
75
76 float m_fGain = 1.0f;
77 PLUGINDEX m_nSlot = 0;
78
79 bool m_isSongPlaying = false;
80 bool m_isResumed = false;
81
82 public:
83 bool m_recordAutomation = false;
84 bool m_passKeypressesToPlug = false;
85 bool m_recordMIDIOut = false;
86
87 protected:
88 virtual ~IMixPlugin();
89
90 // Insert plugin into list of loaded plugins.
91 void InsertIntoFactoryList();
92
93 public:
94 // Non-virtual part of the interface
95 IMixPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
GetSoundFile()96 inline CSoundFile &GetSoundFile() { return m_SndFile; }
GetSoundFile()97 inline const CSoundFile &GetSoundFile() const { return m_SndFile; }
98
99 #ifdef MODPLUG_TRACKER
100 CModDoc *GetModDoc();
101 const CModDoc *GetModDoc() const;
102
103 void SetSlot(PLUGINDEX slot);
GetSlot()104 inline PLUGINDEX GetSlot() const { return m_nSlot; }
105 #endif // MODPLUG_TRACKER
106
GetPluginFactory()107 inline VSTPluginLib &GetPluginFactory() const { return m_Factory; }
108 // Returns the next instance of the same plugin
GetNextInstance()109 inline IMixPlugin *GetNextInstance() const { return m_pNext; }
110
111 void SetDryRatio(uint32 param);
112 bool IsBypassed() const;
113 void RecalculateGain();
114 // Query output latency from host (in seconds)
115 double GetOutputLatency() const;
116
117 // Destroy the plugin
118 virtual void Release() = 0;
119 virtual int32 GetUID() const = 0;
120 virtual int32 GetVersion() const = 0;
121 virtual void Idle() = 0;
122 // Plugin latency in samples
123 virtual uint32 GetLatency() const = 0;
124
125 virtual int32 GetNumPrograms() const = 0;
126 virtual int32 GetCurrentProgram() = 0;
127 virtual void SetCurrentProgram(int32 nIndex) = 0;
128
129 virtual PlugParamIndex GetNumParameters() const = 0;
130 virtual void SetParameter(PlugParamIndex paramindex, PlugParamValue paramvalue) = 0;
131 virtual PlugParamValue GetParameter(PlugParamIndex nIndex) = 0;
132
133 // Save parameters for storing them in a module file
134 virtual void SaveAllParameters();
135 // Restore parameters from module file
136 virtual void RestoreAllParameters(int32 program);
137 virtual void Process(float *pOutL, float *pOutR, uint32 numFrames) = 0;
138 void ProcessMixOps(float *pOutL, float *pOutR, float *leftPlugOutput, float *rightPlugOutput, uint32 numFrames);
139 // Render silence and return the highest resulting output level
140 virtual float RenderSilence(uint32 numSamples);
141
142 // MIDI event handling
MidiSend(uint32)143 virtual bool MidiSend(uint32 /*midiCode*/) { return true; }
MidiSysexSend(mpt::const_byte_span)144 virtual bool MidiSysexSend(mpt::const_byte_span /*sysex*/) { return true; }
MidiCC(MIDIEvents::MidiCC,uint8,CHANNELINDEX)145 virtual void MidiCC(MIDIEvents::MidiCC /*nController*/, uint8 /*nParam*/, CHANNELINDEX /*trackChannel*/) { }
MidiPitchBendRaw(int32,CHANNELINDEX)146 virtual void MidiPitchBendRaw(int32 /*pitchbend*/, CHANNELINDEX /*trackChannel*/) {}
MidiPitchBend(int32,int8,CHANNELINDEX)147 virtual void MidiPitchBend(int32 /*increment*/, int8 /*pwd*/, CHANNELINDEX /*trackChannel*/) { }
MidiVibrato(int32,int8,CHANNELINDEX)148 virtual void MidiVibrato(int32 /*depth*/, int8 /*pwd*/, CHANNELINDEX /*trackerChn*/) { }
MidiCommand(const ModInstrument &,uint16,uint16,CHANNELINDEX)149 virtual void MidiCommand(const ModInstrument &/*instr*/, uint16 /*note*/, uint16 /*vol*/, CHANNELINDEX /*trackChannel*/) { }
HardAllNotesOff()150 virtual void HardAllNotesOff() { }
IsNotePlaying(uint8,CHANNELINDEX)151 virtual bool IsNotePlaying(uint8 /*note*/, CHANNELINDEX /*trackerChn*/) { return false; }
152
153 // Modify parameter by given amount. Only needs to be re-implemented if plugin architecture allows this to be performed atomically.
154 virtual void ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff);
NotifySongPlaying(bool playing)155 virtual void NotifySongPlaying(bool playing) { m_isSongPlaying = playing; }
IsSongPlaying()156 virtual bool IsSongPlaying() const { return m_isSongPlaying; }
IsResumed()157 virtual bool IsResumed() const { return m_isResumed; }
158 virtual void Resume() = 0;
159 virtual void Suspend() = 0;
160 // Tell the plugin that there is a discontinuity between the previous and next render call (e.g. aftert jumping around in the module)
161 virtual void PositionChanged() = 0;
162 virtual void Bypass(bool = true);
ToggleBypass()163 bool ToggleBypass() { Bypass(!IsBypassed()); return IsBypassed(); }
164 virtual bool IsInstrument() const = 0;
165 virtual bool CanRecieveMidiEvents() = 0;
166 // If false is returned, mixing this plugin can be skipped if its input are currently completely silent.
167 virtual bool ShouldProcessSilence() = 0;
ResetSilence()168 virtual void ResetSilence() { m_MixState.ResetSilence(); }
169
170 size_t GetOutputPlugList(std::vector<IMixPlugin *> &list);
171 size_t GetInputPlugList(std::vector<IMixPlugin *> &list);
172 size_t GetInputInstrumentList(std::vector<INSTRUMENTINDEX> &list);
173 size_t GetInputChannelList(std::vector<CHANNELINDEX> &list);
174
175 #ifdef MODPLUG_TRACKER
176 bool SaveProgram();
177 bool LoadProgram(mpt::PathString fileName = mpt::PathString());
178
179 virtual CString GetDefaultEffectName() = 0;
180
181 // Cache a range of names, in case one-by-one retrieval would be slow (e.g. when using plugin bridge)
CacheProgramNames(int32,int32)182 virtual void CacheProgramNames(int32 /*firstProg*/, int32 /*lastProg*/) { }
CacheParameterNames(int32,int32)183 virtual void CacheParameterNames(int32 /*firstParam*/, int32 /*lastParam*/) { }
184
185 // Allowed value range for a parameter
GetParamUIRange(PlugParamIndex)186 virtual std::pair<PlugParamValue, PlugParamValue> GetParamUIRange(PlugParamIndex /*param*/) { return {0.0f, 1.0f}; }
187 // Scale allowed value range of a parameter to/from [0,1]
188 PlugParamValue GetScaledUIParam(PlugParamIndex param);
189 void SetScaledUIParam(PlugParamIndex param, PlugParamValue value);
190
191 virtual CString GetParamName(PlugParamIndex param) = 0;
192 virtual CString GetParamLabel(PlugParamIndex param) = 0;
193 virtual CString GetParamDisplay(PlugParamIndex param) = 0;
194 CString GetFormattedParamName(PlugParamIndex param);
195 CString GetFormattedParamValue(PlugParamIndex param);
196 virtual CString GetCurrentProgramName() = 0;
197 virtual void SetCurrentProgramName(const CString &name) = 0;
198 virtual CString GetProgramName(int32 program) = 0;
199 CString GetFormattedProgramName(int32 index);
200
201 virtual bool HasEditor() const = 0;
202 protected:
203 virtual CAbstractVstEditor *OpenEditor();
204 public:
205 // Get the plugin's editor window
GetEditor()206 CAbstractVstEditor *GetEditor() { return m_pEditor; }
GetEditor()207 const CAbstractVstEditor *GetEditor() const { return m_pEditor; }
208 void ToggleEditor();
209 void CloseEditor();
210 void SetEditorPos(int32 x, int32 y);
211 void GetEditorPos(int32 &x, int32 &y) const;
212
213 // Notify OpenMPT that a plugin parameter has changed and set document as modified
214 void AutomateParameter(PlugParamIndex param);
215 // Plugin state changed, set document as modified.
216 void SetModified();
217 #endif
218
219 virtual int GetNumInputChannels() const = 0;
220 virtual int GetNumOutputChannels() const = 0;
221
222 using ChunkData = mpt::const_byte_span;
ProgramsAreChunks()223 virtual bool ProgramsAreChunks() const { return false; }
GetChunk(bool)224 virtual ChunkData GetChunk(bool /*isBank*/) { return ChunkData(); }
SetChunk(const ChunkData &,bool)225 virtual void SetChunk(const ChunkData &/*chunk*/, bool /*isBank*/) { }
226
227 virtual void BeginSetProgram(int32 /*program*/ = -1) {}
EndSetProgram()228 virtual void EndSetProgram() {}
229 virtual void BeginGetProgram(int32 /*program*/ = -1) {}
EndGetProgram()230 virtual void EndGetProgram() {}
231 };
232
233
ModifyParameter(PlugParamIndex nIndex,PlugParamValue diff)234 inline void IMixPlugin::ModifyParameter(PlugParamIndex nIndex, PlugParamValue diff)
235 {
236 PlugParamValue val = GetParameter(nIndex) + diff;
237 Limit(val, PlugParamValue(0), PlugParamValue(1));
238 SetParameter(nIndex, val);
239 }
240
241
242 // IMidiPlugin: Default implementation of plugins with MIDI input
243
244 class IMidiPlugin : public IMixPlugin
245 {
246 protected:
247 enum
248 {
249 // Pitch wheel constants
250 kPitchBendShift = 12, // Use lowest 12 bits for fractional part and vibrato flag => 16.11 fixed point precision
251 kPitchBendMask = (~1),
252 kVibratoFlag = 1,
253 };
254
255 struct PlugInstrChannel
256 {
257 int32 midiPitchBendPos = 0; // Current Pitch Wheel position, in 16.11 fixed point format. Lowest bit is used for indicating that vibrato was applied. Vibrato offset itself is not stored in this value.
258 uint16 currentProgram = uint16_max;
259 uint16 currentBank = uint16_max;
260 uint8 noteOnMap[128][MAX_CHANNELS];
261
ResetProgramPlugInstrChannel262 void ResetProgram() { currentProgram = uint16_max; currentBank = uint16_max; }
263 };
264
265 std::array<PlugInstrChannel, 16> m_MidiCh; // MIDI channel state
266
267 public:
268 IMidiPlugin(VSTPluginLib &factory, CSoundFile &sndFile, SNDMIXPLUGIN *mixStruct);
269
270 void MidiCC(MIDIEvents::MidiCC nController, uint8 nParam, CHANNELINDEX trackChannel) override;
271 void MidiPitchBendRaw(int32 pitchbend, CHANNELINDEX trackerChn) override;
272 void MidiPitchBend(int32 increment, int8 pwd, CHANNELINDEX trackerChn) override;
273 void MidiVibrato(int32 depth, int8 pwd, CHANNELINDEX trackerChn) override;
274 void MidiCommand(const ModInstrument &instr, uint16 note, uint16 vol, CHANNELINDEX trackChannel) override;
275 bool IsNotePlaying(uint8 note, CHANNELINDEX trackerChn) override;
276
277 // Get the MIDI channel currently associated with a given tracker channel
278 virtual uint8 GetMidiChannel(CHANNELINDEX trackChannel) const;
279
280 protected:
281 // Plugin wants to send MIDI to OpenMPT
282 virtual void ReceiveMidi(uint32 midiCode);
283 virtual void ReceiveSysex(mpt::const_byte_span sysex);
284
285 // Converts a 14-bit MIDI pitch bend position to our internal pitch bend position representation
EncodePitchBendParam(int32 position)286 static constexpr int32 EncodePitchBendParam(int32 position) { return (position << kPitchBendShift); }
287 // Converts the internal pitch bend position to a 14-bit MIDI pitch bend position
DecodePitchBendParam(int32 position)288 static constexpr int16 DecodePitchBendParam(int32 position) { return static_cast<int16>(position >> kPitchBendShift); }
289 // Apply Pitch Wheel Depth (PWD) to some MIDI pitch bend value.
290 static inline void ApplyPitchWheelDepth(int32 &value, int8 pwd);
291
292 void SendMidiPitchBend(uint8 midiCh, int32 newPitchBendPos);
293 };
294
295 OPENMPT_NAMESPACE_END
296
297 #endif // NO_PLUGINS
298
299