Jamoma API  0.6.0.a19
AUScopeElement.cpp
1 /* Copyright 2007 Apple Inc. All Rights Reserved.
2 
3  Disclaimer: IMPORTANT: This Apple software is supplied to you by
4  Apple Inc. ("Apple") in consideration of your agreement to the
5  following terms, and your use, installation, modification or
6  redistribution of this Apple software constitutes acceptance of these
7  terms. If you do not agree with these terms, please do not use,
8  install, modify or redistribute this Apple software.
9 
10  In consideration of your agreement to abide by the following terms, and
11  subject to these terms, Apple grants you a personal, non-exclusive
12  license, under Apple's copyrights in this original Apple software (the
13  "Apple Software"), to use, reproduce, modify and redistribute the Apple
14  Software, with or without modifications, in source and/or binary forms;
15  provided that if you redistribute the Apple Software in its entirety and
16  without modifications, you must retain this notice and the following
17  text and disclaimers in all such redistributions of the Apple Software.
18  Neither the name, trademarks, service marks or logos of Apple Inc.
19  may be used to endorse or promote products derived from the Apple
20  Software without specific prior written permission from Apple. Except
21  as expressly stated in this notice, no other rights or licenses, express
22  or implied, are granted by Apple herein, including but not limited to
23  any patent rights that may be infringed by your derivative works or by
24  other works in which the Apple Software may be incorporated.
25 
26  The Apple Software is provided by Apple on an "AS IS" basis. APPLE
27  MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
28  THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
29  FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
30  OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
31 
32  IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
33  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
36  MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
37  AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
38  STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
39  POSSIBILITY OF SUCH DAMAGE.
40 */
41 #include "AUScopeElement.h"
42 #include "AUBase.h"
43 
44 //_____________________________________________________________________________
45 //
46 // By default, parameterIDs may be arbitrarily spaced, and an STL map
47 // will be used for access. Calling UseIndexedParameters() will
48 // instead use an STL vector for faster indexed access.
49 // This assumes the paramIDs are numbered 0.....inNumberOfParameters-1
50 // Call this before defining/adding any parameters with SetParameter()
51 //
52 void AUElement::UseIndexedParameters(int inNumberOfParameters)
53 {
54  mIndexedParameters.resize (inNumberOfParameters);
55  mUseIndexedParameters = true;
56 }
57 
58 //_____________________________________________________________________________
59 //
60 // Helper method.
61 // returns the ParameterMapEvent object associated with the paramID
62 //
63 inline ParameterMapEvent& AUElement::GetParamEvent(AudioUnitParameterID paramID)
64 {
65  ParameterMapEvent *event;
66 
67  if(mUseIndexedParameters)
68  {
69  if(paramID >= mIndexedParameters.size() )
70  COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
71 
72  event = &mIndexedParameters[paramID];
73  }
74  else
75  {
76  ParameterMap::iterator i = mParameters.find(paramID);
77  if (i == mParameters.end())
78  COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
79 
80  event = &(*i).second;
81  }
82 
83  return *event;
84 }
85 
86 //_____________________________________________________________________________
87 //
88 // caller assumes that this is actually an immediate parameter
89 //
90 AudioUnitParameterValue AUElement::GetParameter(AudioUnitParameterID paramID)
91 {
92  ParameterMapEvent &event = GetParamEvent(paramID);
93 
94  return event.GetValue();
95 }
96 
97 
98 //_____________________________________________________________________________
99 //
100 void AUElement::GetRampSliceStartEnd( AudioUnitParameterID paramID,
101  AudioUnitParameterValue & outStartValue,
102  AudioUnitParameterValue & outEndValue,
103  AudioUnitParameterValue & outValuePerFrameDelta )
104 
105 {
106  ParameterMapEvent &event = GetParamEvent(paramID);
107 
108  // works even if the value is constant (immediate parameter value)
109  event.GetRampSliceStartEnd(outStartValue, outEndValue, outValuePerFrameDelta );
110 }
111 
112 //_____________________________________________________________________________
113 //
114 AudioUnitParameterValue AUElement::GetEndValue( AudioUnitParameterID paramID)
115 
116 {
117  ParameterMapEvent &event = GetParamEvent(paramID);
118 
119  // works even if the value is constant (immediate parameter value)
120  return event.GetEndValue();
121 }
122 
123 //_____________________________________________________________________________
124 //
125 void AUElement::SetParameter(AudioUnitParameterID paramID, AudioUnitParameterValue inValue, bool okWhenInitialized)
126 {
127  if(mUseIndexedParameters)
128  {
129  ParameterMapEvent &event = GetParamEvent(paramID);
130  event.SetValue(inValue);
131  }
132  else
133  {
134  ParameterMap::iterator i = mParameters.find(paramID);
135 
136  if (i == mParameters.end())
137  {
138  if (mAudioUnit->IsInitialized() && !okWhenInitialized) {
139  // The AU should not be creating new parameters once initialized.
140  // If a client tries to set an undefined parameter, we could throw as follows,
141  // but this might cause a regression. So it is better to just fail silently.
142  // COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
143 #if DEBUG
144  fprintf(stderr, "WARNING: %s SetParameter for undefined param ID %d while initialized. Ignoring..\n",
145  mAudioUnit->GetLoggingString(), (int)paramID);
146 #endif
147  } else {
148  // create new entry in map for the paramID (only happens first time)
149  ParameterMapEvent event(inValue);
150  mParameters[paramID] = event;
151  }
152  }
153  else
154  {
155  // paramID already exists in map so simply change its value
156  ParameterMapEvent &event = (*i).second;
157  event.SetValue(inValue);
158  }
159  }
160 }
161 
162 //_____________________________________________________________________________
163 //
164 void AUElement::SetScheduledEvent( AudioUnitParameterID paramID,
165  const AudioUnitParameterEvent &inEvent,
166  UInt32 inSliceOffsetInBuffer,
167  UInt32 inSliceDurationFrames,
168  bool okWhenInitialized )
169 {
170  if(mUseIndexedParameters)
171  {
172  ParameterMapEvent &event = GetParamEvent(paramID);
173  event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames );
174  }
175  else
176  {
177  ParameterMap::iterator i = mParameters.find(paramID);
178 
179  if (i == mParameters.end())
180  {
181  if (mAudioUnit->IsInitialized() && !okWhenInitialized) {
182  // The AU should not be creating new parameters once initialized.
183  // If a client tries to set an undefined parameter, we could throw as follows,
184  // but this might cause a regression. So it is better to just fail silently.
185  // COMPONENT_THROW(kAudioUnitErr_InvalidParameter);
186 #if DEBUG
187  fprintf(stderr, "WARNING: %s SetScheduledEvent for undefined param ID %d while initialized. Ignoring..\n",
188  mAudioUnit->GetLoggingString(), (int)paramID);
189 #endif
190  } else {
191  // create new entry in map for the paramID (only happens first time)
192  ParameterMapEvent event(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames);
193  mParameters[paramID] = event;
194  }
195  }
196  else
197  {
198  // paramID already exists in map so simply change its value
199  ParameterMapEvent &event = (*i).second;
200 
201  event.SetScheduledEvent(inEvent, inSliceOffsetInBuffer, inSliceDurationFrames );
202  }
203  }
204 }
205 
206 
207 
208 //_____________________________________________________________________________
209 //
210 void AUElement::GetParameterList(AudioUnitParameterID *outList)
211 {
212  if(mUseIndexedParameters)
213  {
214  UInt32 nparams = mIndexedParameters.size();
215  for (UInt32 i = 0; i < nparams; i++ )
216  *outList++ = (AudioUnitParameterID)i;
217  }
218  else
219  {
220  for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i)
221  *outList++ = (*i).first;
222  }
223 }
224 
225 //_____________________________________________________________________________
226 //
227 void AUElement::SaveState(CFMutableDataRef data)
228 {
229  if(mUseIndexedParameters)
230  {
231  UInt32 nparams = mIndexedParameters.size();
232  UInt32 theData = CFSwapInt32HostToBig(nparams);
233  CFDataAppendBytes(data, (UInt8 *)&theData, sizeof(nparams));
234 
235  for (UInt32 i = 0; i < nparams; i++)
236  {
237  struct {
238  UInt32 paramID;
239  //CFSwappedFloat32 value; crashes gcc3 PFE
240  UInt32 value; // really a big-endian float
241  } entry;
242 
243  entry.paramID = CFSwapInt32HostToBig(i);
244 
245  AudioUnitParameterValue v = mIndexedParameters[i].GetValue();
246  entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v );
247 
248  CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry));
249  }
250  }
251  else
252  {
253  UInt32 nparams = CFSwapInt32HostToBig(mParameters.size());
254  CFDataAppendBytes(data, (UInt8 *)&nparams, sizeof(nparams));
255 
256  for (ParameterMap::iterator i = mParameters.begin(); i != mParameters.end(); ++i) {
257  struct {
258  UInt32 paramID;
259  //CFSwappedFloat32 value; crashes gcc3 PFE
260  UInt32 value; // really a big-endian float
261  } entry;
262 
263  entry.paramID = CFSwapInt32HostToBig((*i).first);
264 
265  AudioUnitParameterValue v = (*i).second.GetValue();
266  entry.value = CFSwapInt32HostToBig(*(UInt32 *)&v );
267 
268  CFDataAppendBytes(data, (UInt8 *)&entry, sizeof(entry));
269  }
270  }
271 }
272 
273 //_____________________________________________________________________________
274 //
275 const UInt8 * AUElement::RestoreState(const UInt8 *state)
276 {
277  union FloatInt32 { UInt32 i; AudioUnitParameterValue f; };
278  const UInt8 *p = state;
279  UInt32 nparams = CFSwapInt32BigToHost(*(UInt32 *)p);
280  p += sizeof(UInt32);
281 
282  for (UInt32 i = 0; i < nparams; ++i) {
283  struct {
284  AudioUnitParameterID paramID;
285  AudioUnitParameterValue value;
286  } entry;
287 
288  entry.paramID = CFSwapInt32BigToHost(*(UInt32 *)p);
289  p += sizeof(UInt32);
290  FloatInt32 temp;
291  temp.i = CFSwapInt32BigToHost(*(UInt32 *)p);
292  entry.value = temp.f;
293  p += sizeof(AudioUnitParameterValue);
294 
295  SetParameter(entry.paramID, entry.value);
296  }
297  return p;
298 }
299 
300 //_____________________________________________________________________________
301 //
302 void AUElement::SetName (CFStringRef inName)
303 {
304  if (mElementName) CFRelease (mElementName);
305  mElementName = inName;
306  if (mElementName) CFRetain (mElementName);
307 }
308 
309 
310 //_____________________________________________________________________________
311 //
312 AUIOElement::AUIOElement(AUBase *audioUnit) :
313  AUElement(audioUnit),
314  mWillAllocate (true)
315 {
316  mStreamFormat.SetAUCanonical(2, // stereo
317  audioUnit->AudioUnitAPIVersion() == 1);
318  // interleaved if API version 1, deinterleaved if version 2
319  mStreamFormat.mSampleRate = kAUDefaultSampleRate;
320 }
321 
322 //_____________________________________________________________________________
323 //
324 OSStatus AUIOElement::SetStreamFormat(const CAStreamBasicDescription &desc)
325 {
326  mStreamFormat = desc;
327  return AUBase::noErr;
328 }
329 
330 //_____________________________________________________________________________
331 // inFramesToAllocate == 0 implies the AudioUnit's max-frames-per-slice will be used
332 void AUIOElement::AllocateBuffer(UInt32 inFramesToAllocate)
333 {
334  if (GetAudioUnit()->HasBegunInitializing())
335  {
336  UInt32 framesToAllocate = inFramesToAllocate > 0 ? inFramesToAllocate : GetAudioUnit()->GetMaxFramesPerSlice();
337 
338 // printf ("will allocate: %d\n", (int)((mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0));
339 
340  mIOBuffer.Allocate(mStreamFormat, (mWillAllocate && NeedsBufferSpace()) ? framesToAllocate : 0);
341  }
342 }
343 
344 //_____________________________________________________________________________
345 //
346 void AUIOElement::DeallocateBuffer()
347 {
348  mIOBuffer.Deallocate();
349 }
350 
351 //_____________________________________________________________________________
352 //
353 // AudioChannelLayout support
354 
355 // outLayoutTagsPtr WILL be NULL if called to find out how many
356 // layouts that Audio Unit will report
357 // return 0 (ie. NO channel layouts) if the AU doesn't require channel layout knowledge
358 UInt32 AUIOElement::GetChannelLayoutTags (AudioChannelLayoutTag *outLayoutTagsPtr)
359 {
360  return 0;
361 }
362 
363 // As the AudioChannelLayout can be a variable length structure
364 // (though in most cases it won't be!!!)
365 // The size of the ACL is always returned by the method
366 // if outMapPtr is NOT-NULL, then AU should copy into this pointer (outMapPtr) the current ACL that it has in use.
367 // the AU should also return whether the property is writable (that is the client can provide any arbitrary ACL that the audio unit will then honour)
368 // or if the property is read only - which is the generally preferred mode.
369 // If the AU doesn't require an AudioChannelLayout, then just return 0.
370 UInt32 AUIOElement::GetAudioChannelLayout (AudioChannelLayout *outMapPtr,
371  Boolean &outWritable)
372 {
373  return 0;
374 }
375 
376 // the incoming channel map will be at least as big as a basic AudioChannelLayout
377 // but its contents will determine its actual size
378 // Subclass should overide if channel map is writable
379 OSStatus AUIOElement::SetAudioChannelLayout (const AudioChannelLayout &inData)
380 {
381  return kAudioUnitErr_InvalidProperty;
382 }
383 
384 // Some units support optional usage of channel maps - typically converter units
385 // that can do channel remapping between different maps. In that optional case
386 // the user should be able to remove a channel map if that is possible.
387 // Typically this is NOT the case (e.g., the 3DMixer even in the stereo case
388 // needs to know if it is rendering to speakers or headphones)
389 OSStatus AUIOElement::RemoveAudioChannelLayout ()
390 {
391  return kAudioUnitErr_InvalidPropertyValue;
392 }
393 
394 
395 //_____________________________________________________________________________
396 //
397 AUScope::~AUScope()
398 {
399  for (ElementVector::iterator it = mElements.begin(); it != mElements.end(); ++it)
400  delete *it;
401 }
402 
403 //_____________________________________________________________________________
404 //
405 void AUScope::SetNumberOfElements(UInt32 numElements)
406 {
407  if (mDelegate)
408  return mDelegate->SetNumberOfElements(numElements);
409 
410  if (numElements > mElements.size()) {
411  mElements.reserve(numElements);
412  while (numElements > mElements.size()) {
413  AUElement *elem = GetCreator()->CreateElement(GetScope(), mElements.size());
414  mElements.push_back(elem);
415  }
416  } else
417  while (numElements < mElements.size()) {
418  AUElement *elem = mElements.back();
419  mElements.pop_back();
420  delete elem;
421  }
422 }
423 
424 //_____________________________________________________________________________
425 //
426 bool AUScope::HasElementWithName () const
427 {
428  for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
429  AUElement * el = const_cast<AUScope*>(this)->GetElement (i);
430  if (el && el->HasName()) {
431  return true;
432  }
433  }
434  return false;
435 }
436 
437 //_____________________________________________________________________________
438 //
439 
440 void AUScope::AddElementNamesToDict (CFMutableDictionaryRef & inNameDict)
441 {
442  if (HasElementWithName())
443  {
444  static char string[32];
445  CFMutableDictionaryRef elementDict = CFDictionaryCreateMutable (NULL, 0,
446  &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
447  CFStringRef str;
448  for (UInt32 i = 0; i < GetNumberOfElements(); ++i) {
449  AUElement * el = GetElement (i);
450  if (el && el->HasName()) {
451  snprintf (string, sizeof(string), "%d", int(i));
452  str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII);
453  CFDictionarySetValue (elementDict, str, el->GetName());
454  CFRelease (str);
455  }
456  }
457 
458  snprintf (string, sizeof(string), "%d", int(mScope));
459  str = CFStringCreateWithCString (NULL, string, kCFStringEncodingASCII);
460  CFDictionarySetValue (inNameDict, str, elementDict);
461  CFRelease (str);
462  CFRelease (elementDict);
463  }
464 }
465 
466 //_____________________________________________________________________________
467 //
468 bool AUScope::RestoreElementNames (CFDictionaryRef& inNameDict)
469 {
470  static char string[32];
471 
472  //first we have to see if we have enough elements and if not create them
473  bool didAddElements = false;
474  unsigned int maxElNum = 0;
475 
476  int dictSize = CFDictionaryGetCount(inNameDict);
477  CFStringRef * keys = (CFStringRef*)CA_malloc (dictSize * sizeof (CFStringRef));
478  CFDictionaryGetKeysAndValues (inNameDict, reinterpret_cast<const void**>(keys), NULL);
479  for (int i = 0; i < dictSize; i++)
480  {
481  unsigned int intKey;
482  CFStringGetCString (keys[i], string, 32, kCFStringEncodingASCII);
483  sscanf (string, "%u", &intKey);
484  if (UInt32(intKey) > maxElNum)
485  maxElNum = intKey;
486  }
487 
488  if (maxElNum >= GetNumberOfElements()) {
489  SetNumberOfElements (maxElNum+1);
490  didAddElements = true;
491  }
492 
493  // OK, now we have the number of elements that we need - lets restate their names
494  for (int i = 0; i < dictSize; i++)
495  {
496  CFStringRef elName = reinterpret_cast<CFStringRef>(CFDictionaryGetValue (inNameDict, keys[i]));
497  int intKey;
498  CFStringGetCString (keys[i], string, 32, kCFStringEncodingASCII);
499  sscanf (string, "%d", &intKey);
500  GetElement (intKey)->SetName (elName);
501  }
502  free (keys);
503 
504  return didAddElements;
505 }
506