1 //------------------------------------------------------------------------
2 // Project     : VST SDK
3 //
4 // Category    : Helpers
5 // Filename    : public.sdk/source/vst/auwrapper/auwrapper.h
6 // Created by  : Steinberg, 12/2007
7 // Description : VST 3 -> AU Wrapper
8 //
9 //-----------------------------------------------------------------------------
10 // LICENSE
11 // (c) 2020, Steinberg Media Technologies GmbH, All Rights Reserved
12 //-----------------------------------------------------------------------------
13 // Redistribution and use in source and binary forms, with or without modification,
14 // are permitted provided that the following conditions are met:
15 //
16 //   * Redistributions of source code must retain the above copyright notice,
17 //     this list of conditions and the following disclaimer.
18 //   * Redistributions in binary form must reproduce the above copyright notice,
19 //     this list of conditions and the following disclaimer in the documentation
20 //     and/or other materials provided with the distribution.
21 //   * Neither the name of the Steinberg Media Technologies nor the names of its
22 //     contributors may be used to endorse or promote products derived from this
23 //     software without specific prior written permission.
24 //
25 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
33 // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
34 // OF THE POSSIBILITY OF SUCH DAMAGE.
35 //-----------------------------------------------------------------------------
36 
37 #include "docAUv2.h"
38 
39 /// \cond ignore
40 
41 #pragma once
42 
43 #if CA_USE_AUDIO_PLUGIN_ONLY
44 #include "AudioUnits/AUPublic/AUBase/AUBase.h"
45 #define AUWRAPPER_BASE_CLASS AUBase
46 #else
47 #include "AudioUnits/AUPublic/OtherBases/MusicDeviceBase.h"
48 #define AUWRAPPER_BASE_CLASS MusicDeviceBase
49 #endif
50 
51 #include "public.sdk/source/vst/hosting/eventlist.h"
52 #include "public.sdk/source/vst/hosting/parameterchanges.h"
53 #include "public.sdk/source/vst/hosting/processdata.h"
54 #include "base/source/fstring.h"
55 #include "base/source/timer.h"
56 #include "base/thread/include/flock.h"
57 #include "pluginterfaces/vst/ivstaudioprocessor.h"
58 #include "pluginterfaces/vst/ivsteditcontroller.h"
59 #include "pluginterfaces/vst/ivstprocesscontext.h"
60 #include "pluginterfaces/vst/ivstunits.h"
61 
62 #include <AudioToolbox/AudioToolbox.h>
63 #include <Cocoa/Cocoa.h>
64 #include <map>
65 #include <vector>
66 
67 namespace Steinberg {
68 namespace Vst {
69 
70 //------------------------------------------------------------------------
71 typedef struct MIDIMessageInfoStruct
72 {
73 	UInt8 status;
74 	UInt8 channel;
75 	UInt8 data1;
76 	UInt8 data2;
77 	UInt32 startFrame;
78 } MIDIMessageInfoStruct;
79 
80 //------------------------------------------------------------------------
81 class MIDIOutputCallbackHelper
82 {
83 public:
MIDIOutputCallbackHelper()84 	MIDIOutputCallbackHelper ()
85 	{
86 		mMIDIMessageList.reserve (16);
87 		mMIDICallbackStruct.midiOutputCallback = NULL;
88 	}
~MIDIOutputCallbackHelper()89 	virtual ~MIDIOutputCallbackHelper () {};
90 
SetCallbackInfo(AUMIDIOutputCallback callback,void * userData)91 	void SetCallbackInfo (AUMIDIOutputCallback callback, void* userData)
92 	{
93 		mMIDICallbackStruct.midiOutputCallback = callback;
94 		mMIDICallbackStruct.userData = userData;
95 	}
96 
AddEvent(UInt8 status,UInt8 channel,UInt8 data1,UInt8 data2,UInt32 inStartFrame)97 	void AddEvent (UInt8 status, UInt8 channel, UInt8 data1, UInt8 data2, UInt32 inStartFrame)
98 	{
99 		MIDIMessageInfoStruct info = {status, channel, data1, data2, inStartFrame};
100 		mMIDIMessageList.push_back (info);
101 	}
102 
FireAtTimeStamp(const AudioTimeStamp & inTimeStamp)103 	void FireAtTimeStamp (const AudioTimeStamp& inTimeStamp)
104 	{
105 		if (!mMIDIMessageList.empty () && mMIDICallbackStruct.midiOutputCallback != 0)
106 		{
107 			// synthesize the packet list and call the MIDIOutputCallback
108 			// iterate through the vector and get each item
109 			std::vector<MIDIMessageInfoStruct>::iterator myIterator;
110 			MIDIPacketList* pktlist = PacketList ();
111 
112 			for (myIterator = mMIDIMessageList.begin (); myIterator != mMIDIMessageList.end ();
113 			     myIterator++)
114 			{
115 				MIDIMessageInfoStruct item = *myIterator;
116 
117 				MIDIPacket* pkt = MIDIPacketListInit (pktlist);
118 				bool tooBig = false;
119 				Byte data[3] = {item.status, item.data1, item.data2};
120 				if ((pkt = MIDIPacketListAdd (pktlist, sizeof (mBuffersAllocated), pkt,
121 				                              item.startFrame, 4, const_cast<Byte*> (data))) ==
122 				    NULL)
123 					tooBig = true;
124 
125 				if (tooBig)
126 				{ // send what we have and then clear the buffer and send again
127 					// issue the callback with what we got
128 					OSStatus result = mMIDICallbackStruct.midiOutputCallback (
129 					    mMIDICallbackStruct.userData, &inTimeStamp, 0, pktlist);
130 					if (result != noErr)
131 						printf ("error calling output callback: %d", (int)result);
132 
133 					// clear stuff we've already processed, and fire again
134 					mMIDIMessageList.erase (mMIDIMessageList.begin (), myIterator);
135 					this->FireAtTimeStamp (inTimeStamp);
136 					return;
137 				}
138 			}
139 			// fire callback
140 			OSStatus result = mMIDICallbackStruct.midiOutputCallback (mMIDICallbackStruct.userData,
141 			                                                          &inTimeStamp, 0, pktlist);
142 			if (result != noErr)
143 				printf ("error calling output callback: %d", (int)result);
144 
145 			mMIDIMessageList.clear ();
146 		}
147 	}
148 
149 protected:
150 	typedef std::vector<MIDIMessageInfoStruct> MIDIMessageList;
151 
152 private:
PacketList()153 	MIDIPacketList* PacketList () { return (MIDIPacketList*)mBuffersAllocated; }
154 
155 	Byte mBuffersAllocated[1024];
156 	AUMIDIOutputCallbackStruct mMIDICallbackStruct;
157 	MIDIMessageList mMIDIMessageList;
158 };
159 
160 //------------------------------------------------------------------------
161 //------------------------------------------------------------------------
162 class AUWrapper : public AUWRAPPER_BASE_CLASS, public IComponentHandler, public ITimerCallback
163 {
164 public:
165 	AUWrapper (ComponentInstanceRecord* ci);
166 	~AUWrapper ();
167 
168 	//---ComponentBase---------------------
169 	ComponentResult	Version () SMTG_OVERRIDE;
170 	void PostConstructor () SMTG_OVERRIDE;
171 
172 	//---AUBase-----------------------------
173 	void Cleanup () SMTG_OVERRIDE;
174 	ComponentResult Initialize () SMTG_OVERRIDE;
175 	AUElement* CreateElement (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
176 	UInt32 SupportedNumChannels (const AUChannelInfo** outInfo) SMTG_OVERRIDE;
177 	bool StreamFormatWritable (AudioUnitScope scope, AudioUnitElement element) SMTG_OVERRIDE;
178 	ComponentResult ChangeStreamFormat (AudioUnitScope inScope, AudioUnitElement inElement, const CAStreamBasicDescription& inPrevFormat, const CAStreamBasicDescription& inNewFormat) SMTG_OVERRIDE;
179 	ComponentResult SetConnection (const AudioUnitConnection& inConnection) SMTG_OVERRIDE;
180 	ComponentResult GetParameterInfo (AudioUnitScope inScope, AudioUnitParameterID inParameterID, AudioUnitParameterInfo& outParameterInfo) SMTG_OVERRIDE;
181 	ComponentResult SetParameter (AudioUnitParameterID inID, AudioUnitScope inScope, AudioUnitElement inElement, AudioUnitParameterValue inValue, UInt32 inBufferOffsetInFrames) SMTG_OVERRIDE;
182 
183 	ComponentResult SaveState (CFPropertyListRef* outData) SMTG_OVERRIDE;
184 	ComponentResult RestoreState (CFPropertyListRef inData) SMTG_OVERRIDE;
185 
186 	ComponentResult Render (AudioUnitRenderActionFlags &ioActionFlags, const AudioTimeStamp &inTimeStamp, UInt32 inNumberFrames) SMTG_OVERRIDE;
187 	void processOutputEvents (const AudioTimeStamp &inTimeStamp);
188 
189 	int GetNumCustomUIComponents () SMTG_OVERRIDE;
190 	void GetUIComponentDescs (ComponentDescription* inDescArray) SMTG_OVERRIDE;
191 
192 	ComponentResult GetPropertyInfo (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32 &outDataSize, Boolean &outWritable) SMTG_OVERRIDE;
193 	ComponentResult GetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData) SMTG_OVERRIDE;
194 	ComponentResult SetProperty (AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize) SMTG_OVERRIDE;
195 	bool CanScheduleParameters() const; // Not in the base class anymore in newer CoreAudio SDKs
196 
197 	Float64 GetLatency () SMTG_OVERRIDE;
198 	Float64 GetTailTime () SMTG_OVERRIDE;
199 
200 	//---Factory presets
201 	OSStatus GetPresets (CFArrayRef* outData) const SMTG_OVERRIDE;
202 	OSStatus NewFactoryPresetSet (const AUPreset& inNewFactoryPreset) SMTG_OVERRIDE;
203 
204 	//---MusicDeviceBase-------------------------
205 	ComponentResult StartNote (MusicDeviceInstrumentID inInstrument, MusicDeviceGroupID inGroupID, NoteInstanceID* outNoteInstanceID, UInt32 inOffsetSampleFrame, const MusicDeviceNoteParams &inParams) SMTG_OVERRIDE;
206 	ComponentResult StopNote (MusicDeviceGroupID inGroupID, NoteInstanceID inNoteInstanceID, UInt32 inOffsetSampleFrame) SMTG_OVERRIDE;
207 	OSStatus GetInstrumentCount (UInt32 &outInstCount) const SMTG_OVERRIDE;
208 
209 	//---AUMIDIBase------------------------------
210 	OSStatus HandleNonNoteEvent (UInt8 status, UInt8 channel, UInt8	data1, UInt8 data2, UInt32 inStartFrame) SMTG_OVERRIDE;
211 
212 	//---custom----------------------------------
213 	void setControllerParameter (ParamID pid, ParamValue value);
214 
215 	// return for a given midiChannel the unitID and the ProgramListID
216 	bool getProgramListAndUnit (int32 midiChannel, UnitID& unitId, ProgramListID& programListId);
217 
218 	// restore preset state, add StateType "Project" to stream if loading from project
219 	ComponentResult restoreState (CFPropertyListRef inData, bool fromProject);
220 
221 	//------------------------------------------------------------------------
222 #if !CA_USE_AUDIO_PLUGIN_ONLY
223 	static ComponentResult ComponentEntryDispatch (ComponentParameters* params, AUWrapper* This);
224 #endif
225 	//------------------------------------------------------------------------
226 	static CFBundleRef gBundleRef;
227 	//------------------------------------------------------------------------
228 	DECLARE_FUNKNOWN_METHODS
229 
230 protected:
231 	//---from IComponentHandler-------------------
232 	tresult PLUGIN_API beginEdit (ParamID tag) SMTG_OVERRIDE;
233 	tresult PLUGIN_API performEdit (ParamID tag, ParamValue valueNormalized) SMTG_OVERRIDE;
234 	tresult PLUGIN_API endEdit (ParamID tag) SMTG_OVERRIDE;
235 	tresult PLUGIN_API restartComponent (int32 flags) SMTG_OVERRIDE;
236 
237 	//---from ITimerCallback----------------------
238 	void onTimer (Timer* timer) SMTG_OVERRIDE;
239 
240 	// internal helpers
getSampleRate()241 	double getSampleRate () const { return sampleRate; }
242 	void updateProcessContext ();
243 	void syncParameterValues ();
244 	void cacheParameterValues ();
245 	void clearParameterValueCache ();
246 
247 	virtual IPluginFactory* getFactory ();
248 	void loadVST3Module ();
249 	void unloadVST3Module ();
250 	bool validateChannelPair (int inChannelsIn, int inChannelsOut, const AUChannelInfo* info,
251 	                          UInt32 numChanInfo) const;
252 
253 	IAudioProcessor* audioProcessor;
254 	IEditController* editController;
255 	IMidiMapping* midiMapping;
256 
257 	Timer* timer;
258 
259 	HostProcessData processData;
260 	ParameterChanges processParamChanges;
261 	ParameterChanges outputParamChanges;
262 	ParameterChangeTransfer transferParamChanges;
263 	ParameterChangeTransfer outputParamTransfer;
264 	ProcessContext processContext;
265 	EventList eventList;
266 
267 	typedef std::map<uint32, AudioUnitParameterInfo> CachedParameterInfoMap;
268 	typedef std::map<UnitID, UnitInfo> UnitInfoMap;
269 	typedef std::vector<String> ClumpGroupVector;
270 
271 	UnitInfoMap unitInfos;
272 	ClumpGroupVector clumpGroups;
273 	CachedParameterInfoMap cachedParameterInfos;
274 	Steinberg::Base::Thread::FLock parameterCacheChanging;
275 
276 	NoteInstanceID noteCounter;
277 	double sampleRate;
278 	ParamID bypassParamID;
279 
280 	AUPreset* presets;
281 	int32 numPresets;
282 	ParamID factoryProgramChangedID;
283 
284 	bool isInstrument;
285 	bool isBypassed;
286 
287 	AUParameterListenerRef paramListenerRef;
288 	static const int32 kMaxProgramChangeParameters = 16;
289 	ParamID programChangeParameters[kMaxProgramChangeParameters]; // for each midi channel
290 
291 	int32 midiOutCount; // currently only 0 or 1 supported
292 	MIDIOutputCallbackHelper mCallbackHelper;
293 	EventList outputEvents;
294 
295 private:
296 	void buildUnitInfos (IUnitInfo* unitInfoController, UnitInfoMap& units) const;
297 };
298 
299 //------------------------------------------------------------------------
300 class AutoreleasePool
301 {
302 public:
AutoreleasePool()303 	AutoreleasePool () { ap = [[NSAutoreleasePool alloc] init]; }
~AutoreleasePool()304 	~AutoreleasePool () { [ap drain]; }
305 //------------------------------------------------------------------------
306 protected:
307 	NSAutoreleasePool* ap;
308 };
309 
310 //------------------------------------------------------------------------
311 } // namespace Vst
312 } // namespace Steinberg
313 
314 /// \endcond
315