From 5a7b6b715952d770c48af056a0ae1f689faa956b Mon Sep 17 00:00:00 2001 From: Patrick Exner Date: Wed, 25 Jun 2025 15:00:19 +0200 Subject: [PATCH] Add permission request for Apple embedded platforms, fix microphone input Co-Authored-By: Miguel de Icaza Supersedes https://github.com/godotengine/godot/pull/107233 Fixes https://github.com/godotengine/godot-proposals/issues/12563 Fixes https://github.com/godotengine/godot/issues/33885 Superseding Miguel's PR to get it in during the beta stage. --- doc/classes/OS.xml | 4 ++- drivers/apple_embedded/os_apple_embedded.h | 3 ++ drivers/apple_embedded/os_apple_embedded.mm | 32 +++++++++++++++++++++ drivers/coreaudio/audio_driver_coreaudio.h | 2 +- drivers/coreaudio/audio_driver_coreaudio.mm | 13 ++++----- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index 018e42a6572..774ee21ed56 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -302,6 +302,7 @@ On Android devices: Returns the list of dangerous permissions that have been granted. On macOS: Returns the list of granted permissions and user selected folders accessible to the application (sandboxed applications only). Use the native file dialog to request folder access permission. + On iOS, visionOS: Returns the list of granted permissions. @@ -781,8 +782,9 @@ - [code]OS.request_permission("android.permission.READ_EXTERNAL_STORAGE")[/code] - [code]OS.request_permission("android.permission.POST_NOTIFICATIONS")[/code] - [code]OS.request_permission("macos.permission.RECORD_SCREEN")[/code] + - [code]OS.request_permission("appleembedded.permission.AUDIO_RECORD")[/code] [b]Note:[/b] On Android, permission must be checked during export. - [b]Note:[/b] This method is implemented on Android and macOS. + [b]Note:[/b] This method is implemented on Android, macOS, and visionOS platforms. diff --git a/drivers/apple_embedded/os_apple_embedded.h b/drivers/apple_embedded/os_apple_embedded.h index 148fada9a08..e799c0a71a7 100644 --- a/drivers/apple_embedded/os_apple_embedded.h +++ b/drivers/apple_embedded/os_apple_embedded.h @@ -137,6 +137,9 @@ public: void on_exit_background(); virtual Rect2 calculate_boot_screen_rect(const Size2 &p_window_size, const Size2 &p_imgrect_size) const override; + + virtual bool request_permission(const String &p_name) override; + virtual Vector get_granted_permissions() const override; }; #endif // APPLE_EMBEDDED_ENABLED diff --git a/drivers/apple_embedded/os_apple_embedded.mm b/drivers/apple_embedded/os_apple_embedded.mm index 6468c688aca..07d1f8a270c 100644 --- a/drivers/apple_embedded/os_apple_embedded.mm +++ b/drivers/apple_embedded/os_apple_embedded.mm @@ -43,6 +43,7 @@ #import "drivers/apple/os_log_logger.h" #include "main/main.h" +#import #import #import #import @@ -733,4 +734,35 @@ Rect2 OS_AppleEmbedded::calculate_boot_screen_rect(const Size2 &p_window_size, c } } +bool OS_AppleEmbedded::request_permission(const String &p_name) { + if (p_name == "appleembedded.permission.AUDIO_RECORD") { + if (@available(iOS 17.0, *)) { + AVAudioApplicationRecordPermission permission = [AVAudioApplication sharedInstance].recordPermission; + if (permission == AVAudioApplicationRecordPermissionGranted) { + // Permission already granted, you can start recording. + return true; + } else if (permission == AVAudioApplicationRecordPermissionDenied) { + // Permission denied, or not yet granted. + return false; + } else { + // Request the permission, but for now return false as documented. + [AVAudioApplication requestRecordPermissionWithCompletionHandler:^(BOOL granted) { + get_main_loop()->emit_signal(SNAME("on_request_permissions_result"), p_name, granted); + }]; + } + } + } + return false; +} + +Vector OS_AppleEmbedded::get_granted_permissions() const { + Vector ret; + + if (@available(iOS 17.0, *)) { + if ([AVAudioApplication sharedInstance].recordPermission == AVAudioApplicationRecordPermissionGranted) { + ret.push_back("appleembedded.permission.AUDIO_RECORD"); + } + } + return ret; +} #endif // APPLE_EMBEDDED_ENABLED diff --git a/drivers/coreaudio/audio_driver_coreaudio.h b/drivers/coreaudio/audio_driver_coreaudio.h index 64c77fe97ff..36f5eee73b7 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.h +++ b/drivers/coreaudio/audio_driver_coreaudio.h @@ -59,7 +59,7 @@ class AudioDriverCoreAudio : public AudioDriver { unsigned int capture_buffer_frames = 0; Vector samples_in; - Vector input_buf; + unsigned int buffer_size = 0; #ifdef MACOS_ENABLED PackedStringArray _get_device_list(bool capture = false); diff --git a/drivers/coreaudio/audio_driver_coreaudio.mm b/drivers/coreaudio/audio_driver_coreaudio.mm index d9efa9c658e..6f9f690d528 100644 --- a/drivers/coreaudio/audio_driver_coreaudio.mm +++ b/drivers/coreaudio/audio_driver_coreaudio.mm @@ -241,14 +241,15 @@ OSStatus AudioDriverCoreAudio::input_callback(void *inRefCon, AudioBufferList bufferList; bufferList.mNumberBuffers = 1; - bufferList.mBuffers[0].mData = ad->input_buf.ptrw(); + bufferList.mBuffers[0].mData = nullptr; bufferList.mBuffers[0].mNumberChannels = ad->capture_channels; - bufferList.mBuffers[0].mDataByteSize = ad->input_buf.size() * sizeof(int16_t); + bufferList.mBuffers[0].mDataByteSize = ad->buffer_size * sizeof(int16_t); OSStatus result = AudioUnitRender(ad->input_unit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList); if (result == noErr) { + int16_t *data = (int16_t *)bufferList.mBuffers[0].mData; for (unsigned int i = 0; i < inNumberFrames * ad->capture_channels; i++) { - int32_t sample = ad->input_buf[i] << 16; + int32_t sample = data[i] << 16; ad->input_buffer_write(sample); if (ad->capture_channels == 1) { @@ -393,9 +394,6 @@ Error AudioDriverCoreAudio::init_input_device() { UInt32 flag = 1; 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 MACOS_ENABLED @@ -460,8 +458,7 @@ Error AudioDriverCoreAudio::init_input_device() { // Sample rate is independent of channels (ref: https://stackoverflow.com/questions/11048825/audio-sample-frequency-rely-on-channels) capture_buffer_frames = closest_power_of_2(latency * (uint32_t)capture_mix_rate / (uint32_t)1000); - unsigned int buffer_size = capture_buffer_frames * capture_channels; - input_buf.resize(buffer_size); + buffer_size = capture_buffer_frames * capture_channels; AURenderCallbackStruct callback; memset(&callback, 0, sizeof(AURenderCallbackStruct));