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