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