| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | /*  audio_driver_coreaudio.cpp                                           */ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							| 
									
										
										
										
											2017-08-27 14:16:55 +02:00
										 |  |  | /*                      https://godotengine.org                          */ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2020-01-01 11:16:22 +01:00
										 |  |  | /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							|  |  |  | /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* Permission is hereby granted, free of charge, to any person obtaining */ | 
					
						
							|  |  |  | /* a copy of this software and associated documentation files (the       */ | 
					
						
							|  |  |  | /* "Software"), to deal in the Software without restriction, including   */ | 
					
						
							|  |  |  | /* without limitation the rights to use, copy, modify, merge, publish,   */ | 
					
						
							|  |  |  | /* distribute, sublicense, and/or sell copies of the Software, and to    */ | 
					
						
							|  |  |  | /* permit persons to whom the Software is furnished to do so, subject to */ | 
					
						
							|  |  |  | /* the following conditions:                                             */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* The above copyright notice and this permission notice shall be        */ | 
					
						
							|  |  |  | /* included in all copies or substantial portions of the Software.       */ | 
					
						
							|  |  |  | /*                                                                       */ | 
					
						
							|  |  |  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */ | 
					
						
							|  |  |  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */ | 
					
						
							|  |  |  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ | 
					
						
							|  |  |  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */ | 
					
						
							|  |  |  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */ | 
					
						
							|  |  |  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */ | 
					
						
							|  |  |  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2018-01-05 00:50:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #ifdef COREAUDIO_ENABLED
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #include "audio_driver_coreaudio.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-11 18:13:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "core/os/os.h"
 | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | #include "core/project_settings.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define kOutputBus 0
 | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | #define kInputBus 1
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #ifdef OSX_ENABLED
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | OSStatus AudioDriverCoreAudio::input_device_address_cb(AudioObjectID inObjectID, | 
					
						
							|  |  |  | 		UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, | 
					
						
							|  |  |  | 		void *inClientData) { | 
					
						
							|  |  |  | 	AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If our selected device is the Default call set_device to update the
 | 
					
						
							|  |  |  | 	// kAudioOutputUnitProperty_CurrentDevice property
 | 
					
						
							|  |  |  | 	if (driver->capture_device_name == "Default") { | 
					
						
							|  |  |  | 		driver->capture_set_device("Default"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return noErr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID, | 
					
						
							|  |  |  | 		UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, | 
					
						
							|  |  |  | 		void *inClientData) { | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | 	AudioDriverCoreAudio *driver = (AudioDriverCoreAudio *)inClientData; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 	// If our selected device is the Default call set_device to update the
 | 
					
						
							|  |  |  | 	// kAudioOutputUnitProperty_CurrentDevice property
 | 
					
						
							|  |  |  | 	if (driver->device_name == "Default") { | 
					
						
							|  |  |  | 		driver->set_device("Default"); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-07-16 22:29:47 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return noErr; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 08:38:32 -03:00
										 |  |  | Error AudioDriverCoreAudio::init() { | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	AudioComponentDescription desc; | 
					
						
							|  |  |  | 	zeromem(&desc, sizeof(desc)); | 
					
						
							|  |  |  | 	desc.componentType = kAudioUnitType_Output; | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #ifdef OSX_ENABLED
 | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	desc.componentSubType = kAudioUnitSubType_HALOutput; | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #else
 | 
					
						
							|  |  |  | 	desc.componentSubType = kAudioUnitSubType_RemoteIO; | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	desc.componentManufacturer = kAudioUnitManufacturer_Apple; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 	AudioComponent comp = AudioComponentFindNext(nullptr, &desc); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(comp == nullptr, FAILED); | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	OSStatus result = AudioComponentInstanceNew(comp, &audio_unit); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-11 08:38:32 -03:00
										 |  |  | #ifdef OSX_ENABLED
 | 
					
						
							|  |  |  | 	AudioObjectPropertyAddress prop; | 
					
						
							|  |  |  | 	prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; | 
					
						
							|  |  |  | 	prop.mScope = kAudioObjectPropertyScopeGlobal; | 
					
						
							|  |  |  | 	prop.mElement = kAudioObjectPropertyElementMaster; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	AudioStreamBasicDescription strdesc; | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-22 18:27:17 -03:00
										 |  |  | 	zeromem(&strdesc, sizeof(strdesc)); | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	UInt32 size = sizeof(strdesc); | 
					
						
							|  |  |  | 	result = AudioUnitGetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kOutputBus, &strdesc, &size); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (strdesc.mChannelsPerFrame) { | 
					
						
							|  |  |  | 		case 2: // Stereo
 | 
					
						
							| 
									
										
										
										
											2017-08-22 18:27:17 -03:00
										 |  |  | 		case 4: // Surround 3.1
 | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 		case 6: // Surround 5.1
 | 
					
						
							|  |  |  | 		case 8: // Surround 7.1
 | 
					
						
							|  |  |  | 			channels = strdesc.mChannelsPerFrame; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			// Unknown number of channels, default to stereo
 | 
					
						
							|  |  |  | 			channels = 2; | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2017-08-22 18:27:17 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-19 18:58:15 -03:00
										 |  |  | 	mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	zeromem(&strdesc, sizeof(strdesc)); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	strdesc.mFormatID = kAudioFormatLinearPCM; | 
					
						
							|  |  |  | 	strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; | 
					
						
							|  |  |  | 	strdesc.mChannelsPerFrame = channels; | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	strdesc.mSampleRate = mix_rate; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	strdesc.mFramesPerPacket = 1; | 
					
						
							|  |  |  | 	strdesc.mBitsPerChannel = 16; | 
					
						
							| 
									
										
										
										
											2017-07-16 22:29:47 -03:00
										 |  |  | 	strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; | 
					
						
							|  |  |  | 	strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &strdesc, sizeof(strdesc)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-19 18:58:15 -03:00
										 |  |  | 	int latency = GLOBAL_DEF_RST("audio/output_latency", DEFAULT_OUTPUT_LATENCY); | 
					
						
							| 
									
										
										
										
											2017-08-29 16:47:44 -03:00
										 |  |  | 	// Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels)
 | 
					
						
							|  |  |  | 	buffer_frames = closest_power_of_2(latency * mix_rate / 1000); | 
					
						
							| 
									
										
										
										
											2017-07-19 11:05:51 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #ifdef OSX_ENABLED
 | 
					
						
							| 
									
										
										
										
											2017-08-29 16:47:44 -03:00
										 |  |  | 	result = AudioUnitSetProperty(audio_unit, kAudioDevicePropertyBufferFrameSize, kAudioUnitScope_Global, kOutputBus, &buffer_frames, sizeof(UInt32)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | 	unsigned int buffer_size = buffer_frames * channels; | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	samples_in.resize(buffer_size); | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | 	input_buf.resize(buffer_size); | 
					
						
							| 
									
										
										
										
											2017-08-29 16:47:44 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 08:47:34 +02:00
										 |  |  | 	print_verbose("CoreAudio: detected " + itos(channels) + " channels"); | 
					
						
							|  |  |  | 	print_verbose("CoreAudio: audio buffer frames: " + itos(buffer_frames) + " calculated latency: " + itos(buffer_frames * 1000 / mix_rate) + "ms"); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	AURenderCallbackStruct callback; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	zeromem(&callback, sizeof(AURenderCallbackStruct)); | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | 	callback.inputProc = &AudioDriverCoreAudio::output_callback; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	callback.inputProcRefCon = this; | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = AudioUnitInitialize(audio_unit); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-14 00:35:01 +09:00
										 |  |  | 	if (GLOBAL_GET("audio/enable_audio_input")) { | 
					
						
							|  |  |  | 		return capture_init(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return OK; | 
					
						
							| 
									
										
										
										
											2017-07-16 22:29:47 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | OSStatus AudioDriverCoreAudio::output_callback(void *inRefCon, | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | 		AudioUnitRenderActionFlags *ioActionFlags, | 
					
						
							|  |  |  | 		const AudioTimeStamp *inTimeStamp, | 
					
						
							|  |  |  | 		UInt32 inBusNumber, UInt32 inNumberFrames, | 
					
						
							|  |  |  | 		AudioBufferList *ioData) { | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | 	AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | 	if (!ad->active || !ad->try_lock()) { | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | 			AudioBuffer *abuf = &ioData->mBuffers[i]; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			zeromem(abuf->mData, abuf->mDataByteSize); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 21:02:02 -03:00
										 |  |  | 	ad->start_counting_ticks(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	for (unsigned int i = 0; i < ioData->mNumberBuffers; i++) { | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | 		AudioBuffer *abuf = &ioData->mBuffers[i]; | 
					
						
							| 
									
										
										
										
											2019-10-24 17:34:18 +03:00
										 |  |  | 		unsigned int frames_left = inNumberFrames; | 
					
						
							| 
									
										
										
										
											2017-03-05 16:44:50 +01:00
										 |  |  | 		int16_t *out = (int16_t *)abuf->mData; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		while (frames_left) { | 
					
						
							| 
									
										
										
										
											2019-10-24 17:34:18 +03:00
										 |  |  | 			unsigned int frames = MIN(frames_left, ad->buffer_frames); | 
					
						
							| 
									
										
										
										
											2017-11-25 10:42:20 +01:00
										 |  |  | 			ad->audio_server_process(frames, ad->samples_in.ptrw()); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-24 17:34:18 +03:00
										 |  |  | 			for (unsigned int j = 0; j < frames * ad->channels; j++) { | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | 				out[j] = ad->samples_in[j] >> 16; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			frames_left -= frames; | 
					
						
							|  |  |  | 			out += frames * ad->channels; | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-20 21:02:02 -03:00
										 |  |  | 	ad->stop_counting_ticks(); | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | 	ad->unlock(); | 
					
						
							| 
									
										
										
										
											2014-11-02 11:31:01 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, | 
					
						
							|  |  |  | 		AudioUnitRenderActionFlags *ioActionFlags, | 
					
						
							|  |  |  | 		const AudioTimeStamp *inTimeStamp, | 
					
						
							|  |  |  | 		UInt32 inBusNumber, UInt32 inNumberFrames, | 
					
						
							|  |  |  | 		AudioBufferList *ioData) { | 
					
						
							|  |  |  | 	AudioDriverCoreAudio *ad = (AudioDriverCoreAudio *)inRefCon; | 
					
						
							|  |  |  | 	if (!ad->active) { | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ad->lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	AudioBufferList bufferList; | 
					
						
							|  |  |  | 	bufferList.mNumberBuffers = 1; | 
					
						
							|  |  |  | 	bufferList.mBuffers[0].mData = ad->input_buf.ptrw(); | 
					
						
							| 
									
										
										
										
											2018-07-27 13:54:30 -03:00
										 |  |  | 	bufferList.mBuffers[0].mNumberChannels = ad->capture_channels; | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | 	bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	OSStatus result = AudioUnitRender(ad->input_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | 	if (result == noErr) { | 
					
						
							| 
									
										
										
										
											2019-10-24 17:34:18 +03:00
										 |  |  | 		for (unsigned int i = 0; i < inNumberFrames * ad->capture_channels; i++) { | 
					
						
							| 
									
										
										
										
											2018-07-27 13:54:30 -03:00
										 |  |  | 			int32_t sample = ad->input_buf[i] << 16; | 
					
						
							| 
									
										
										
										
											2020-01-20 13:11:47 +01:00
										 |  |  | 			ad->input_buffer_write(sample); | 
					
						
							| 
									
										
										
										
											2018-07-27 13:54:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			if (ad->capture_channels == 1) { | 
					
						
							| 
									
										
										
										
											2020-01-20 13:11:47 +01:00
										 |  |  | 				// In case input device is single channel convert it to Stereo
 | 
					
						
							|  |  |  | 				ad->input_buffer_write(sample); | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2019-11-06 17:03:04 +01:00
										 |  |  | 		ERR_PRINT("AudioUnitRender failed, code: " + itos(result)); | 
					
						
							| 
									
										
										
										
											2018-07-03 22:08:43 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ad->unlock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | void AudioDriverCoreAudio::start() { | 
					
						
							| 
									
										
										
										
											2017-07-16 22:29:47 -03:00
										 |  |  | 	if (!active) { | 
					
						
							|  |  |  | 		OSStatus result = AudioOutputUnitStart(audio_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							| 
									
										
										
										
											2019-11-06 17:03:04 +01:00
										 |  |  | 			ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result)); | 
					
						
							| 
									
										
										
										
											2017-07-16 22:29:47 -03:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			active = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-07 15:44:07 +07:00
										 |  |  | void AudioDriverCoreAudio::stop() { | 
					
						
							|  |  |  | 	if (active) { | 
					
						
							|  |  |  | 		OSStatus result = AudioOutputUnitStop(audio_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							| 
									
										
										
										
											2019-11-06 17:03:04 +01:00
										 |  |  | 			ERR_PRINT("AudioOutputUnitStop failed, code: " + itos(result)); | 
					
						
							| 
									
										
										
										
											2018-05-07 15:44:07 +07:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			active = false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | int AudioDriverCoreAudio::get_mix_rate() const { | 
					
						
							| 
									
										
										
										
											2017-08-29 16:47:44 -03:00
										 |  |  | 	return mix_rate; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-29 15:22:42 -03:00
										 |  |  | AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const { | 
					
						
							| 
									
										
										
										
											2017-08-22 18:27:17 -03:00
										 |  |  | 	return get_speaker_mode_by_total_channels(channels); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | void AudioDriverCoreAudio::lock() { | 
					
						
							| 
									
										
										
										
											2020-02-26 11:28:13 +01:00
										 |  |  | 	mutex.lock(); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioDriverCoreAudio::unlock() { | 
					
						
							| 
									
										
										
										
											2020-02-26 11:28:13 +01:00
										 |  |  | 	mutex.unlock(); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool AudioDriverCoreAudio::try_lock() { | 
					
						
							| 
									
										
										
										
											2020-02-26 11:28:13 +01:00
										 |  |  | 	return mutex.try_lock() == OK; | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void AudioDriverCoreAudio::finish() { | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	capture_finish(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 	if (audio_unit) { | 
					
						
							|  |  |  | 		OSStatus result; | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		lock(); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		AURenderCallbackStruct callback; | 
					
						
							|  |  |  | 		zeromem(&callback, sizeof(AURenderCallbackStruct)); | 
					
						
							|  |  |  | 		result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback)); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		if (result != noErr) { | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 			ERR_PRINT("AudioUnitSetProperty failed"); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		if (active) { | 
					
						
							|  |  |  | 			result = AudioOutputUnitStop(audio_unit); | 
					
						
							|  |  |  | 			if (result != noErr) { | 
					
						
							|  |  |  | 				ERR_PRINT("AudioOutputUnitStop failed"); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 			active = false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = AudioUnitUninitialize(audio_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioUnitUninitialize failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | #ifdef OSX_ENABLED
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		AudioObjectPropertyAddress prop; | 
					
						
							|  |  |  | 		prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice; | 
					
						
							|  |  |  | 		prop.mScope = kAudioObjectPropertyScopeGlobal; | 
					
						
							|  |  |  | 		prop.mElement = kAudioObjectPropertyElementMaster; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioObjectRemovePropertyListener failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		result = AudioComponentInstanceDispose(audio_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioComponentInstanceDispose failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		audio_unit = nullptr; | 
					
						
							| 
									
										
										
										
											2018-08-25 10:42:30 -03:00
										 |  |  | 		unlock(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | Error AudioDriverCoreAudio::capture_init() { | 
					
						
							|  |  |  | 	AudioComponentDescription desc; | 
					
						
							|  |  |  | 	zeromem(&desc, sizeof(desc)); | 
					
						
							|  |  |  | 	desc.componentType = kAudioUnitType_Output; | 
					
						
							|  |  |  | #ifdef OSX_ENABLED
 | 
					
						
							|  |  |  | 	desc.componentSubType = kAudioUnitSubType_HALOutput; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	desc.componentSubType = kAudioUnitSubType_RemoteIO; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	desc.componentManufacturer = kAudioUnitManufacturer_Apple; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 	AudioComponent comp = AudioComponentFindNext(nullptr, &desc); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(comp == nullptr, FAILED); | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	OSStatus result = AudioComponentInstanceNew(comp, &input_unit); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef OSX_ENABLED
 | 
					
						
							|  |  |  | 	AudioObjectPropertyAddress prop; | 
					
						
							|  |  |  | 	prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; | 
					
						
							|  |  |  | 	prop.mScope = kAudioObjectPropertyScopeGlobal; | 
					
						
							|  |  |  | 	prop.mElement = kAudioObjectPropertyElementMaster; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	UInt32 flag = 1; | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 	flag = 0; | 
					
						
							|  |  |  | 	result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UInt32 size; | 
					
						
							|  |  |  | #ifdef OSX_ENABLED
 | 
					
						
							|  |  |  | 	AudioDeviceID deviceId; | 
					
						
							|  |  |  | 	size = sizeof(AudioDeviceID); | 
					
						
							|  |  |  | 	AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 	result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId); | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	AudioStreamBasicDescription strdesc; | 
					
						
							|  |  |  | 	zeromem(&strdesc, sizeof(strdesc)); | 
					
						
							|  |  |  | 	size = sizeof(strdesc); | 
					
						
							|  |  |  | 	result = AudioUnitGetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, &size); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (strdesc.mChannelsPerFrame) { | 
					
						
							|  |  |  | 		case 1: // Mono
 | 
					
						
							|  |  |  | 			capture_channels = 1; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case 2: // Stereo
 | 
					
						
							|  |  |  | 			capture_channels = 2; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			// Unknown number of channels, default to stereo
 | 
					
						
							|  |  |  | 			capture_channels = 2; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mix_rate = GLOBAL_DEF_RST("audio/mix_rate", DEFAULT_MIX_RATE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	zeromem(&strdesc, sizeof(strdesc)); | 
					
						
							|  |  |  | 	strdesc.mFormatID = kAudioFormatLinearPCM; | 
					
						
							|  |  |  | 	strdesc.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; | 
					
						
							|  |  |  | 	strdesc.mChannelsPerFrame = capture_channels; | 
					
						
							|  |  |  | 	strdesc.mSampleRate = mix_rate; | 
					
						
							|  |  |  | 	strdesc.mFramesPerPacket = 1; | 
					
						
							|  |  |  | 	strdesc.mBitsPerChannel = 16; | 
					
						
							|  |  |  | 	strdesc.mBytesPerFrame = strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; | 
					
						
							|  |  |  | 	strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = AudioUnitSetProperty(input_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &strdesc, sizeof(strdesc)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	AURenderCallbackStruct callback; | 
					
						
							|  |  |  | 	zeromem(&callback, sizeof(AURenderCallbackStruct)); | 
					
						
							|  |  |  | 	callback.inputProc = &AudioDriverCoreAudio::input_callback; | 
					
						
							|  |  |  | 	callback.inputProcRefCon = this; | 
					
						
							|  |  |  | 	result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, kInputBus, &callback, sizeof(callback)); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	result = AudioUnitInitialize(input_unit); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 	ERR_FAIL_COND_V(result != noErr, FAILED); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | void AudioDriverCoreAudio::capture_finish() { | 
					
						
							|  |  |  | 	if (input_unit) { | 
					
						
							|  |  |  | 		lock(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		AURenderCallbackStruct callback; | 
					
						
							|  |  |  | 		zeromem(&callback, sizeof(AURenderCallbackStruct)); | 
					
						
							|  |  |  | 		OSStatus result = AudioUnitSetProperty(input_unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callback, sizeof(callback)); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioUnitSetProperty failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = AudioUnitUninitialize(input_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioUnitUninitialize failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef OSX_ENABLED
 | 
					
						
							|  |  |  | 		AudioObjectPropertyAddress prop; | 
					
						
							|  |  |  | 		prop.mSelector = kAudioHardwarePropertyDefaultInputDevice; | 
					
						
							|  |  |  | 		prop.mScope = kAudioObjectPropertyScopeGlobal; | 
					
						
							|  |  |  | 		prop.mElement = kAudioObjectPropertyElementMaster; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioObjectRemovePropertyListener failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		result = AudioComponentInstanceDispose(input_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							|  |  |  | 			ERR_PRINT("AudioComponentInstanceDispose failed"); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		input_unit = nullptr; | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 		unlock(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Error AudioDriverCoreAudio::capture_start() { | 
					
						
							| 
									
										
										
										
											2020-01-20 13:11:47 +01:00
										 |  |  | 	input_buffer_init(buffer_frames); | 
					
						
							| 
									
										
										
										
											2018-10-19 16:32:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	OSStatus result = AudioOutputUnitStart(input_unit); | 
					
						
							|  |  |  | 	if (result != noErr) { | 
					
						
							| 
									
										
										
										
											2019-11-06 17:03:04 +01:00
										 |  |  | 		ERR_PRINT("AudioOutputUnitStart failed, code: " + itos(result)); | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | Error AudioDriverCoreAudio::capture_stop() { | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 	if (input_unit) { | 
					
						
							|  |  |  | 		OSStatus result = AudioOutputUnitStop(input_unit); | 
					
						
							|  |  |  | 		if (result != noErr) { | 
					
						
							| 
									
										
										
										
											2019-11-06 17:03:04 +01:00
										 |  |  | 			ERR_PRINT("AudioOutputUnitStop failed, code: " + itos(result)); | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef OSX_ENABLED
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Array AudioDriverCoreAudio::_get_device_list(bool capture) { | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 	Array list; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	list.push_back("Default"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	AudioObjectPropertyAddress prop; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prop.mSelector = kAudioHardwarePropertyDevices; | 
					
						
							|  |  |  | 	prop.mScope = kAudioObjectPropertyScopeGlobal; | 
					
						
							|  |  |  | 	prop.mElement = kAudioObjectPropertyElementMaster; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UInt32 size = 0; | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 	AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 	AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 	AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	UInt32 deviceCount = size / sizeof(AudioDeviceID); | 
					
						
							|  |  |  | 	for (UInt32 i = 0; i < deviceCount; i++) { | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		prop.mSelector = kAudioDevicePropertyStreamConfiguration; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		AudioBufferList *bufferList = (AudioBufferList *)malloc(size); | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		UInt32 channelCount = 0; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 			channelCount += bufferList->mBuffers[j].mNumberChannels; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		free(bufferList); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		if (channelCount >= 1) { | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 			CFStringRef cfname; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			size = sizeof(CFStringRef); | 
					
						
							|  |  |  | 			prop.mSelector = kAudioObjectPropertyName; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 			AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, &cfname); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			CFIndex length = CFStringGetLength(cfname); | 
					
						
							|  |  |  | 			CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; | 
					
						
							|  |  |  | 			char *buffer = (char *)malloc(maxSize); | 
					
						
							|  |  |  | 			if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { | 
					
						
							|  |  |  | 				// Append the ID to the name in case we have devices with duplicate name
 | 
					
						
							|  |  |  | 				list.push_back(String(buffer) + " (" + itos(audioDevices[i]) + ")"); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			free(buffer); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	free(audioDevices); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return list; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | void AudioDriverCoreAudio::_set_device(const String &device, bool capture) { | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 	AudioDeviceID deviceId; | 
					
						
							|  |  |  | 	bool found = false; | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 	if (device != "Default") { | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		AudioObjectPropertyAddress prop; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		prop.mSelector = kAudioHardwarePropertyDevices; | 
					
						
							|  |  |  | 		prop.mScope = kAudioObjectPropertyScopeGlobal; | 
					
						
							|  |  |  | 		prop.mElement = kAudioObjectPropertyElementMaster; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		UInt32 size = 0; | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &prop, 0, nullptr, &size); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		AudioDeviceID *audioDevices = (AudioDeviceID *)malloc(size); | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		AudioObjectGetPropertyData(kAudioObjectSystemObject, &prop, 0, nullptr, &size, audioDevices); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		UInt32 deviceCount = size / sizeof(AudioDeviceID); | 
					
						
							|  |  |  | 		for (UInt32 i = 0; i < deviceCount && !found; i++) { | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 			prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 			prop.mSelector = kAudioDevicePropertyStreamConfiguration; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 			AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, nullptr, &size); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 			AudioBufferList *bufferList = (AudioBufferList *)malloc(size); | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 			AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, bufferList); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 			UInt32 channelCount = 0; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 			for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++) | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 				channelCount += bufferList->mBuffers[j].mNumberChannels; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			free(bufferList); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 			if (channelCount >= 1) { | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 				CFStringRef cfname; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				size = sizeof(CFStringRef); | 
					
						
							|  |  |  | 				prop.mSelector = kAudioObjectPropertyName; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 				AudioObjectGetPropertyData(audioDevices[i], &prop, 0, nullptr, &size, &cfname); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				CFIndex length = CFStringGetLength(cfname); | 
					
						
							|  |  |  | 				CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; | 
					
						
							|  |  |  | 				char *buffer = (char *)malloc(maxSize); | 
					
						
							|  |  |  | 				if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) { | 
					
						
							|  |  |  | 					String name = String(buffer) + " (" + itos(audioDevices[i]) + ")"; | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 					if (name == device) { | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 						deviceId = audioDevices[i]; | 
					
						
							|  |  |  | 						found = true; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				free(buffer); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		free(audioDevices); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!found) { | 
					
						
							| 
									
										
										
										
											2018-07-11 08:38:32 -03:00
										 |  |  | 		// If we haven't found the desired device get the system default one
 | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		UInt32 size = sizeof(AudioDeviceID); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; | 
					
						
							|  |  |  | 		AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 		OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, nullptr, &size, &deviceId); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		ERR_FAIL_COND(result != noErr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		found = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (found) { | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 		OSStatus result = AudioUnitSetProperty(capture ? input_unit : audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID)); | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 		ERR_FAIL_COND(result != noErr); | 
					
						
							| 
									
										
										
										
											2018-07-27 13:54:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 		if (capture) { | 
					
						
							| 
									
										
										
										
											2020-01-20 13:11:47 +01:00
										 |  |  | 			// Reset audio input to keep synchronisation.
 | 
					
						
							|  |  |  | 			input_position = 0; | 
					
						
							|  |  |  | 			input_size = 0; | 
					
						
							| 
									
										
										
										
											2018-10-11 13:22:30 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-25 00:43:51 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | Array AudioDriverCoreAudio::get_device_list() { | 
					
						
							|  |  |  | 	return _get_device_list(); | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | String AudioDriverCoreAudio::get_device() { | 
					
						
							|  |  |  | 	return device_name; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-11 08:38:32 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | void AudioDriverCoreAudio::set_device(String device) { | 
					
						
							|  |  |  | 	device_name = device; | 
					
						
							| 
									
										
										
										
											2018-07-11 08:38:32 -03:00
										 |  |  | 	if (active) { | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | 		_set_device(device_name); | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | void AudioDriverCoreAudio::capture_set_device(const String &p_name) { | 
					
						
							|  |  |  | 	capture_device_name = p_name; | 
					
						
							|  |  |  | 	if (active) { | 
					
						
							|  |  |  | 		_set_device(capture_device_name, true); | 
					
						
							| 
									
										
										
										
											2017-07-30 16:35:08 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-02-27 07:54:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | Array AudioDriverCoreAudio::capture_get_device_list() { | 
					
						
							|  |  |  | 	return _get_device_list(true); | 
					
						
							| 
									
										
										
										
											2018-02-27 07:54:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | String AudioDriverCoreAudio::capture_get_device() { | 
					
						
							|  |  |  | 	return capture_device_name; | 
					
						
							| 
									
										
										
										
											2018-02-27 07:54:56 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 17:01:17 +02:00
										 |  |  | AudioDriverCoreAudio::AudioDriverCoreAudio() { | 
					
						
							| 
									
										
										
										
											2017-08-17 18:35:55 -03:00
										 |  |  | 	samples_in.clear(); | 
					
						
							| 
									
										
										
										
											2018-07-25 15:35:52 -03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-11-02 11:31:01 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 17:01:17 +02:00
										 |  |  | #endif // COREAUDIO_ENABLED
 |