iPlug2 - C++ Audio Plug-in Framework
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 
21 using namespace iplug;
22 
23 IPlugAUv3::IPlugAUv3(const InstanceInfo& instanceInfo, const Config& config)
24 : IPlugAPIBase(config, kAPIAUv3)
25 , IPlugProcessor(config, kAPIAUv3)
26 {
27  Trace(TRACELOC, "%s", config.pluginName);
28 }
29 
30 void IPlugAUv3::SetAUAudioUnit(void* pAUAudioUnit)
31 {
32  mAUAudioUnit = pAUAudioUnit;
33 }
34 
36 {
37  const AUParameterAddress address = GetParamAddress(paramIdx);
38  [(__bridge IPlugAUAudioUnit*) mAUAudioUnit beginInformHostOfParamChange:address];
39 }
40 
41 void IPlugAUv3::InformHostOfParamChange(int paramIdx, double normalizedValue)
42 {
43  const AUParameterAddress address = GetParamAddress(paramIdx);
44 
45  [(__bridge IPlugAUAudioUnit*) mAUAudioUnit informHostOfParamChange:address :(float) GetParam(paramIdx)->FromNormalized(normalizedValue)];
46 }
47 
49 {
50  const AUParameterAddress address = GetParamAddress(paramIdx);
51  [(__bridge IPlugAUAudioUnit*) 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 IPlugAUAudioUnit*) mAUAudioUnit sendMidiData: sampleTime : sizeof(data) : data];
61 }
62 
63 //bool IPlugAUv3::SendMidiMsgs(WDL_TypedBuf<IMidiMsg>& msgs)
64 //{
65 // return false;
66 //}
67 
68 bool IPlugAUv3::SendSysEx(const ISysEx& msg)
69 {
70  int64_t sampleTime = mLastTimeStamp.mSampleTime + msg.mOffset;
71 
72  return [(__bridge IPlugAUAudioUnit*) mAUAudioUnit sendMidiData: sampleTime : msg.mSize : msg.mData];
73 }
74 
75 //void IPlugAUv3::HandleOneEvent(AURenderEvent const *event, AUEventSampleTime startTime)
76 //{
77 // switch (event->head.eventType)
78 // {
79 // case AURenderEventParameter:
80 // case AURenderEventParameterRamp:
81 // {
82 // AUParameterEvent const& paramEvent = event->parameter;
83 // const int paramIdx = GetParamIdx(paramEvent.parameterAddress);
84 // const double value = (double) paramEvent.value;
85 // const int sampleOffset = (int) (paramEvent.eventSampleTime - startTime);
86 // ENTER_PARAMS_MUTEX
87 // GetParam(paramIdx)->Set(value);
88 // LEAVE_PARAMS_MUTEX
89 // OnParamChange(paramIdx, EParamSource::kHost, sampleOffset);
90 // break;
91 // }
92 //
93 // case AURenderEventMIDI:
94 // {
95 // IMidiMsg msg;
96 // msg.mStatus = event->MIDI.data[0];
97 // msg.mData1 = event->MIDI.data[1];
98 // msg.mData2 = event->MIDI.data[2];
99 // msg.mOffset = (int) (event->MIDI.eventSampleTime - startTime);
100 // ProcessMidiMsg(msg);
101 // mMidiMsgsFromProcessor.Push(msg);
102 // break;
103 // }
104 // default:
105 // break;
106 // }
107 //}
108 //
109 //void IPlugAUv3::PerformAllSimultaneousEvents(AUEventSampleTime now, AURenderEvent const *&event)
110 //{
111 // do {
112 // HandleOneEvent(event, now);
113 //
114 // // Go to next event.
115 // event = event->head.next;
116 //
117 // // While event is not null and is simultaneous (or late).
118 // } while (event && event->head.eventSampleTime <= now);
119 //}
120 
121 void IPlugAUv3::ProcessWithEvents(AudioTimeStamp const* pTimestamp, uint32_t frameCount, AURenderEvent const* pEvents, ITimeInfo& timeInfo)
122 {
123  SetTimeInfo(timeInfo);
124 
125  IMidiMsg midiMsg;
126  while (mMidiMsgsFromEditor.Pop(midiMsg))
127  {
128  ProcessMidiMsg(midiMsg);
129  }
130 
131  mLastTimeStamp = *pTimestamp;
132  AUEventSampleTime now = AUEventSampleTime(pTimestamp->mSampleTime);
133  uint32_t framesRemaining = frameCount;
134 
135  for (const AURenderEvent* pEvent = pEvents; pEvent != nullptr; pEvent = pEvent->head.next)
136  {
137  switch (pEvent->head.eventType)
138  {
139  case AURenderEventMIDI:
140  {
141  const AUMIDIEvent& midiEvent = pEvent->MIDI;
142 
143  midiMsg = {static_cast<int>(midiEvent.eventSampleTime - now), midiEvent.data[0], midiEvent.data[1], midiEvent.data[2] };
144  ProcessMidiMsg(midiMsg);
145  mMidiMsgsFromProcessor.Push(midiMsg);
146  }
147  break;
148 
149  case AURenderEventParameter:
150  case AURenderEventParameterRamp:
151  {
152  const AUParameterEvent& paramEvent = pEvent->parameter;
153  const int paramIdx = GetParamIdx(paramEvent.parameterAddress);
154  const double value = (double) paramEvent.value;
155  const int sampleOffset = (int) (paramEvent.eventSampleTime - now);
156  ENTER_PARAMS_MUTEX
157  GetParam(paramIdx)->Set(value);
158  LEAVE_PARAMS_MUTEX
159  OnParamChange(paramIdx, EParamSource::kHost, sampleOffset);
160  break;
161  }
162  break;
163 
164  default:
165  break;
166  }
167  }
168 
169  ENTER_PARAMS_MUTEX;
170  ProcessBuffers(0.f, framesRemaining); // what about bufferOffset
171  LEAVE_PARAMS_MUTEX;
172 
173  //Output SYSEX from the editor, which has bypassed ProcessSysEx()
174  while (mSysExDataFromEditor.Pop(mSysexBuf))
175  {
176  ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
177  SendSysEx(smsg);
178  }
179 
180 
181 // while (framesRemaining > 0) {
182 // // If there are no more events, we can process the entire remaining segment and exit.
183 // if (event == nullptr) {
185 // TODO - ProcessBuffers should be within param mutex lock
186 // ProcessBuffers(0.f, framesRemaining); // what about bufferOffset
187 // return;
188 // }
189 //
190 // // **** start late events late.
191 // auto timeZero = AUEventSampleTime(0);
192 // auto headEventTime = event->head.eventSampleTime;
193 // uint32_t const framesThisSegment = uint32_t(std::max(timeZero, headEventTime - now));
194 //
195 // // Compute everything before the next event.
196 // if (framesThisSegment > 0)
197 // {
199 // TODO - ProcessBuffers should be within param mutex lock
200 // ProcessBuffers(0.f, framesThisSegment); // what about bufferOffset
201 //
202 // // Advance frames.
203 // framesRemaining -= framesThisSegment;
204 //
205 // // Advance time.
206 // now += AUEventSampleTime(framesThisSegment);
207 // }
208 //
209 // PerformAllSimultaneousEvents(now, event);
210 // }
211 }
212 
213 // this is called on a secondary thread (not main thread, not audio thread)
214 void IPlugAUv3::SetParameterFromValueObserver(uint64_t address, float value)
215 {
216  const int paramIdx = GetParamIdx(address);
217 
218  ENTER_PARAMS_MUTEX
219  IParam* pParam = GetParam(paramIdx);
220  assert(pParam);
221  pParam->Set((double) value);
222  LEAVE_PARAMS_MUTEX
223  OnParamChange(paramIdx, kHost, -1);
224 }
225 
226 void IPlugAUv3::SendParameterValueFromObserver(uint64_t address, float value)
227 {
228  const int paramIdx = GetParamIdx(address);
229 
230  SendParameterValueFromAPI(paramIdx, value, false); // will trigger OnParamChangeUI()
231 }
232 
233 float IPlugAUv3::GetParameter(uint64_t address)
234 {
235  const int paramIdx = GetParamIdx(address);
236 
237  ENTER_PARAMS_MUTEX
238  const float val = (float) GetParam(paramIdx)->Value();
239  LEAVE_PARAMS_MUTEX
240  return val;
241 }
242 
243 const char* IPlugAUv3::GetParamDisplay(uint64_t address, float value)
244 {
245  const int paramIdx = GetParamIdx(address);
246 
247  ENTER_PARAMS_MUTEX
248  GetParam(paramIdx)->GetDisplay(value, false, mParamDisplayStr);
249  LEAVE_PARAMS_MUTEX
250  return (const char*) mParamDisplayStr.Get();
251 }
252 
253 float IPlugAUv3::GetParamStringToValue(uint64_t address, const char* str)
254 {
255  const int paramIdx = GetParamIdx(address);
256 
257  ENTER_PARAMS_MUTEX
258  float val = (float) GetParam(paramIdx)->StringToValue(str);
259  LEAVE_PARAMS_MUTEX
260  return val;
261 }
262 
263 void IPlugAUv3::SetBuffers(AudioBufferList* pInBufList, AudioBufferList* pOutBufList, uint32_t outBusNumber)
264 {
265  int chanIdx = 0;
266  if(pInBufList)
267  {
268  for(int i = 0; i < pInBufList->mNumberBuffers; i++)
269  {
270  int nConnected = pInBufList->mBuffers[i].mNumberChannels;
271  SetChannelConnections(ERoute::kInput, chanIdx, nConnected, true);
272  AttachBuffers(ERoute::kInput, chanIdx, nConnected, (float**) &(pInBufList->mBuffers[i].mData), GetBlockSize());
273  chanIdx += nConnected;
274  }
275  }
276 
277  chanIdx = 0;
278 
279  if(pOutBufList)
280  {
281  int numChannelsInBus = pOutBufList->mNumberBuffers; // TODO: this assumes all busses have the same channel count
282 
283  for(int i = 0; i < pOutBufList->mNumberBuffers; i++)
284  {
285  int nConnected = pOutBufList->mBuffers[i].mNumberChannels;
286  SetChannelConnections(ERoute::kOutput, (outBusNumber * numChannelsInBus) + chanIdx, nConnected, true);
287  AttachBuffers(ERoute::kOutput, (outBusNumber * numChannelsInBus) + chanIdx, nConnected, (float**) &(pOutBufList->mBuffers[i].mData), GetBlockSize());
288  chanIdx += nConnected;
289  }
290  }
291 }
292 
293 void IPlugAUv3::Prepare(double sampleRate, uint32_t blockSize)
294 {
295  SetBlockSize(blockSize);
296  SetSampleRate(sampleRate);
297 }
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:42
int GetBlockSize() const
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:30
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...
IPlug&#39;s parameter class.
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
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
The base class for IPlug Audio Processing.
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
AudioUnit v3 API base class for an IPlug plug-in.
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:538
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
Encapsulates information about the host transport state.
Definition: IPlugStructs.h:581