Jamoma API  0.6.0.a19
TTAudioUnit.cpp
1 /*
2  * TTAudioUnit -- AudioUnit plug-in host
3  * Extension Class for Jamoma DSP
4  * Copyright © 2008, Timothy Place
5  *
6  * License: This code is licensed under the terms of the "New BSD License"
7  * http://creativecommons.org/licenses/BSD/
8  */
9 
10 #include "TTDSP.h"
11 
12 #ifdef uint
13 #undef uint
14 #endif
15 
16 #include <CoreServices/CoreServices.h>
17 #include <AudioToolbox/AudioToolBox.h>
18 #include <AudioUnit/AudioUnit.h>
19 
20 
21 #define thisTTClass TTAudioUnit
22 
23 
24 /** An AudioUnit render callback.
25  The AudioUnit will get its audio input by calling this function. */
26 OSStatus TTAudioUnitGetInputSamples(void* inRefCon,
27  AudioUnitRenderActionFlags* ioActionFlags,
28  const AudioTimeStamp* inTimeStamp,
29  UInt32 inBusNumber,
30  UInt32 inNumberFrames,
31  AudioBufferList* ioData);
32 
33 
34 /** Host AudioUnit plug-ins. */
36 public:
37  TTSymbol mPlugin; ///< Attribute: the name of the current plugin
38  AudioUnit mAudioUnit; ///< the actual plugin
39  AudioBufferList* mInputBufferList;
40  AudioBufferList* mOutputBufferList;
41  AudioTimeStamp mTimeStamp;
42  TTHashPtr mParameterNames; ///< parameter names -> parameter ids
43  TTUInt32 mInputCount;
44  TTUInt32 mOutputCount;
45 
46  /** Constructor. */
47  TTAudioUnit(TTValue& arguments) :
48  TTAudioObjectBase(arguments),
49  mAudioUnit(NULL),
50  mInputBufferList(NULL),
51  mOutputBufferList(NULL),
52  mInputCount(0),
53  mOutputCount(0)
54  {
57  addAttributeWithSetter(OutputCount, kTypeUInt32);
58 
59  addMessageWithArguments(getPluginNames);
60  addMessageWithArguments(getParameterNames);
61  addMessageWithArguments(getPresetNames);
62 
63  addMessageWithArguments(setParameter);
64  addMessageWithArguments(getParameter);
65  addMessageWithArguments(recallPreset);
66 
67  addUpdates(MaxNumChannels);
68 
69  mParameterNames = new TTHash;
70  mTimeStamp.mSampleTime = 0;
71  mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
72 
73  setAttributeValue(TT("inputCount"), 1);
74  setAttributeValue(TT("outputCount"), 1);
75  setAttributeValue(TT("plugin"), TT("AULowpass"));
77  }
78 
79 
80  /** Destructor. */
82  {
83  if (mAudioUnit) {
84  AudioUnitUninitialize(mAudioUnit);
85  CloseComponent(mAudioUnit);
86  mAudioUnit = NULL;
87  }
88  free(mInputBufferList);
89  free(mOutputBufferList);
90  delete mParameterNames;
91  }
92 
93 
94  TTErr updateMaxNumChannels(TTValueConstRef oldMaxNumChannels, TTValueRef)
95  {
96  if (mInputBufferList)
97  free(mInputBufferList);
98  if (mOutputBufferList)
99  free(mOutputBufferList);
100 
101  // inputBufferList = (AudioBufferList*)malloc(offsetof(AudioBufferList, mBuffers[maxNumChannels]));
102  // outputBufferList = (AudioBufferList*)malloc(offsetof(AudioBufferList, mBuffers[maxNumChannels]));
103  mInputBufferList = (AudioBufferList*)malloc(offsetof(AudioBufferList, mBuffers) + (mMaxNumChannels * sizeof(AudioBuffer)));
104  mOutputBufferList = (AudioBufferList*)malloc(offsetof(AudioBufferList, mBuffers) + (mMaxNumChannels * sizeof(AudioBuffer)));
105 
106  for (TTUInt16 channel=0; channel<mMaxNumChannels; channel++) {
107  mInputBufferList->mBuffers[channel].mNumberChannels = 1;
108  mInputBufferList->mBuffers[channel].mData = NULL; // We will set this pointer in the process method
109  mInputBufferList->mBuffers[channel].mDataByteSize = 0;
110  mOutputBufferList->mBuffers[channel].mNumberChannels = 1;
111  mOutputBufferList->mBuffers[channel].mData = NULL; // Tell the AU to deal with the memory
112  mOutputBufferList->mBuffers[channel].mDataByteSize = 0;
113  }
114  return kTTErrNone;
115  }
116 
117 
118  TTErr setInputCount(TTValueConstRef aValue, TTValueRef)
119  {
120  TTUInt32 newInputCount = aValue;
121 
122  if (newInputCount != mInputCount) {
123  mInputCount = newInputCount;
124  // TODO: NEED TO RE-INIT PLUGIN HERE!
125  }
126  return kTTErrNone;
127  }
128 
129 
130  TTErr setOutputCount(TTValueConstRef aValue, TTValueRef)
131  {
132  TTUInt32 newOutputCount = aValue;
133 
134  if (newOutputCount != mOutputCount) {
135  mOutputCount = newOutputCount;
136  // TODO: NEED TO RE-INIT PLUGIN HERE!
137  }
138  return kTTErrNone;
139  }
140 
141 
142  TTErr getPluginNames(TTValueConstRef, TTValueRef pluginNames)
143  {
144  ComponentDescription searchDesc;
145  Component comp = NULL;
146  ComponentDescription compDesc;
147  Handle compName;
148  char* compNameStr;
149  int compNameLen;
150 
151  pluginNames.clear();
152 
153  searchDesc.componentType = kAudioUnitType_Effect; // TODO: support other types
154  searchDesc.componentSubType = 0; // kAudioUnitSubType_DefaultOutput;
155  searchDesc.componentManufacturer = 0; //kAudioUnitManufacturer_Apple;
156  searchDesc.componentFlags = 0;
157  searchDesc.componentFlagsMask = 0;
158 
159  while (comp = FindNextComponent(comp, &searchDesc)) {
160  compName = NewHandle(0);
161  GetComponentInfo(comp, &compDesc, compName, NULL, NULL);
162  HLock(compName);
163  compNameStr = *compName;
164  compNameLen = *compNameStr++;
165  compNameStr[compNameLen] = 0;
166  compNameStr = strchr(compNameStr, ':');
167  compNameStr++;
168  compNameStr++;
169 
170  pluginNames.append(TT(compNameStr));
171 
172  HUnlock(compName);
173  DisposeHandle(compName);
174  }
175  return kTTErrNone;
176  }
177 
178 
179  TTErr setPlugin(TTValue& newPluginName)
180  {
181  ComponentDescription searchDesc;
182  Component comp = NULL;
183  ComponentDescription compDesc;
184  Handle compName;
185  char* compNameStr;
186  int compNameLen;
187  TTSymbol pluginName = newPluginName;
188  TTUInt32 dataSizeDontCare;
189  AudioStreamBasicDescription audioStreamBasicDescription;
190  OSStatus result;
191 
192  if (mAudioUnit) {
193  AudioUnitUninitialize(mAudioUnit);
194  CloseComponent(mAudioUnit);
195  mAudioUnit = NULL;
196  }
197 
198  searchDesc.componentType = kAudioUnitType_Effect; // TODO: support other types
199  searchDesc.componentSubType = 0; // kAudioUnitSubType_DefaultOutput;
200  searchDesc.componentManufacturer = 0; //kAudioUnitManufacturer_Apple;
201  searchDesc.componentFlags = 0;
202  searchDesc.componentFlagsMask = 0;
203 
204  while (comp = FindNextComponent(comp, &searchDesc)) {
205  compName = NewHandle(0);
206  GetComponentInfo(comp, &compDesc, compName, NULL, NULL);
207  HLock(compName);
208  compNameStr = *compName;
209  compNameLen = *compNameStr++;
210  compNameStr[compNameLen] = 0;
211  compNameStr = strchr(compNameStr, ':');
212  compNameStr++;
213  compNameStr++;
214 
215  if (!strcmp(compNameStr, pluginName)) {
216  AURenderCallbackStruct callbackStruct;
217 
218  mAudioUnit = OpenComponent(comp);
219  mPlugin = pluginName;
220 
221  stuffParameterNamesIntoHash();
222 
223  HUnlock(compName);
224  DisposeHandle(compName);
225 
226  // plugin is loaded, now activate it
227  callbackStruct.inputProc = &TTAudioUnitGetInputSamples;
228  callbackStruct.inputProcRefCon = this;
229  AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callbackStruct, sizeof(AURenderCallbackStruct));
230  AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Global, 0, &sr, sizeof(sr));
231 
232 
233  // configure for channels in and out
234  dataSizeDontCare = sizeof(audioStreamBasicDescription);
235  result = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioStreamBasicDescription, &dataSizeDontCare);
236  if (!result) {
237  audioStreamBasicDescription.mChannelsPerFrame = mInputCount;
238  result = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioStreamBasicDescription, sizeof(audioStreamBasicDescription));
239  }
240  dataSizeDontCare = sizeof(audioStreamBasicDescription);
241  result = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &audioStreamBasicDescription, &dataSizeDontCare);
242  if (!result) {
243  audioStreamBasicDescription.mChannelsPerFrame = mOutputCount;
244  result = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &audioStreamBasicDescription, sizeof(audioStreamBasicDescription));
245  }
246 
247  //
248  AudioUnitInitialize(mAudioUnit);
249 
250  return kTTErrNone;
251  }
252 
253  HUnlock(compName);
254  DisposeHandle(compName);
255  }
256  return kTTErrGeneric;
257  }
258 
259 
260  void stuffParameterNamesIntoHash()
261  {
262  UInt32 size = 0;
263  Boolean writable = false;
264  OSStatus err = noErr;
265  AudioUnitParameterID* parameterArray = NULL;
266 
267  mParameterNames->clear();
268 
269  err = AudioUnitGetPropertyInfo(mAudioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, &size, &writable);
270  if (err || size == 0)
271  return;
272 
273  parameterArray = (AudioUnitParameterID*)malloc(size);
274  err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, parameterArray, &size);
275  if (err)
276  goto out;
277 
278  for (UInt32 paramNumber = 0; paramNumber < size/sizeof(AudioUnitParameterID); paramNumber++) {
279  AudioUnitParameterInfo info;
280  UInt32 infoSize = sizeof(AudioUnitParameterInfo);
281  char parameterName[256];
282 
283  err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, parameterArray[paramNumber], &info, &infoSize);
284  if (!err) {
285  CFStringGetCString(info.cfNameString, parameterName, 256, kCFStringEncodingUTF8);
286  mParameterNames->append(TT(parameterName), paramNumber);
287  }
288  }
289 
290  out:
291  free(parameterArray);
292  }
293 
294 
295  TTErr getParameterNames(TTValueConstRef, TTValueRef returnedParameterNames)
296  {
297  return mParameterNames->getKeys(returnedParameterNames);
298  }
299 
300 
301  TTErr setParameter(TTValueConstRef nameAndValue, TTValueRef)
302  {
303  TTSymbol parameterName;
304  TTFloat32 parameterValue;
305  TTValue v;
306  TTErr err;
307 
308  if (nameAndValue.getSize() != 2) {
309  logError("Bad arguments for setParameter()");
310  return kTTErrGeneric;
311  }
312 
313  nameAndValue.get(0, parameterName);
314  nameAndValue.get(1, parameterValue);
315  err = mParameterNames->lookup(parameterName, v);
316  if (!err)
317  AudioUnitSetParameter(mAudioUnit, v, kAudioUnitScope_Global, 0, parameterValue, 0);
318  return err;
319  }
320 
321 
322  TTErr getParameter(TTValueConstRef nameIn, TTValueRef valueOut)
323  {
324  TTSymbol parameterName = nameIn;
325  TTValue v;
326  TTErr err;
327  long parameterID = -1;
328  Float32 parameterValue = 0.0;
329 
330  err = mParameterNames->lookup(parameterName, v);
331  if (!err) {
332  parameterID = v;
333  AudioUnitGetParameter(mAudioUnit, parameterID, kAudioUnitScope_Global, 0, &parameterValue);
334  valueOut = parameterValue;
335  }
336  return err;
337  }
338 
339 
340  TTErr getPresetNames(TTValueConstRef, TTValueRef returnedPresetNames)
341  {
342  CFArrayRef factoryPresets = NULL;
343  UInt32 size = sizeof(CFArrayRef);
344  OSStatus err = noErr;
345  CFIndex count;
346 
347  returnedPresetNames.clear();
348 
349  err = AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_FactoryPresets, kAudioUnitScope_Global, 0, &factoryPresets, &size);
350  if (err)
351  return kTTErrGeneric;
352 
353  count = CFArrayGetCount(factoryPresets);
354  for (int i=0; i<count; i++) {
355  const AUPreset* preset = (const AUPreset*)CFArrayGetValueAtIndex(factoryPresets, i);
356  char presetName[256];
357 
358  CFStringGetCString(preset->presetName, presetName, 256, kCFStringEncodingUTF8);
359  returnedPresetNames.append(TT(presetName));
360  }
361  CFRelease(factoryPresets);
362  return kTTErrNone;
363  }
364 
365 
366  // We could also keep a hash of factory presets and allow a symbol to set the preset by name at some point...
367  TTErr recallPreset(TTValueConstRef presetNumber, TTValueRef)
368  {
369  AUPreset presetInfo;
370  OSStatus err = noErr;
371 
372  presetInfo.presetNumber = presetNumber;
373  err = AudioUnitSetProperty(mAudioUnit, kAudioUnitProperty_CurrentPreset, kAudioUnitScope_Global, 0, &presetInfo, sizeof(AUPreset));
374  return TTErr(err);
375  }
376 
377 
378 
379  /** Audio Processing Method */
381  {
382  TTAudioSignal& in = inputs->getSignal(0);
383  TTAudioSignal& out = outputs->getSignal(0);
384  TTUInt16 vs = in.getVectorSizeAsInt();
385  TTUInt16 numInputChannels = in.getNumChannelsAsInt();
386  TTUInt16 numOutputChannels = out.getNumChannelsAsInt();
387 // TTFloat32* auInput[numInputChannels];
388  TTFloat32* auOutput;//[numOutputChannels];
389  AudioUnitRenderActionFlags ioActionFlags = 0;
390 
391  // prepare the input
392  for (TTUInt16 channel=0; channel<numInputChannels; channel++) {
393  TTUInt32 dataByteSize = sizeof(TTFloat32) * vs;
394 
395  if (mInputBufferList->mBuffers[channel].mDataByteSize != dataByteSize) {
396  if (mInputBufferList->mBuffers[channel].mDataByteSize)
397  TTFree16(mInputBufferList->mBuffers[channel].mData);
398  mInputBufferList->mBuffers[channel].mData = TTMalloc16(dataByteSize);
399  mInputBufferList->mBuffers[channel].mDataByteSize = dataByteSize;
400  }
401 
402  in.getVector(channel, vs, (TTFloat32*)mInputBufferList->mBuffers[channel].mData);
403 // mInputBufferList->mBuffers[channel].mDataByteSize = sizeof(TTFloat32) * vs;
404  mOutputBufferList->mBuffers[channel].mDataByteSize = sizeof(TTFloat32) * vs;
405  }
406  mInputBufferList->mNumberBuffers = numInputChannels;
407  mOutputBufferList->mNumberBuffers = numOutputChannels;
408 
409  // render the output using the plugin
410  AudioUnitRender(mAudioUnit, &ioActionFlags, &mTimeStamp, 0, vs, mOutputBufferList);
411 
412  // handle the output
413  numOutputChannels = mOutputBufferList->mNumberBuffers;
414  for (TTUInt16 channel=0; channel<numOutputChannels; channel++) {
415  auOutput = (TTFloat32*)mOutputBufferList->mBuffers[channel].mData;
416  out.setVector(channel, vs, auOutput);
417  }
418 
419  mTimeStamp.mSampleTime += vs;
420  return kTTErrNone;
421  }
422 
423 };
424 
425 
426 
427 // Implemention of the AU render callback.
428 OSStatus TTAudioUnitGetInputSamples(void* inRefCon,
429  AudioUnitRenderActionFlags* ioActionFlags,
430  const AudioTimeStamp* inTimeStamp,
431  UInt32 inBusNumber,
432  UInt32 inNumberFrames,
433  AudioBufferList* ioData)
434 {
435  TTAudioUnit* ttAudioUnit = (TTAudioUnit*)inRefCon;
436 
437  for (TTUInt16 channel=0; channel < ioData->mNumberBuffers; channel++)
438  memcpy(ioData->mBuffers[channel].mData, ttAudioUnit->mInputBufferList->mBuffers[channel].mData, sizeof(TTFloat32) * inNumberFrames);
439  return noErr;
440 }
441 
442 
443 
444 TT_AUDIO_CLASS_SETUP("audiounit", "audio, processor", TTAudioUnit);
445 
~TTAudioUnit()
Destructor.
Definition: TTAudioUnit.cpp:81
std::uint16_t TTUInt16
16 bit unsigned integer
Definition: TTBase.h:176
void TTFOUNDATION_EXPORT TTFree16(TTPtr ptr)
Free memory allocated using TTMalloc16().
Definition: TTBase.cpp:647
TTUInt16 getSize() const
DEPRECATED.
Definition: TTValue.h:521
TTErr lookup(const TTSymbol key, TTValue &value)
Find the value for the given key.
Definition: TTHash.cpp:76
TTAudioObjectBase is the base class for all audio generating and processing objects in Jamoma DSP...
TTChannelCount mMaxNumChannels
This is the maximum number of channels that can be guaranteed to work.
TTAudioUnit(TTValue &arguments)
Constructor.
Definition: TTAudioUnit.cpp:47
TTErr logError(TTImmutableCString fmtstring,...)
Log errors scoped to this object instance.
TTPtr TTFOUNDATION_EXPORT TTMalloc16(size_t numBytes)
Allocate memory from the heap aligned to 16-byte boundaries.
Definition: TTBase.cpp:641
#define setProcessMethod(methodName)
A convenience macro to be used by subclasses for setting the process method.
TTErr getKeys(TTValue &hashKeys)
Get an array of all of the keys for the hash table.
Definition: TTHash.cpp:126
Host AudioUnit plug-ins.
Definition: TTAudioUnit.cpp:35
AudioUnit mAudioUnit
the actual plugin
Definition: TTAudioUnit.cpp:38
Jamoma DSP Library.
TTErr setAttributeValue(const TTSymbol name, TTValue &value)
Set an attribute value for an object.
Maintain a collection of TTValue objects indexed by TTSymbol pointers.
Definition: TTHash.h:36
Symbol type.
Definition: TTBase.h:282
TTErr processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
Audio Processing Method.
#define TT
This macro is defined as a shortcut for doing a lookup in the symbol table.
Definition: TTSymbol.h:155
void append(const T &anElementValueToAppend)
Insert a single TTElement at the end.
Definition: TTValue.h:243
TTErr setVector(const TTChannelCount channel, const TTUInt16 vectorSize, const TTSampleValuePtr newVector)
[doxygenAppendixC_methodExample]
TTHashPtr mParameterNames
parameter names -> parameter ids
Definition: TTAudioUnit.cpp:42
The TTSymbol class is used to represent a string and efficiently pass and compare that string...
Definition: TTSymbol.h:26
TTErr append(const TTSymbol key, const TTValue &value)
Insert an item into the hash table.
Definition: TTHash.cpp:70
float TTFloat32
32 bit floating point number
Definition: TTBase.h:187
void get(const TTUInt16 index, T &returnedElementValue) const
DEPRECATED.
Definition: TTValue.h:591
The TTAudioSignal class represents N vectors of audio samples for M channels.
Definition: TTAudioSignal.h:57
#define addMessageWithArguments(name)
A convenience macro to be used by subclasses for registering messages.
Definition: TTMessage.h:27
TTErr clear()
Remove all items from the hash table.
Definition: TTHash.cpp:117
A simple container for an array of TTAudioSignal pointers.
void clear()
Clear all values from the vector, leaving with size of 0.
Definition: TTValue.h:131
Something went wrong, but what exactly is not known. Typically used for context-specific problems...
Definition: TTBase.h:344
TTErr
Jamoma Error Codes Enumeration of error codes that might be returned by any of the TTBlue functions a...
Definition: TTBase.h:342
std::uint32_t TTUInt32
32 bit unsigned integer
Definition: TTBase.h:178
TTSymbol mPlugin
Attribute: the name of the current plugin.
Definition: TTAudioUnit.cpp:37
#define addAttributeWithSetter(name, type)
A convenience macro to be used by subclasses for registering attributes with a custom setter...
Definition: TTAttribute.h:47
32-bit unsigned integer, range is 0 through 4,294,967,295.
Definition: TTBase.h:278
No Error.
Definition: TTBase.h:343
[doxygenAppendixC_copyExample]
Definition: TTValue.h:34
#define TT_AUDIO_CLASS_SETUP(strname, tags, className)
A macro for setting up the class binding to the library in extension classes.
Definition: TTDSP.h:81
#define addUpdates(updateName)
An 'update' is a message sent to a subclass instance from its parent class.
Definition: TTMessage.h:44
TTUInt32 sr
Current sample rate being used by this object.