this repo has no description
1#include "AudioUnitBase.h"
2#include "AudioUnitProperties.h"
3#include "AudioUnitRenderer.h"
4#include <CoreServices/MacErrors.h>
5#include <util/debug.h>
6#include <cstring>
7
8AudioUnitComponent::AudioUnitComponent(std::initializer_list<CFStringRef> elements)
9{
10 m_elementNames.insert(m_elementNames.end(), elements.begin(), elements.end());
11 m_config.resize(m_elementNames.size());
12
13 // Default audio params
14 const AudioStreamBasicDescription defaultConfig = AudioStreamBasicDescription {
15 44100.0, kAudioFormatLinearPCM, kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked,
16 4, 1, 4, 2, 16, 0
17 };
18
19 for (size_t i = 0; i < m_config.size(); i++)
20 {
21 m_config[i] = std::pair<AudioStreamBasicDescription, AudioStreamBasicDescription>(defaultConfig, defaultConfig);
22 }
23
24 memset(&m_inputUnit, 0, sizeof(m_inputUnit));
25}
26
27AudioUnitComponent::~AudioUnitComponent()
28{
29 if (m_contextName != nullptr)
30 CFRelease(m_contextName);
31 CloseComponent(m_inputUnit.sourceAudioUnit);
32}
33
34OSStatus AudioUnitComponent::getPropertyInfo(AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, UInt32* dataSize, Boolean* writable)
35{
36 switch (prop)
37 {
38 case kAudioUnitProperty_StreamFormat:
39 *dataSize = sizeof(AudioStreamBasicDescription);
40 *writable = true;
41 break;
42 case kAudioOutputUnitProperty_EnableIO:
43 *dataSize = sizeof(UInt32);
44 *writable = true;
45 break;
46 case kAudioUnitProperty_SetRenderCallback:
47 *dataSize = sizeof(AURenderCallbackStruct);
48 *writable = true;
49 break;
50 case kAudioUnitProperty_ShouldAllocateBuffer:
51 *dataSize = sizeof(int);
52 *writable = true;
53 break;
54 case kAudioUnitProperty_ElementCount:
55 *dataSize = sizeof(UInt32);
56 *writable = false;
57 break;
58 case kAudioUnitProperty_LastRenderError: // TODO: implement
59 *dataSize = sizeof(OSStatus);
60 *writable = false;
61 break;
62 case kAudioUnitProperty_SampleRate: // TODO: implement
63 *dataSize = sizeof(Float64);
64 *writable = true; // TODO: query itself on kAudioUnitProperty_StreamFormat if writable
65 break;
66 case kAudioUnitProperty_ElementName:
67 *dataSize = sizeof(CFStringRef);
68 *writable = false;
69 break;
70 case kAudioUnitProperty_ContextName:
71 *dataSize = sizeof(CFStringRef);
72 *writable = true;
73 break;
74 default:
75 return kAudioUnitErr_InvalidProperty;
76 }
77
78 return noErr;
79}
80
81OSStatus AudioUnitComponent::setProperty(AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, const void* data, UInt32 dataSize)
82{
83 TRACE5(prop, scope, elem, data, dataSize);
84
85 switch (prop)
86 {
87 case kAudioUnitProperty_StreamFormat:
88 {
89 const AudioStreamBasicDescription* newConfig = static_cast<const AudioStreamBasicDescription*>(data);
90
91 if (dataSize != sizeof(*newConfig))
92 return kAudioUnitErr_InvalidParameter;
93
94 if (elem >= m_config.size())
95 return kAudioUnitErr_InvalidElement;
96
97 // TODO: perform validation
98
99 if (scope == kAudioUnitScope_Output)
100 m_config[elem].second = *newConfig;
101 else if (scope == kAudioUnitScope_Input)
102 m_config[elem].first = *newConfig;
103 else if (scope == kAudioUnitScope_Global)
104 m_config[0].second = *newConfig;
105 else
106 return kAudioUnitErr_InvalidScope;
107
108 return noErr;
109 }
110 case kAudioUnitProperty_SetRenderCallback:
111 {
112 if (dataSize != sizeof(AURenderCallbackStruct))
113 return kAudioUnitErr_InvalidParameter;
114 //if (scope == kAudioUnitScope_Input)
115 //{
116 if (elem != 0)
117 return kAudioUnitErr_InvalidElement;
118
119 CloseComponent(m_inputUnit.sourceAudioUnit); // TODO: wrong, we may not own the unit!
120 m_inputUnit.sourceOutputNumber = 0;
121 m_inputUnit.destInputNumber = 0;
122 m_inputUnit.sourceAudioUnit = new AudioUnitRenderer(*(AURenderCallbackStruct*) data);
123 //}
124 //else
125 // return kAudioUnitErr_InvalidScope;
126
127 return noErr;
128 }
129 case kAudioUnitProperty_MakeConnection:
130 {
131 if (dataSize != sizeof(AudioUnitConnection))
132 return kAudioUnitErr_InvalidParameter;
133 if (scope != kAudioUnitScope_Input)
134 return kAudioUnitErr_InvalidScope;
135
136 // TODO: support multiple units!
137 // TODO: reconfigure input format based on output format
138
139 CloseComponent(m_inputUnit.sourceAudioUnit); // TODO: wrong, we may not own the unit!
140 memcpy(&m_inputUnit, data, sizeof(AudioUnitConnection));
141
142 return noErr;
143 }
144 case kAudioUnitProperty_ShouldAllocateBuffer:
145 {
146 int* b = (int*) data;
147 if (dataSize < sizeof(int))
148 return kAudioUnitErr_InvalidParameter;
149
150 m_shouldAllocateBuffer = *b != 0;
151 return noErr;
152 }
153 case kAudioUnitProperty_ContextName:
154 {
155 if (dataSize < sizeof(CFStringRef))
156 return kAudioUnitErr_InvalidParameter;
157
158 if (data == nullptr)
159 {
160 if (m_contextName != nullptr)
161 {
162 CFRelease(m_contextName);
163 m_contextName = nullptr;
164 }
165 }
166 else
167 {
168 m_contextName = (CFStringRef) data;
169 CFRetain(m_contextName);
170 }
171 return noErr;
172 }
173 default:
174 return kAudioUnitErr_InvalidProperty;
175 }
176}
177
178OSStatus AudioUnitComponent::getProperty(AudioUnitPropertyID prop, AudioUnitScope scope, AudioUnitElement elem, void* data, UInt32* dataSize)
179{
180 TRACE5(prop, scope, elem, data, dataSize);
181
182 switch (prop)
183 {
184 case kAudioUnitProperty_StreamFormat:
185 {
186 AudioStreamBasicDescription* newConfig = static_cast<AudioStreamBasicDescription*>(data);
187
188 if (*dataSize < sizeof(AudioStreamBasicDescription))
189 return kAudioUnitErr_InvalidParameter;
190
191 if (elem >= m_config.size())
192 return kAudioUnitErr_InvalidElement;
193
194 if (scope == kAudioUnitScope_Output)
195 *newConfig = m_config[elem].second;
196 else if (scope == kAudioUnitScope_Input)
197 *newConfig = m_config[elem].first;
198 else if (scope == kAudioUnitScope_Global)
199 *newConfig = m_config[0].second;
200 else
201 return kAudioUnitErr_InvalidScope;
202
203 *dataSize = sizeof(AudioStreamBasicDescription);
204
205 return noErr;
206 }
207 case kAudioUnitProperty_ShouldAllocateBuffer:
208 {
209 int* out = (int*) data;
210 if (*dataSize < sizeof(UInt32))
211 return kAudioUnitErr_InvalidParameter;
212
213 *out = m_shouldAllocateBuffer;
214 return noErr;
215 }
216 case kAudioUnitProperty_ElementCount:
217 {
218 UInt32* out = (UInt32*) data;
219 if (*dataSize < sizeof(UInt32))
220 return kAudioUnitErr_InvalidParameter;
221
222 *out = m_config.size();
223 return noErr;
224 }
225 case kAudioUnitProperty_ElementName:
226 {
227 CFStringRef* out = (CFStringRef*) data;
228 if (*dataSize != sizeof(CFStringRef))
229 return kAudioUnitErr_InvalidParameter;
230 if (elem >= m_elementNames.size())
231 return kAudioUnitErr_InvalidElement;
232
233 *out = m_elementNames[elem];
234 return noErr;
235 }
236 case kAudioUnitProperty_ContextName:
237 {
238 CFStringRef* out = (CFStringRef*) data;
239 if (*dataSize != sizeof(CFStringRef))
240 return kAudioUnitErr_InvalidParameter;
241
242 *out = m_contextName;
243 return noErr;
244 }
245 default:
246 return kAudioUnitErr_InvalidProperty;
247 }
248}
249
250OSStatus AudioUnitComponent::addRenderNotify(AURenderCallback inProc, void* opaque)
251{
252 std::lock_guard<std::mutex> guard(m_listenersMutex);
253
254 if (!inProc)
255 return paramErr;
256 m_listeners.insert(std::pair<AURenderCallback,void*>(inProc, opaque));
257
258 return noErr;
259}
260
261OSStatus AudioUnitComponent::removeRenderNotify(AURenderCallback inProc, void* opaque)
262{
263 std::lock_guard<std::mutex> guard(m_listenersMutex);
264 m_listeners.erase(std::pair<AURenderCallback,void*>(inProc, opaque));
265 return noErr;
266}
267
268OSStatus AudioUnitComponent::notifyListeners(AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp,
269 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
270{
271 std::lock_guard<std::mutex> guard(m_listenersMutex);
272 OSStatus status = noErr;
273
274 for (auto p : m_listeners)
275 {
276 status = p.first(p.second, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
277 if (status != noErr)
278 break;
279 }
280
281 return status;
282}