iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugAUv3.mm
1 /*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#import <AudioToolbox/AudioToolbox.h>
12#include <CoreMIDI/CoreMIDI.h>
13
14#include "IPlugAUv3.h"
15#import "IPlugAUAudioUnit.h"
16
17#if !__has_feature(objc_arc)
18#error This file must be compiled with Arc. Use -fobjc-arc flag
19#endif
20
21using namespace iplug;
22
23IPlugAUv3::IPlugAUv3(const InstanceInfo& instanceInfo, const Config& config)
24: IPlugAPIBase(config, kAPIAUv3)
25, IPlugProcessor(config, kAPIAUv3)
26{
27 Trace(TRACELOC, "%s", config.pluginName);
28}
29
30void IPlugAUv3::SetAUAudioUnit(void* pAUAudioUnit)
31{
32 mAUAudioUnit = pAUAudioUnit;
33}
34
36{
37 const AUParameterAddress address = GetParamAddress(paramIdx);
38 [(__bridge IPLUG_AUAUDIOUNIT*) mAUAudioUnit beginInformHostOfParamChange:address];
39}
40
41void IPlugAUv3::InformHostOfParamChange(int paramIdx, double normalizedValue)
42{
43 const AUParameterAddress address = GetParamAddress(paramIdx);
44
45 [(__bridge IPLUG_AUAUDIOUNIT*) mAUAudioUnit informHostOfParamChange:address :(float) GetParam(paramIdx)->FromNormalized(normalizedValue)];
46}
47
49{
50 const AUParameterAddress address = GetParamAddress(paramIdx);
51 [(__bridge IPLUG_AUAUDIOUNIT*) mAUAudioUnit endInformHostOfParamChange:address];
52}
53
55{
56 uint8_t data[3] = { msg.mStatus, msg.mData1, msg.mData2 };
57
58 int64_t sampleTime = mLastTimeStamp.mSampleTime + msg.mOffset;
59
60 return [(__bridge IPLUG_AUAUDIOUNIT*) mAUAudioUnit sendMidiData: sampleTime : sizeof(data) : data];
61}
62
63//bool IPlugAUv3::SendMidiMsgs(WDL_TypedBuf<IMidiMsg>& msgs)
64//{
65// return false;
66//}
67
69{
70 int64_t sampleTime = mLastTimeStamp.mSampleTime + msg.mOffset;
71
72 return [(__bridge IPLUG_AUAUDIOUNIT*) mAUAudioUnit sendMidiData: sampleTime : msg.mSize : msg.mData];
73}
74
75bool IPlugAUv3::EditorResize(int viewWidth, int viewHeight)
76{
77 // AUv3 handles host-initiated resizing through NSLayoutConstraints in the view controller.
78 // Plugin-initiated resizing is not supported on macOS AUv3 - hosts ignore preferredContentSize
79 // changes after initial setup. Always return false to indicate resize was not performed here.
80 return false;
81}
82
83//void IPlugAUv3::HandleOneEvent(AURenderEvent const *event, AUEventSampleTime startTime)
84//{
85// switch (event->head.eventType)
86// {
87// case AURenderEventParameter:
88// case AURenderEventParameterRamp:
89// {
90// AUParameterEvent const& paramEvent = event->parameter;
91// const int paramIdx = GetParamIdx(paramEvent.parameterAddress);
92// const double value = (double) paramEvent.value;
93// const int sampleOffset = (int) (paramEvent.eventSampleTime - startTime);
94// ENTER_PARAMS_MUTEX
95// GetParam(paramIdx)->Set(value);
96// LEAVE_PARAMS_MUTEX
97// OnParamChange(paramIdx, EParamSource::kHost, sampleOffset);
98// break;
99// }
100//
101// case AURenderEventMIDI:
102// {
103// IMidiMsg msg;
104// msg.mStatus = event->MIDI.data[0];
105// msg.mData1 = event->MIDI.data[1];
106// msg.mData2 = event->MIDI.data[2];
107// msg.mOffset = (int) (event->MIDI.eventSampleTime - startTime);
108// ProcessMidiMsg(msg);
109// mMidiMsgsFromProcessor.Push(msg);
110// break;
111// }
112// default:
113// break;
114// }
115//}
116//
117//void IPlugAUv3::PerformAllSimultaneousEvents(AUEventSampleTime now, AURenderEvent const *&event)
118//{
119// do {
120// HandleOneEvent(event, now);
121//
122// // Go to next event.
123// event = event->head.next;
124//
125// // While event is not null and is simultaneous (or late).
126// } while (event && event->head.eventSampleTime <= now);
127//}
128
129void IPlugAUv3::ProcessWithEvents(AudioTimeStamp const* pTimestamp, uint32_t frameCount, AURenderEvent const* pEvents, ITimeInfo& timeInfo)
130{
131 SetTimeInfo(timeInfo);
132
133 IMidiMsg midiMsg;
134 while (mMidiMsgsFromEditor.Pop(midiMsg))
135 {
136 ProcessMidiMsg(midiMsg);
137 }
138
139 mLastTimeStamp = *pTimestamp;
140 AUEventSampleTime now = AUEventSampleTime(pTimestamp->mSampleTime);
141 uint32_t framesRemaining = frameCount;
142
143 for (const AURenderEvent* pEvent = pEvents; pEvent != nullptr; pEvent = pEvent->head.next)
144 {
145 switch (pEvent->head.eventType)
146 {
147 case AURenderEventMIDI:
148 {
149 const AUMIDIEvent& midiEvent = pEvent->MIDI;
150
151 midiMsg = {static_cast<int>(midiEvent.eventSampleTime - now), midiEvent.data[0], midiEvent.data[1], midiEvent.data[2] };
152 ProcessMidiMsg(midiMsg);
153 mMidiMsgsFromProcessor.Push(midiMsg);
154 }
155 break;
156
157 case AURenderEventParameter:
158 case AURenderEventParameterRamp:
159 {
160 const AUParameterEvent& paramEvent = pEvent->parameter;
161
162 if (paramEvent.parameterAddress < NParams())
163 {
164 const int paramIdx = GetParamIdx(paramEvent.parameterAddress);
165
166 const double value = (double) paramEvent.value;
167 const int sampleOffset = (int) (paramEvent.eventSampleTime - now);
168 ENTER_PARAMS_MUTEX
169 GetParam(paramIdx)->Set(value);
170 LEAVE_PARAMS_MUTEX
171 OnParamChange(paramIdx, EParamSource::kHost, sampleOffset);
172 }
173
174 break;
175 }
176 break;
177
178 default:
179 break;
180 }
181 }
182
183 ENTER_PARAMS_MUTEX;
184 ProcessBuffers(0.f, framesRemaining); // what about bufferOffset
185 LEAVE_PARAMS_MUTEX;
186
187 //Output SYSEX from the editor, which has bypassed ProcessSysEx()
188 while (mSysExDataFromEditor.Pop(mSysexBuf))
189 {
190 ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
191 SendSysEx(smsg);
192 }
193
194
195// while (framesRemaining > 0) {
196// // If there are no more events, we can process the entire remaining segment and exit.
197// if (event == nullptr) {
199// TODO - ProcessBuffers should be within param mutex lock
200// ProcessBuffers(0.f, framesRemaining); // what about bufferOffset
201// return;
202// }
203//
204// // **** start late events late.
205// auto timeZero = AUEventSampleTime(0);
206// auto headEventTime = event->head.eventSampleTime;
207// uint32_t const framesThisSegment = uint32_t(std::max(timeZero, headEventTime - now));
208//
209// // Compute everything before the next event.
210// if (framesThisSegment > 0)
211// {
213// TODO - ProcessBuffers should be within param mutex lock
214// ProcessBuffers(0.f, framesThisSegment); // what about bufferOffset
215//
216// // Advance frames.
217// framesRemaining -= framesThisSegment;
218//
219// // Advance time.
220// now += AUEventSampleTime(framesThisSegment);
221// }
222//
223// PerformAllSimultaneousEvents(now, event);
224// }
225}
226
227// this is called on a secondary thread (not main thread, not audio thread)
228void IPlugAUv3::SetParameterFromValueObserver(uint64_t address, float value)
229{
230 if (address < NParams())
231 {
232 const int paramIdx = GetParamIdx(address);
233
234 ENTER_PARAMS_MUTEX
235 IParam* pParam = GetParam(paramIdx);
236 assert(pParam);
237 pParam->Set((double) value);
238 LEAVE_PARAMS_MUTEX
239 OnParamChange(paramIdx, kHost, -1);
240 }
241}
242
243void IPlugAUv3::SendParameterValueFromObserver(uint64_t address, float value)
244{
245 if (address < NParams())
246 {
247 const int paramIdx = GetParamIdx(address);
248
249 SendParameterValueFromAPI(paramIdx, value, false); // will trigger OnParamChangeUI()
250 }
251}
252
253float IPlugAUv3::GetParameter(uint64_t address)
254{
255 const int paramIdx = GetParamIdx(address);
256
257 ENTER_PARAMS_MUTEX
258 const float val = (float) GetParam(paramIdx)->Value();
259 LEAVE_PARAMS_MUTEX
260 return val;
261}
262
263const char* IPlugAUv3::GetParamDisplay(uint64_t address, float value)
264{
265 const int paramIdx = GetParamIdx(address);
266
267 ENTER_PARAMS_MUTEX
268 GetParam(paramIdx)->GetDisplay(value, false, mParamDisplayStr);
269 LEAVE_PARAMS_MUTEX
270 return (const char*) mParamDisplayStr.Get();
271}
272
273float IPlugAUv3::GetParamStringToValue(uint64_t address, const char* str)
274{
275 const int paramIdx = GetParamIdx(address);
276
277 ENTER_PARAMS_MUTEX
278 float val = (float) GetParam(paramIdx)->StringToValue(str);
279 LEAVE_PARAMS_MUTEX
280 return val;
281}
282
283void IPlugAUv3::AttachInputBuffers(AudioBufferList* pInBufList)
284{
285 int chanIdx = 0;
286
287 if (pInBufList)
288 {
289 for (int i = 0; i < pInBufList->mNumberBuffers; i++)
290 {
291 int nConnected = pInBufList->mBuffers[i].mNumberChannels;
292 SetChannelConnections(ERoute::kInput, chanIdx, nConnected, true);
293 AttachBuffers(ERoute::kInput, chanIdx, nConnected, (float**) &(pInBufList->mBuffers[i].mData), GetBlockSize());
294 chanIdx += nConnected;
295 }
296
297 SetChannelConnections(ERoute::kInput, chanIdx, MaxNChannels(kInput) - chanIdx, false);
298 }
299}
300
301void IPlugAUv3::AttachOutputBuffers(AudioBufferList* pOutBufList, uint32_t busNumber)
302{
303 int chanIdx = 0;
304
305 if (pOutBufList)
306 {
307 int numChannelsInBus = pOutBufList->mNumberBuffers; // TODO: this assumes all busses have the same channel count
308
309 for (int i = 0; i < pOutBufList->mNumberBuffers; i++)
310 {
311 int nConnected = pOutBufList->mBuffers[i].mNumberChannels;
312 SetChannelConnections(ERoute::kOutput, (busNumber * numChannelsInBus) + chanIdx, nConnected, true);
313 AttachBuffers(ERoute::kOutput, (busNumber * numChannelsInBus) + chanIdx, nConnected, (float**) &(pOutBufList->mBuffers[i].mData), GetBlockSize());
314 chanIdx += nConnected;
315 }
316 SetChannelConnections(ERoute::kInput, chanIdx, MaxNChannels(kOutput) - chanIdx, false);
317 }
318}
319
320void IPlugAUv3::Prepare(double sampleRate, uint32_t blockSize)
321{
322 SetChannelConnections(ERoute::kInput, 0, MaxNChannels(ERoute::kInput), false);
323 SetChannelConnections(ERoute::kOutput, 0, MaxNChannels(ERoute::kOutput), false);
324 SetBlockSize(blockSize);
325 SetSampleRate(sampleRate);
326}
IPlug's parameter class.
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:43
virtual void SendParameterValueFromAPI(int paramIdx, double value, bool normalized)
This is called from the plug-in API class in order to update UI controls linked to plug-in parameters...
void InformHostOfParamChange(int idx, double normalizedValue) override
Implemented by the API class, called by the UI via SetParameterValue() with the value of a parameter ...
Definition: IPlugAUv3.mm:41
bool SendSysEx(const ISysEx &msg) override
Send a single MIDI System Exclusive (SysEx) message // TODO: info about what thread should this be ca...
Definition: IPlugAUv3.mm:68
bool SendMidiMsg(const IMidiMsg &msg) override
Send a single MIDI message // TODO: info about what thread should this be called on or not called on!
Definition: IPlugAUv3.mm:54
bool EditorResize(int viewWidth, int viewHeight) override
Implementations call into the APIs resize hooks returns a bool to indicate whether the DAW or plugin ...
Definition: IPlugAUv3.mm:75
void BeginInformHostOfParamChange(int idx) override
Implemented by the API class, called by the UI (or by a delegate) at the beginning of a parameter cha...
Definition: IPlugAUv3.mm:35
void EndInformHostOfParamChange(int idx) override
Implemented by the API class, called by the UI (or by a delegate) at the end of a parameter change ge...
Definition: IPlugAUv3.mm:48
The base class for IPlug Audio Processing.
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
int GetBlockSize() const
int MaxNChannels(ERoute direction) const
Encapsulates information about the host transport state.
Definition: IPlugStructs.h:584
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:539