Browse Source

Implemented capture device selection for CoreAudio

Marcelo Fernandez 7 years ago
parent
commit
2cf8da9d9f

+ 141 - 94
drivers/coreaudio/audio_driver_coreaudio.cpp

@@ -38,6 +38,20 @@
 #define kInputBus 1
 
 #ifdef OSX_ENABLED
+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;
+}
+
 OSStatus AudioDriverCoreAudio::output_device_address_cb(AudioObjectID inObjectID,
 		UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
 		void *inClientData) {
@@ -80,6 +94,11 @@ Error AudioDriverCoreAudio::init() {
 
 	result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
 	ERR_FAIL_COND_V(result != noErr, FAILED);
+
+	prop.mSelector = kAudioHardwarePropertyDefaultInputDevice;
+
+	result = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &prop, &input_device_address_cb, this);
+	ERR_FAIL_COND_V(result != noErr, FAILED);
 #endif
 
 	AudioStreamBasicDescription strdesc;
@@ -276,9 +295,94 @@ AudioDriver::SpeakerMode AudioDriverCoreAudio::get_speaker_mode() const {
 	return get_speaker_mode_by_total_channels(channels);
 };
 
+void AudioDriverCoreAudio::lock() {
+	if (mutex)
+		mutex->lock();
+};
+
+void AudioDriverCoreAudio::unlock() {
+	if (mutex)
+		mutex->unlock();
+};
+
+bool AudioDriverCoreAudio::try_lock() {
+	if (mutex)
+		return mutex->try_lock() == OK;
+	return true;
+}
+
+void AudioDriverCoreAudio::finish() {
+	OSStatus result;
+
+	lock();
+
+	AURenderCallbackStruct callback;
+	zeromem(&callback, sizeof(AURenderCallbackStruct));
+	result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
+	if (result != noErr) {
+		ERR_PRINT("AudioUnitSetProperty failed");
+	}
+
+	if (active) {
+		result = AudioOutputUnitStop(audio_unit);
+		if (result != noErr) {
+			ERR_PRINT("AudioOutputUnitStop failed");
+		}
+
+		active = false;
+	}
+
+	result = AudioUnitUninitialize(audio_unit);
+	if (result != noErr) {
+		ERR_PRINT("AudioUnitUninitialize failed");
+	}
+
 #ifdef OSX_ENABLED
+	AudioObjectPropertyAddress prop;
+	prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
+	prop.mScope = kAudioObjectPropertyScopeGlobal;
+	prop.mElement = kAudioObjectPropertyElementMaster;
 
-Array AudioDriverCoreAudio::get_device_list() {
+	result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
+	if (result != noErr) {
+		ERR_PRINT("AudioObjectRemovePropertyListener failed");
+	}
+#endif
+
+	result = AudioComponentInstanceDispose(audio_unit);
+	if (result != noErr) {
+		ERR_PRINT("AudioComponentInstanceDispose failed");
+	}
+
+	unlock();
+
+	if (mutex) {
+		memdelete(mutex);
+		mutex = NULL;
+	}
+};
+
+Error AudioDriverCoreAudio::capture_start() {
+
+	UInt32 flag = 1;
+	OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
+	ERR_FAIL_COND_V(result != noErr, FAILED);
+
+	return OK;
+}
+
+Error AudioDriverCoreAudio::capture_stop() {
+
+	UInt32 flag = 0;
+	OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
+	ERR_FAIL_COND_V(result != noErr, FAILED);
+
+	return OK;
+}
+
+#ifdef OSX_ENABLED
+
+Array AudioDriverCoreAudio::_get_device_list(bool capture) {
 
 	Array list;
 
@@ -297,20 +401,20 @@ Array AudioDriverCoreAudio::get_device_list() {
 
 	UInt32 deviceCount = size / sizeof(AudioDeviceID);
 	for (UInt32 i = 0; i < deviceCount; i++) {
-		prop.mScope = kAudioDevicePropertyScopeOutput;
+		prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
 		prop.mSelector = kAudioDevicePropertyStreamConfiguration;
 
 		AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size);
 		AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
 		AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList);
 
-		UInt32 outputChannelCount = 0;
+		UInt32 channelCount = 0;
 		for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++)
-			outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+			channelCount += bufferList->mBuffers[j].mNumberChannels;
 
 		free(bufferList);
 
-		if (outputChannelCount >= 1) {
+		if (channelCount >= 1) {
 			CFStringRef cfname;
 
 			size = sizeof(CFStringRef);
@@ -335,21 +439,11 @@ Array AudioDriverCoreAudio::get_device_list() {
 	return list;
 }
 
-String AudioDriverCoreAudio::get_device() {
-
-	return device_name;
-}
-
-void AudioDriverCoreAudio::set_device(String device) {
-
-	device_name = device;
-	if (!active) {
-		return;
-	}
+void AudioDriverCoreAudio::_set_device(const String &device, bool capture) {
 
 	AudioDeviceID deviceId;
 	bool found = false;
-	if (device_name != "Default") {
+	if (device != "Default") {
 		AudioObjectPropertyAddress prop;
 
 		prop.mSelector = kAudioHardwarePropertyDevices;
@@ -363,20 +457,20 @@ void AudioDriverCoreAudio::set_device(String device) {
 
 		UInt32 deviceCount = size / sizeof(AudioDeviceID);
 		for (UInt32 i = 0; i < deviceCount && !found; i++) {
-			prop.mScope = kAudioDevicePropertyScopeOutput;
+			prop.mScope = capture ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
 			prop.mSelector = kAudioDevicePropertyStreamConfiguration;
 
 			AudioObjectGetPropertyDataSize(audioDevices[i], &prop, 0, NULL, &size);
 			AudioBufferList *bufferList = (AudioBufferList *)malloc(size);
 			AudioObjectGetPropertyData(audioDevices[i], &prop, 0, NULL, &size, bufferList);
 
-			UInt32 outputChannelCount = 0;
+			UInt32 channelCount = 0;
 			for (UInt32 j = 0; j < bufferList->mNumberBuffers; j++)
-				outputChannelCount += bufferList->mBuffers[j].mNumberChannels;
+				channelCount += bufferList->mBuffers[j].mNumberChannels;
 
 			free(bufferList);
 
-			if (outputChannelCount >= 1) {
+			if (channelCount >= 1) {
 				CFStringRef cfname;
 
 				size = sizeof(CFStringRef);
@@ -389,7 +483,7 @@ void AudioDriverCoreAudio::set_device(String device) {
 				char *buffer = (char *)malloc(maxSize);
 				if (CFStringGetCString(cfname, buffer, maxSize, kCFStringEncodingUTF8)) {
 					String name = String(buffer) + " (" + itos(audioDevices[i]) + ")";
-					if (name == device_name) {
+					if (name == device) {
 						deviceId = audioDevices[i];
 						found = true;
 					}
@@ -405,7 +499,8 @@ void AudioDriverCoreAudio::set_device(String device) {
 	if (!found) {
 		// If we haven't found the desired device get the system default one
 		UInt32 size = sizeof(AudioDeviceID);
-		AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+		UInt32 elem = capture ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
+		AudioObjectPropertyAddress property = { elem, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
 
 		OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property, 0, NULL, &size, &deviceId);
 		ERR_FAIL_COND(result != noErr);
@@ -414,98 +509,49 @@ void AudioDriverCoreAudio::set_device(String device) {
 	}
 
 	if (found) {
-		OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceId, sizeof(AudioDeviceID));
+		OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, capture ? kInputBus : kOutputBus, &deviceId, sizeof(AudioDeviceID));
 		ERR_FAIL_COND(result != noErr);
 	}
 }
 
-#endif
-
-void AudioDriverCoreAudio::lock() {
-	if (mutex)
-		mutex->lock();
-};
-
-void AudioDriverCoreAudio::unlock() {
-	if (mutex)
-		mutex->unlock();
-};
+Array AudioDriverCoreAudio::get_device_list() {
 
-bool AudioDriverCoreAudio::try_lock() {
-	if (mutex)
-		return mutex->try_lock() == OK;
-	return true;
+	return _get_device_list();
 }
 
-void AudioDriverCoreAudio::finish() {
-	OSStatus result;
+String AudioDriverCoreAudio::get_device() {
 
-	lock();
+	return device_name;
+}
 
-	AURenderCallbackStruct callback;
-	zeromem(&callback, sizeof(AURenderCallbackStruct));
-	result = AudioUnitSetProperty(audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callback, sizeof(callback));
-	if (result != noErr) {
-		ERR_PRINT("AudioUnitSetProperty failed");
-	}
+void AudioDriverCoreAudio::set_device(String device) {
 
+	device_name = device;
 	if (active) {
-		result = AudioOutputUnitStop(audio_unit);
-		if (result != noErr) {
-			ERR_PRINT("AudioOutputUnitStop failed");
-		}
-
-		active = false;
-	}
-
-	result = AudioUnitUninitialize(audio_unit);
-	if (result != noErr) {
-		ERR_PRINT("AudioUnitUninitialize failed");
-	}
-
-#ifdef OSX_ENABLED
-	AudioObjectPropertyAddress prop;
-	prop.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
-	prop.mScope = kAudioObjectPropertyScopeGlobal;
-	prop.mElement = kAudioObjectPropertyElementMaster;
-
-	result = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &prop, &output_device_address_cb, this);
-	if (result != noErr) {
-		ERR_PRINT("AudioObjectRemovePropertyListener failed");
-	}
-#endif
-
-	result = AudioComponentInstanceDispose(audio_unit);
-	if (result != noErr) {
-		ERR_PRINT("AudioComponentInstanceDispose failed");
+		_set_device(device_name);
 	}
+}
 
-	unlock();
+void AudioDriverCoreAudio::capture_set_device(const String &p_name) {
 
-	if (mutex) {
-		memdelete(mutex);
-		mutex = NULL;
+	capture_device_name = p_name;
+	if (active) {
+		_set_device(capture_device_name, true);
 	}
-};
-
-Error AudioDriverCoreAudio::capture_start() {
+}
 
-	UInt32 flag = 1;
-	OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
-	ERR_FAIL_COND_V(result != noErr, FAILED);
+Array AudioDriverCoreAudio::capture_get_device_list() {
 
-	return OK;
+	return _get_device_list(true);
 }
 
-Error AudioDriverCoreAudio::capture_stop() {
-
-	UInt32 flag = 0;
-	OSStatus result = AudioUnitSetProperty(audio_unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag));
-	ERR_FAIL_COND_V(result != noErr, FAILED);
+String AudioDriverCoreAudio::capture_get_device() {
 
-	return OK;
+	return capture_device_name;
 }
 
+#endif
+
 AudioDriverCoreAudio::AudioDriverCoreAudio() {
 	active = false;
 	mutex = NULL;
@@ -518,7 +564,8 @@ AudioDriverCoreAudio::AudioDriverCoreAudio() {
 	samples_in.clear();
 
 	device_name = "Default";
-};
+	capture_device_name = "Default";
+}
 
 AudioDriverCoreAudio::~AudioDriverCoreAudio(){};
 

+ 19 - 5
drivers/coreaudio/audio_driver_coreaudio.h

@@ -48,6 +48,7 @@ class AudioDriverCoreAudio : public AudioDriver {
 	Mutex *mutex;
 
 	String device_name;
+	String capture_device_name;
 
 	int mix_rate;
 	unsigned int channels;
@@ -57,6 +58,13 @@ class AudioDriverCoreAudio : public AudioDriver {
 	Vector<int16_t> input_buf;
 
 #ifdef OSX_ENABLED
+	Array _get_device_list(bool capture = false);
+	void _set_device(const String &device, bool capture = false);
+
+	static OSStatus input_device_address_cb(AudioObjectID inObjectID,
+			UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
+			void *inClientData);
+
 	static OSStatus output_device_address_cb(AudioObjectID inObjectID,
 			UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses,
 			void *inClientData);
@@ -83,11 +91,7 @@ public:
 	virtual void start();
 	virtual int get_mix_rate() const;
 	virtual SpeakerMode get_speaker_mode() const;
-#ifdef OSX_ENABLED
-	virtual Array get_device_list();
-	virtual String get_device();
-	virtual void set_device(String device);
-#endif
+
 	virtual void lock();
 	virtual void unlock();
 	virtual void finish();
@@ -98,6 +102,16 @@ public:
 	bool try_lock();
 	void stop();
 
+#ifdef OSX_ENABLED
+	virtual Array get_device_list();
+	virtual String get_device();
+	virtual void set_device(String device);
+
+	virtual Array capture_get_device_list();
+	virtual void capture_set_device(const String &p_name);
+	virtual String capture_get_device();
+#endif
+
 	AudioDriverCoreAudio();
 	~AudioDriverCoreAudio();
 };

+ 1 - 1
drivers/wasapi/audio_driver_wasapi.cpp

@@ -786,7 +786,7 @@ Error AudioDriverWASAPI::capture_stop() {
 	return FAILED;
 }
 
-void AudioDriverWASAPI::capture_set_device(StringName p_name) {
+void AudioDriverWASAPI::capture_set_device(const String &p_name) {
 
 	lock();
 	audio_input.new_device = p_name;

+ 2 - 2
drivers/wasapi/audio_driver_wasapi.h

@@ -120,8 +120,8 @@ public:
 	virtual Error capture_start();
 	virtual Error capture_stop();
 	virtual Array capture_get_device_list();
-	virtual void capture_set_device(StringName p_name);
-	virtual StringName capture_get_device();
+	virtual void capture_set_device(const String &p_name);
+	virtual String capture_get_device();
 
 	AudioDriverWASAPI();
 };

+ 7 - 3
servers/audio_server.cpp

@@ -1215,14 +1215,14 @@ Array AudioServer::capture_get_device_list() {
 	return AudioDriver::get_singleton()->capture_get_device_list();
 }
 
-StringName AudioServer::capture_get_device() {
+String AudioServer::capture_get_device() {
 
 	return AudioDriver::get_singleton()->capture_get_device();
 }
 
-void AudioServer::capture_set_device(StringName device) {
+void AudioServer::capture_set_device(const String &p_name) {
 
-	AudioDriver::get_singleton()->capture_set_device(device);
+	AudioDriver::get_singleton()->capture_set_device(p_name);
 }
 
 void AudioServer::_bind_methods() {
@@ -1275,6 +1275,10 @@ void AudioServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device);
 	ClassDB::bind_method(D_METHOD("set_device"), &AudioServer::set_device);
 
+	ClassDB::bind_method(D_METHOD("capture_get_device_list"), &AudioServer::capture_get_device_list);
+	ClassDB::bind_method(D_METHOD("capture_get_device"), &AudioServer::capture_get_device);
+	ClassDB::bind_method(D_METHOD("capture_set_device"), &AudioServer::capture_set_device);
+
 	ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout);
 	ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout);
 

+ 4 - 4
servers/audio_server.h

@@ -98,8 +98,8 @@ public:
 
 	virtual Error capture_start() { return FAILED; }
 	virtual Error capture_stop() { return FAILED; }
-	virtual void capture_set_device(StringName p_name) {}
-	virtual StringName capture_get_device() { return "Default"; }
+	virtual void capture_set_device(const String &p_name) {}
+	virtual String capture_get_device() { return "Default"; }
 	virtual Array capture_get_device_list(); // TODO: convert this and get_device_list to PoolStringArray
 
 	virtual float get_latency() { return 0; }
@@ -362,8 +362,8 @@ public:
 	void set_device(String device);
 
 	Array capture_get_device_list();
-	StringName capture_get_device();
-	void capture_set_device(StringName device);
+	String capture_get_device();
+	void capture_set_device(const String &p_name);
 
 	float get_output_latency() { return output_latency; }
 	AudioServer();