1 /*************************************************************************/
2 /* audio_driver_osx.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30 #ifdef OSX_ENABLED
31
32 #include "audio_driver_osx.h"
33 #include "globals.h"
34 #include "os/os.h"
35
36 #define kOutputBus 0
37
outputDeviceAddressCB(AudioObjectID inObjectID,UInt32 inNumberAddresses,const AudioObjectPropertyAddress * inAddresses,void * __nullable inClientData)38 static OSStatus outputDeviceAddressCB(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *__nullable inClientData) {
39 AudioDriverOSX *driver = (AudioDriverOSX *)inClientData;
40
41 driver->reopen();
42
43 return noErr;
44 }
45
initDevice()46 Error AudioDriverOSX::initDevice() {
47 AudioComponentDescription desc;
48 zeromem(&desc, sizeof(desc));
49 desc.componentType = kAudioUnitType_Output;
50 desc.componentSubType = kAudioUnitSubType_HALOutput;
51 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
52
53 AudioComponent comp = AudioComponentFindNext(NULL, &desc);
54 ERR_FAIL_COND_V(comp == NULL, FAILED);
55
56 OSStatus result = AudioComponentInstanceNew(comp, &audio_unit);
57 ERR_FAIL_COND_V(result != noErr, FAILED);
58
59 AudioStreamBasicDescription strdesc;
60
61 // TODO: Implement this
62 /*zeromem(&strdesc, sizeof(strdesc));
63 UInt32 size = sizeof(strdesc);
64 result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &strdesc, &size);
65 ERR_FAIL_COND_V(result != noErr, FAILED);
66
67 switch (strdesc.mChannelsPerFrame) {
68 case 2: // Stereo
69 case 6: // Surround 5.1
70 case 8: // Surround 7.1
71 channels = strdesc.mChannelsPerFrame;
72 break;
73
74 default:
75 // Unknown number of channels, default to stereo
76 channels = 2;
77 break;
78 }*/
79
80 mix_rate = GLOBAL_DEF("audio/mix_rate", 44100);
81
82 zeromem(&strdesc, sizeof(strdesc));
83 strdesc.mFormatID = kAudioFormatLinearPCM;
84 strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
85 strdesc.mChannelsPerFrame = channels;
86 strdesc.mSampleRate = mix_rate;
87 strdesc.mFramesPerPacket = 1;
88 strdesc.mBitsPerChannel = 16;
89 strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
90 strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
91
92 result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc));
93 ERR_FAIL_COND_V(result != noErr, FAILED);
94
95 int latency = GLOBAL_DEF("audio/output_latency", 25);
96 unsigned int buffer_size = closest_power_of_2(latency * mix_rate / 1000);
97
98 if (OS::get_singleton()->is_stdout_verbose()) {
99 print_line("audio buffer size: " + itos(buffer_size) + " calculated latency: " + itos(buffer_size * 1000 / mix_rate));
100 }
101
102 samples_in.resize(buffer_size);
103 buffer_frames = buffer_size / channels;
104
105 AURenderCallbackStruct callback;
106 zeromem(&callback, sizeof(AURenderCallbackStruct));
107 callback.inputProc = &AudioDriverOSX::output_callback;
108 callback.inputProcRefCon = this;
109 result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
110 ERR_FAIL_COND_V(result != noErr, FAILED);
111
112 result = AudioUnitInitialize(audio_unit);
113 ERR_FAIL_COND_V(result != noErr, FAILED);
114
115 return OK;
116 }
117
finishDevice()118 Error AudioDriverOSX::finishDevice() {
119 OSStatus result;
120
121 if (active) {
122 result = AudioOutputUnitStop(audio_unit);
123 ERR_FAIL_COND_V(result != noErr, FAILED);
124
125 active = false;
126 }
127
128 result = AudioUnitUninitialize(audio_unit);
129 ERR_FAIL_COND_V(result != noErr, FAILED);
130
131 return OK;
132 }
133
init()134 Error AudioDriverOSX::init() {
135 OSStatus result;
136
137 mutex = Mutex::create();
138 active = false;
139 channels = 2;
140
141 outputDeviceAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
142 outputDeviceAddress.mScope = kAudioObjectPropertyScopeGlobal;
143 outputDeviceAddress.mElement = kAudioObjectPropertyElementMaster;
144
145 result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this);
146 ERR_FAIL_COND_V(result != noErr, FAILED);
147
148 return initDevice();
149 };
150
reopen()151 Error AudioDriverOSX::reopen() {
152 bool restart = false;
153
154 lock();
155
156 if (active) {
157 restart = true;
158 }
159
160 Error err = finishDevice();
161 if (err != OK) {
162 ERR_PRINT("finishDevice failed");
163 unlock();
164 return err;
165 }
166
167 err = initDevice();
168 if (err != OK) {
169 ERR_PRINT("initDevice failed");
170 unlock();
171 return err;
172 }
173
174 if (restart) {
175 start();
176 }
177
178 unlock();
179
180 return OK;
181 }
182
output_callback(void * inRefCon,AudioUnitRenderActionFlags * ioActionFlags,const AudioTimeStamp * inTimeStamp,UInt32 inBusNumber,UInt32 inNumberFrames,AudioBufferList * ioData)183 OSStatus AudioDriverOSX::output_callback(void *inRefCon,
184 AudioUnitRenderActionFlags *ioActionFlags,
185 const AudioTimeStamp *inTimeStamp,
186 UInt32 inBusNumber, UInt32 inNumberFrames,
187 AudioBufferList *ioData) {
188
189 AudioDriverOSX *ad = (AudioDriverOSX *)inRefCon;
190
191 if (!ad->active || !ad->try_lock()) {
192 for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) {
193 AudioBuffer *abuf = &ioData->mBuffers[i];
194 zeromem(abuf->mData, abuf->mDataByteSize);
195 };
196 return 0;
197 };
198
199 for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) {
200
201 AudioBuffer *abuf = &ioData->mBuffers[i];
202 int frames_left = inNumberFrames;
203 int16_t *out = (int16_t *)abuf->mData;
204
205 while (frames_left) {
206
207 int frames = MIN(frames_left, ad->buffer_frames);
208 ad->audio_server_process(frames, ad->samples_in.ptr());
209
210 for (int j = 0; j < frames * ad->channels; j++) {
211
212 out[j] = ad->samples_in[j] >> 16;
213 }
214
215 frames_left -= frames;
216 out += frames * ad->channels;
217 };
218 };
219
220 ad->unlock();
221
222 return 0;
223 };
224
start()225 void AudioDriverOSX::start() {
226 if (!active) {
227 OSStatus result = AudioOutputUnitStart(audio_unit);
228 if (result != noErr) {
229 ERR_PRINT("AudioOutputUnitStart failed");
230 } else {
231 active = true;
232 }
233 }
234 };
235
get_mix_rate() const236 int AudioDriverOSX::get_mix_rate() const {
237 return 44100;
238 };
239
get_output_format() const240 AudioDriverSW::OutputFormat AudioDriverOSX::get_output_format() const {
241 return OUTPUT_STEREO;
242 };
243
lock()244 void AudioDriverOSX::lock() {
245 if (mutex)
246 mutex->lock();
247 };
248
unlock()249 void AudioDriverOSX::unlock() {
250 if (mutex)
251 mutex->unlock();
252 };
253
try_lock()254 bool AudioDriverOSX::try_lock() {
255 if (mutex)
256 return mutex->try_lock() == OK;
257 return true;
258 }
259
finish()260 void AudioDriverOSX::finish() {
261 finishDevice();
262
263 OSStatus result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &outputDeviceAddress, &outputDeviceAddressCB, this);
264 if (result != noErr) {
265 ERR_PRINT("AudioObjectRemovePropertyListener failed");
266 }
267
268 AURenderCallbackStruct callback;
269 zeromem(&callback, sizeof(AURenderCallbackStruct));
270 result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
271 if (result != noErr) {
272 ERR_PRINT("AudioUnitSetProperty failed");
273 }
274
275 if (mutex) {
276 memdelete(mutex);
277 mutex = NULL;
278 }
279 };
280
AudioDriverOSX()281 AudioDriverOSX::AudioDriverOSX() {
282 active = false;
283 mutex = NULL;
284
285 mix_rate = 44100;
286 channels = 2;
287 samples_in.clear();
288 };
289
~AudioDriverOSX()290 AudioDriverOSX::~AudioDriverOSX(){};
291
292 #endif
293