diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index 92dd51846..df445a313 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -17,6 +17,7 @@
+
sl) {
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java
index 7f3328673..cc602aeb5 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/utils/SettingsFile.java
@@ -60,6 +60,7 @@ public final class SettingsFile {
public static final String KEY_AUDIO_OUTPUT_ENGINE = "output_engine";
public static final String KEY_ENABLE_AUDIO_STRETCHING = "enable_audio_stretching";
public static final String KEY_VOLUME = "volume";
+ public static final String KEY_MIC_INPUT_TYPE = "mic_input_type";
public static final String KEY_USE_VIRTUAL_SD = "use_virtual_sd";
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 8fe02f210..04c609dde 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -18,13 +18,15 @@ add_library(main SHARED
game_info.h
id_cache.cpp
id_cache.h
+ mic.cpp
+ mic.h
native.cpp
native.h
ndk_motion.cpp
ndk_motion.h
)
-target_link_libraries(main PRIVATE common core input_common network)
+target_link_libraries(main PRIVATE audio_core common core input_common network)
target_link_libraries(main PRIVATE android camera2ndk mediandk jnigraphics EGL glad inih log yuv)
set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} main)
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 93ff627c7..9451d3439 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -170,7 +170,7 @@ void Config::ReadValues() {
Settings::values.mic_input_device =
sdl2_config->GetString("Audio", "mic_input_device", "Default");
Settings::values.mic_input_type =
- static_cast(sdl2_config->GetInteger("Audio", "mic_input_type", 0));
+ static_cast(sdl2_config->GetInteger("Audio", "mic_input_type", 1));
// Data Storage
Settings::values.use_virtual_sd =
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
index 894a7f8b1..f8f4f4151 100644
--- a/src/android/app/src/main/jni/default_ini.h
+++ b/src/android/app/src/main/jni/default_ini.h
@@ -201,6 +201,10 @@ enable_audio_stretching =
# auto (default): Auto-select
output_device =
+# Which mic input type to use.
+# 0: None, 1 (default): Real device, 2: Static noise
+mic_input_type =
+
# Output volume.
# 1.0 (default): 100%, 0.0; mute
volume =
diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp
index 2ed4bed57..b7197389f 100644
--- a/src/android/app/src/main/jni/id_cache.cpp
+++ b/src/android/app/src/main/jni/id_cache.cpp
@@ -26,6 +26,7 @@ static jmethodID s_is_portrait_mode;
static jmethodID s_landscape_screen_layout;
static jmethodID s_exit_emulation_activity;
static jmethodID s_request_camera_permission;
+static jmethodID s_request_mic_permission;
namespace IDCache {
@@ -80,6 +81,10 @@ jmethodID GetRequestCameraPermission() {
return s_request_camera_permission;
}
+jmethodID GetRequestMicPermission() {
+ return s_request_mic_permission;
+}
+
} // namespace IDCache
#ifdef __cplusplus
@@ -120,6 +125,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
env->GetStaticMethodID(s_native_library_class, "exitEmulationActivity", "(I)V");
s_request_camera_permission =
env->GetStaticMethodID(s_native_library_class, "RequestCameraPermission", "()Z");
+ s_request_mic_permission =
+ env->GetStaticMethodID(s_native_library_class, "RequestMicPermission", "()Z");
MiiSelector::InitJNI(env);
SoftwareKeyboard::InitJNI(env);
diff --git a/src/android/app/src/main/jni/id_cache.h b/src/android/app/src/main/jni/id_cache.h
index f210e848c..64c185e80 100644
--- a/src/android/app/src/main/jni/id_cache.h
+++ b/src/android/app/src/main/jni/id_cache.h
@@ -19,6 +19,7 @@ jmethodID GetIsPortraitMode();
jmethodID GetLandscapeScreenLayout();
jmethodID GetExitEmulationActivity();
jmethodID GetRequestCameraPermission();
+jmethodID GetRequestMicPermission();
} // namespace IDCache
diff --git a/src/android/app/src/main/jni/mic.cpp b/src/android/app/src/main/jni/mic.cpp
new file mode 100644
index 000000000..90191fbcf
--- /dev/null
+++ b/src/android/app/src/main/jni/mic.cpp
@@ -0,0 +1,38 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include
+#include "common/logging/log.h"
+#include "jni/id_cache.h"
+#include "jni/mic.h"
+
+#ifdef HAVE_CUBEB
+#include "audio_core/cubeb_input.h"
+#endif
+
+namespace Mic {
+
+AndroidFactory::~AndroidFactory() = default;
+
+std::unique_ptr AndroidFactory::Create(std::string mic_device_name) {
+#ifdef HAVE_CUBEB
+ if (!permission_granted) {
+ JNIEnv* env = IDCache::GetEnvForThread();
+ permission_granted = env->CallStaticBooleanMethod(IDCache::GetNativeLibraryClass(),
+ IDCache::GetRequestMicPermission());
+ }
+
+ if (permission_granted) {
+ return std::make_unique(std::move(mic_device_name));
+ } else {
+ LOG_WARNING(Frontend, "Mic permissions denied");
+ return std::make_unique();
+ }
+#else
+ LOG_WARNING(Frontend, "No cubeb support");
+ return std::make_unique();
+#endif
+}
+
+} // namespace Mic
diff --git a/src/android/app/src/main/jni/mic.h b/src/android/app/src/main/jni/mic.h
new file mode 100644
index 000000000..d790d52e5
--- /dev/null
+++ b/src/android/app/src/main/jni/mic.h
@@ -0,0 +1,21 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/frontend/mic.h"
+
+namespace Mic {
+
+class AndroidFactory final : public Frontend::Mic::RealMicFactory {
+public:
+ ~AndroidFactory() override;
+
+ std::unique_ptr Create(std::string mic_device_name) override;
+
+private:
+ bool permission_granted = false;
+};
+
+} // namespace Mic
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index ae23af37a..24c66e187 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -17,6 +17,7 @@
#include "core/core.h"
#include "core/frontend/applets/default_applets.h"
#include "core/frontend/camera/factory.h"
+#include "core/frontend/mic.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/nfc/nfc.h"
@@ -30,6 +31,7 @@
#include "jni/emu_window/emu_window.h"
#include "jni/game_info.h"
#include "jni/id_cache.h"
+#include "jni/mic.h"
#include "jni/native.h"
#include "jni/ndk_motion.h"
#include "video_core/renderer_base.h"
@@ -137,6 +139,9 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
system.RegisterMiiSelector(std::make_shared());
system.RegisterSoftwareKeyboard(std::make_shared());
+ // Register real Mic factory
+ Frontend::Mic::RegisterRealMicFactory(std::make_unique());
+
InputManager::Init();
window->MakeCurrent();
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index 928b7c024..174151c39 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -141,4 +141,16 @@
- 2
- 3
+
+
+ - None
+ - Real Device
+ - Static Noise
+
+
+
+ - 0
+ - 1
+ - 2
+
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 40bfbee8e..34a76d559 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -99,6 +99,7 @@
Enable audio stretching
Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.
+ Audio Input Device
Clear
@@ -178,4 +179,8 @@
Select Image
Camera
Citra needs to access your camera to emulate the 3DS\'s cameras.\n\nAlternatively, you can also set \"Image Source\" to \"Still Image\" in Camera Settings.
+
+
+ Microphone
+ Citra needs to access your microphone to emulate the 3DS\'s microphone.\n\nAlternatively, you can also change \"Audio Input Device\" in Audio Settings.
diff --git a/src/audio_core/cubeb_input.cpp b/src/audio_core/cubeb_input.cpp
index 04d89e525..91c2013c1 100644
--- a/src/audio_core/cubeb_input.cpp
+++ b/src/audio_core/cubeb_input.cpp
@@ -189,4 +189,11 @@ std::vector ListCubebInputDevices() {
cubeb_destroy(ctx);
return device_list;
}
+
+CubebFactory::~CubebFactory() = default;
+
+std::unique_ptr CubebFactory::Create(std::string mic_device_name) {
+ return std::make_unique(std::move(mic_device_name));
+}
+
} // namespace AudioCore
diff --git a/src/audio_core/cubeb_input.h b/src/audio_core/cubeb_input.h
index 2e752da7a..f81fb5ac2 100644
--- a/src/audio_core/cubeb_input.h
+++ b/src/audio_core/cubeb_input.h
@@ -31,4 +31,11 @@ private:
std::vector ListCubebInputDevices();
+class CubebFactory final : public Frontend::Mic::RealMicFactory {
+public:
+ ~CubebFactory() override;
+
+ std::unique_ptr Create(std::string mic_device_name) override;
+};
+
} // namespace AudioCore
diff --git a/src/core/frontend/mic.cpp b/src/core/frontend/mic.cpp
index 543a23767..c9dbc9cc7 100644
--- a/src/core/frontend/mic.cpp
+++ b/src/core/frontend/mic.cpp
@@ -5,6 +5,10 @@
#include
#include "core/frontend/mic.h"
+#ifdef HAVE_CUBEB
+#include "audio_core/cubeb_input.h"
+#endif
+
namespace Frontend::Mic {
constexpr std::array NOISE_SAMPLE_8_BIT = {0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
@@ -57,4 +61,26 @@ Samples StaticMic::Read() {
return (sample_size == 8) ? CACHE_8_BIT : CACHE_16_BIT;
}
+RealMicFactory::~RealMicFactory() = default;
+
+NullFactory::~NullFactory() = default;
+
+std::unique_ptr NullFactory::Create([[maybe_unused]] std::string mic_device_name) {
+ return std::make_unique();
+}
+
+#ifdef HAVE_CUBEB
+static std::unique_ptr g_factory = std::make_unique();
+#else
+static std::unique_ptr g_factory = std::make_unique();
+#endif
+
+void RegisterRealMicFactory(std::unique_ptr factory) {
+ g_factory = std::move(factory);
+}
+
+std::unique_ptr CreateRealMic(std::string mic_device_name) {
+ return g_factory->Create(std::move(mic_device_name));
+}
+
} // namespace Frontend::Mic
diff --git a/src/core/frontend/mic.h b/src/core/frontend/mic.h
index 1504ad3ca..28738df52 100644
--- a/src/core/frontend/mic.h
+++ b/src/core/frontend/mic.h
@@ -115,4 +115,23 @@ private:
std::vector CACHE_16_BIT;
};
+/// Factory for creating a real Mic input device;
+class RealMicFactory {
+public:
+ virtual ~RealMicFactory();
+
+ virtual std::unique_ptr Create(std::string mic_device_name) = 0;
+};
+
+class NullFactory final : public RealMicFactory {
+public:
+ ~NullFactory() override;
+
+ std::unique_ptr Create(std::string mic_device_name) override;
+};
+
+void RegisterRealMicFactory(std::unique_ptr factory);
+
+std::unique_ptr CreateRealMic(std::string mic_device_name);
+
} // namespace Frontend::Mic
diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp
index de1bfc074..39e375d39 100644
--- a/src/core/hle/service/mic_u.cpp
+++ b/src/core/hle/service/mic_u.cpp
@@ -3,9 +3,6 @@
// Refer to the license.txt file included.
#include
-#ifdef HAVE_CUBEB
-#include "audio_core/cubeb_input.h"
-#endif
#include "common/archives.h"
#include "common/logging/log.h"
#include "core/core.h"
@@ -359,11 +356,7 @@ struct MIC_U::Impl {
new_mic = std::make_unique();
break;
case Settings::MicInputType::Real:
-#if HAVE_CUBEB
- new_mic = std::make_unique(Settings::values.mic_input_device);
-#else
- new_mic = std::make_unique();
-#endif
+ new_mic = Frontend::Mic::CreateRealMic(Settings::values.mic_input_device);
break;
case Settings::MicInputType::Static:
new_mic = std::make_unique();