mirror of
				https://git.h3cjp.net/H3cJP/citra.git
				synced 2025-11-04 09:05:08 +00:00 
			
		
		
		
	audio_core: Implement OpenAL backend (#6450)
This commit is contained in:
		
							parent
							
								
									ce553ab995
								
							
						
					
					
						commit
						055a58f01e
					
				
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -64,3 +64,6 @@
 | 
			
		|||
[submodule "dds-ktx"]
 | 
			
		||||
    path = externals/dds-ktx
 | 
			
		||||
    url = https://github.com/septag/dds-ktx
 | 
			
		||||
[submodule "externals/openal-soft"]
 | 
			
		||||
	path = externals/openal-soft
 | 
			
		||||
	url = https://github.com/kcat/openal-soft
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ if (MSVC)
 | 
			
		|||
endif()
 | 
			
		||||
 | 
			
		||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
 | 
			
		||||
option(ENABLE_OPENAL "Enables the OpenAL audio backend" ON)
 | 
			
		||||
 | 
			
		||||
CMAKE_DEPENDENT_OPTION(ENABLE_LIBUSB "Enable libusb for GameCube Adapter support" ON "NOT IOS" OFF)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								externals/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -190,3 +190,16 @@ if(ANDROID)
 | 
			
		|||
    add_subdirectory(libyuv)
 | 
			
		||||
    target_include_directories(yuv INTERFACE ./libyuv/include)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# OpenAL Soft
 | 
			
		||||
if (ENABLE_OPENAL)
 | 
			
		||||
    set(ALSOFT_EMBED_HRTF_DATA OFF CACHE BOOL "")
 | 
			
		||||
    set(ALSOFT_EXAMPLES OFF CACHE BOOL "")
 | 
			
		||||
    set(ALSOFT_INSTALL OFF CACHE BOOL "")
 | 
			
		||||
    set(ALSOFT_INSTALL_CONFIG OFF CACHE BOOL "")
 | 
			
		||||
    set(ALSOFT_INSTALL_HRTF_DATA OFF CACHE BOOL "")
 | 
			
		||||
    set(ALSOFT_INSTALL_AMBDEC_PRESETS OFF CACHE BOOL "")
 | 
			
		||||
    set(ALSOFT_UTILS OFF CACHE BOOL "")
 | 
			
		||||
    set(LIBTYPE "STATIC" CACHE STRING "")
 | 
			
		||||
    add_subdirectory(openal-soft EXCLUDE_FROM_ALL)
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								externals/openal-soft
									
									
									
									
										vendored
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								externals/openal-soft
									
									
									
									
										vendored
									
									
										Submodule
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
Subproject commit d9fed51aa6391debc31dbbca550f055c980afe70
 | 
			
		||||
| 
						 | 
				
			
			@ -400,10 +400,10 @@ public final class SettingsFragmentPresenter {
 | 
			
		|||
 | 
			
		||||
        SettingSection audioSection = mSettings.getSection(Settings.SECTION_AUDIO);
 | 
			
		||||
        Setting audioStretch = audioSection.getSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING);
 | 
			
		||||
        Setting micInputType = audioSection.getSetting(SettingsFile.KEY_MIC_INPUT_TYPE);
 | 
			
		||||
        Setting audioInputType = audioSection.getSetting(SettingsFile.KEY_AUDIO_INPUT_TYPE);
 | 
			
		||||
 | 
			
		||||
        sl.add(new CheckBoxSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING, Settings.SECTION_AUDIO, R.string.audio_stretch, R.string.audio_stretch_description, true, audioStretch));
 | 
			
		||||
        sl.add(new SingleChoiceSetting(SettingsFile.KEY_MIC_INPUT_TYPE, Settings.SECTION_AUDIO, R.string.audio_input_type, 0, R.array.audioInputTypeNames, R.array.audioInputTypeValues, 1, micInputType));
 | 
			
		||||
        sl.add(new SingleChoiceSetting(SettingsFile.KEY_AUDIO_INPUT_TYPE, Settings.SECTION_AUDIO, R.string.audio_input_type, 0, R.array.audioInputTypeNames, R.array.audioInputTypeValues, 0, audioInputType));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void addDebugSettings(ArrayList<SettingsItem> sl) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,10 +75,10 @@ public final class SettingsFile {
 | 
			
		|||
    public static final String KEY_PRELOAD_TEXTURES = "preload_textures";
 | 
			
		||||
    public static final String KEY_ASYNC_CUSTOM_LOADING = "async_custom_loading";
 | 
			
		||||
 | 
			
		||||
    public static final String KEY_AUDIO_OUTPUT_ENGINE = "output_engine";
 | 
			
		||||
    public static final String KEY_AUDIO_OUTPUT_TYPE = "output_type";
 | 
			
		||||
    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_AUDIO_INPUT_TYPE = "input_type";
 | 
			
		||||
 | 
			
		||||
    public static final String KEY_USE_VIRTUAL_SD = "use_virtual_sd";
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,8 +25,6 @@ add_library(citra-android SHARED
 | 
			
		|||
    game_settings.h
 | 
			
		||||
    id_cache.cpp
 | 
			
		||||
    id_cache.h
 | 
			
		||||
    mic.cpp
 | 
			
		||||
    mic.h
 | 
			
		||||
    native.cpp
 | 
			
		||||
    native.h
 | 
			
		||||
    ndk_motion.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -199,12 +199,12 @@ void Config::ReadValues() {
 | 
			
		|||
 | 
			
		||||
    // Audio
 | 
			
		||||
    ReadSetting("Audio", Settings::values.audio_emulation);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.sink_id);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.enable_audio_stretching);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.audio_device_id);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.volume);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.mic_input_device);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.mic_input_type);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.output_type);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.output_device);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.input_type);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.input_device);
 | 
			
		||||
 | 
			
		||||
    // Data Storage
 | 
			
		||||
    ReadSetting("Data Storage", Settings::values.use_virtual_sd);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -226,28 +226,32 @@ enable_dsp_lle =
 | 
			
		|||
# 0 (default): No, 1: Yes
 | 
			
		||||
enable_dsp_lle_thread =
 | 
			
		||||
 | 
			
		||||
# Which audio output engine to use.
 | 
			
		||||
# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
 | 
			
		||||
output_engine =
 | 
			
		||||
 | 
			
		||||
# Whether or not to enable the audio-stretching post-processing effect.
 | 
			
		||||
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
 | 
			
		||||
# at the cost of increasing audio latency.
 | 
			
		||||
# 0: No, 1 (default): Yes
 | 
			
		||||
enable_audio_stretching =
 | 
			
		||||
 | 
			
		||||
# Which audio device to use.
 | 
			
		||||
# 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 =
 | 
			
		||||
 | 
			
		||||
# Which audio output type to use.
 | 
			
		||||
# 0 (default): Auto-select, 1: No audio output, 2: Cubeb (if available), 3: OpenAL (if available), 4: SDL2 (if available)
 | 
			
		||||
output_type =
 | 
			
		||||
 | 
			
		||||
# Which audio output device to use.
 | 
			
		||||
# auto (default): Auto-select
 | 
			
		||||
output_device =
 | 
			
		||||
 | 
			
		||||
# Which audio input type to use.
 | 
			
		||||
# 0 (default): Auto-select, 1: No audio input, 2: Static noise, 3: Cubeb (if available), 4: OpenAL (if available)
 | 
			
		||||
input_type =
 | 
			
		||||
 | 
			
		||||
# Which audio input device to use.
 | 
			
		||||
# auto (default): Auto-select
 | 
			
		||||
input_device =
 | 
			
		||||
 | 
			
		||||
[Data Storage]
 | 
			
		||||
# Whether to create a virtual SD card.
 | 
			
		||||
# 1 (default): Yes, 0: No
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,38 +0,0 @@
 | 
			
		|||
// Copyright 2020 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <jni.h>
 | 
			
		||||
#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<Frontend::Mic::Interface> 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<AudioCore::CubebInput>(std::move(mic_device_name));
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_WARNING(Frontend, "Mic permissions denied");
 | 
			
		||||
        return std::make_unique<Frontend::Mic::NullMic>();
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    LOG_WARNING(Frontend, "No cubeb support");
 | 
			
		||||
    return std::make_unique<Frontend::Mic::NullMic>();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Mic
 | 
			
		||||
| 
						 | 
				
			
			@ -1,21 +0,0 @@
 | 
			
		|||
// 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<Frontend::Mic::Interface> Create(std::string mic_device_name) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool permission_granted = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Mic
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,6 @@
 | 
			
		|||
#include "core/core.h"
 | 
			
		||||
#include "core/frontend/applets/default_applets.h"
 | 
			
		||||
#include "core/frontend/camera/factory.h"
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "core/hle/service/am/am.h"
 | 
			
		||||
#include "core/hle/service/nfc/nfc.h"
 | 
			
		||||
#include "core/savestate.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +38,6 @@
 | 
			
		|||
#include "jni/game_settings.h"
 | 
			
		||||
#include "jni/id_cache.h"
 | 
			
		||||
#include "jni/input_manager.h"
 | 
			
		||||
#include "jni/mic.h"
 | 
			
		||||
#include "jni/native.h"
 | 
			
		||||
#include "jni/ndk_motion.h"
 | 
			
		||||
#include "video_core/renderer_base.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -134,6 +132,11 @@ static void TryShutdown() {
 | 
			
		|||
    MicroProfileShutdown();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool CheckMicPermission() {
 | 
			
		||||
    return IDCache::GetEnvForThread()->CallStaticBooleanMethod(IDCache::GetNativeLibraryClass(),
 | 
			
		||||
                                                               IDCache::GetRequestMicPermission());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Core::System::ResultStatus RunCitra(const std::string& filepath) {
 | 
			
		||||
    // Citra core only supports a single running instance
 | 
			
		||||
    std::lock_guard<std::mutex> lock(running_mutex);
 | 
			
		||||
| 
						 | 
				
			
			@ -183,8 +186,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
 | 
			
		|||
    system.RegisterMiiSelector(std::make_shared<MiiSelector::AndroidMiiSelector>());
 | 
			
		||||
    system.RegisterSoftwareKeyboard(std::make_shared<SoftwareKeyboard::AndroidKeyboard>());
 | 
			
		||||
 | 
			
		||||
    // Register real Mic factory
 | 
			
		||||
    Frontend::Mic::RegisterRealMicFactory(std::make_unique<Mic::AndroidFactory>());
 | 
			
		||||
    // Register microphone permission check
 | 
			
		||||
    Core::System::GetInstance().RegisterMicPermissionCheck(&CheckMicPermission);
 | 
			
		||||
 | 
			
		||||
    InputManager::Init();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,15 +143,19 @@
 | 
			
		|||
    </integer-array>
 | 
			
		||||
 | 
			
		||||
    <string-array name="audioInputTypeNames">
 | 
			
		||||
        <item>Auto</item>
 | 
			
		||||
        <item>None</item>
 | 
			
		||||
        <item>Real Device</item>
 | 
			
		||||
        <item>Static Noise</item>
 | 
			
		||||
        <item>Real Device (Cubeb)</item>
 | 
			
		||||
        <item>Real Device (OpenAL)</item>
 | 
			
		||||
    </string-array>
 | 
			
		||||
 | 
			
		||||
    <integer-array name="audioInputTypeValues">
 | 
			
		||||
        <item>0</item>
 | 
			
		||||
        <item>1</item>
 | 
			
		||||
        <item>2</item>
 | 
			
		||||
        <item>3</item>
 | 
			
		||||
        <item>4</item>
 | 
			
		||||
    </integer-array>
 | 
			
		||||
 | 
			
		||||
    <string-array name="render3dModes">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,18 +20,25 @@ add_library(audio_core STATIC
 | 
			
		|||
    hle/source.h
 | 
			
		||||
    lle/lle.cpp
 | 
			
		||||
    lle/lle.h
 | 
			
		||||
    input.h
 | 
			
		||||
    input_details.cpp
 | 
			
		||||
    input_details.h
 | 
			
		||||
    interpolate.cpp
 | 
			
		||||
    interpolate.h
 | 
			
		||||
    null_input.h
 | 
			
		||||
    null_sink.h
 | 
			
		||||
    precompiled_headers.h
 | 
			
		||||
    sink.h
 | 
			
		||||
    sink_details.cpp
 | 
			
		||||
    sink_details.h
 | 
			
		||||
    static_input.cpp
 | 
			
		||||
    static_input.h
 | 
			
		||||
    time_stretch.cpp
 | 
			
		||||
    time_stretch.h
 | 
			
		||||
 | 
			
		||||
    $<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
 | 
			
		||||
    $<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h cubeb_input.cpp cubeb_input.h>
 | 
			
		||||
    $<$<BOOL:${ENABLE_OPENAL}>:openal_input.cpp openal_input.h openal_sink.cpp openal_sink.h>
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
create_target_directory_groups(audio_core)
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +99,12 @@ if(ENABLE_CUBEB)
 | 
			
		|||
    target_compile_definitions(audio_core PUBLIC HAVE_CUBEB)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if(ENABLE_OPENAL)
 | 
			
		||||
    target_link_libraries(audio_core PRIVATE OpenAL)
 | 
			
		||||
    target_compile_definitions(audio_core PUBLIC HAVE_OPENAL)
 | 
			
		||||
    add_definitions(-DAL_LIBTYPE_STATIC)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (CITRA_USE_PRECOMPILED_HEADERS)
 | 
			
		||||
    target_precompile_headers(audio_core PRIVATE precompiled_headers.h)
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,11 +6,14 @@
 | 
			
		|||
#include <vector>
 | 
			
		||||
#include <cubeb/cubeb.h>
 | 
			
		||||
#include "audio_core/cubeb_input.h"
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/threadsafe_queue.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
using SampleQueue = Common::SPSCQueue<Frontend::Mic::Samples>;
 | 
			
		||||
using SampleQueue = Common::SPSCQueue<Samples>;
 | 
			
		||||
 | 
			
		||||
struct CubebInput::Impl {
 | 
			
		||||
    cubeb* ctx = nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -48,10 +51,10 @@ CubebInput::~CubebInput() {
 | 
			
		|||
    cubeb_destroy(impl->ctx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CubebInput::StartSampling(const Frontend::Mic::Parameters& params) {
 | 
			
		||||
void CubebInput::StartSampling(const InputParameters& params) {
 | 
			
		||||
    // Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support)
 | 
			
		||||
    // TODO resample the input stream
 | 
			
		||||
    if (params.sign == Frontend::Mic::Signedness::Unsigned) {
 | 
			
		||||
    if (params.sign == Signedness::Unsigned) {
 | 
			
		||||
        LOG_ERROR(Audio,
 | 
			
		||||
                  "Application requested unsupported unsigned pcm format. Falling back to signed");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +65,7 @@ void CubebInput::StartSampling(const Frontend::Mic::Parameters& params) {
 | 
			
		|||
    is_sampling = true;
 | 
			
		||||
 | 
			
		||||
    cubeb_devid input_device = nullptr;
 | 
			
		||||
    if (device_id != Frontend::Mic::default_device_name && !device_id.empty()) {
 | 
			
		||||
    if (device_id != auto_device_name && !device_id.empty()) {
 | 
			
		||||
        cubeb_device_collection collection;
 | 
			
		||||
        if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) {
 | 
			
		||||
            LOG_WARNING(Audio, "Audio input device enumeration not supported");
 | 
			
		||||
| 
						 | 
				
			
			@ -122,9 +125,9 @@ void CubebInput::AdjustSampleRate(u32 sample_rate) {
 | 
			
		|||
    LOG_ERROR(Audio, "AdjustSampleRate unimplemented!");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Frontend::Mic::Samples CubebInput::Read() {
 | 
			
		||||
    Frontend::Mic::Samples samples{};
 | 
			
		||||
    Frontend::Mic::Samples queue;
 | 
			
		||||
Samples CubebInput::Read() {
 | 
			
		||||
    Samples samples{};
 | 
			
		||||
    Samples queue;
 | 
			
		||||
    while (impl->sample_queue->Pop(queue)) {
 | 
			
		||||
        samples.insert(samples.end(), queue.begin(), queue.end());
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -190,10 +193,4 @@ std::vector<std::string> ListCubebInputDevices() {
 | 
			
		|||
    return device_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CubebFactory::~CubebFactory() = default;
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Frontend::Mic::Interface> CubebFactory::Create(std::string mic_device_name) {
 | 
			
		||||
    return std::make_unique<CubebInput>(std::move(mic_device_name));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,23 +5,24 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class CubebInput final : public Frontend::Mic::Interface {
 | 
			
		||||
class CubebInput final : public Input {
 | 
			
		||||
public:
 | 
			
		||||
    explicit CubebInput(std::string device_id);
 | 
			
		||||
    ~CubebInput() override;
 | 
			
		||||
 | 
			
		||||
    void StartSampling(const Frontend::Mic::Parameters& params) override;
 | 
			
		||||
    void StartSampling(const InputParameters& params) override;
 | 
			
		||||
 | 
			
		||||
    void StopSampling() override;
 | 
			
		||||
 | 
			
		||||
    void AdjustSampleRate(u32 sample_rate) override;
 | 
			
		||||
 | 
			
		||||
    Frontend::Mic::Samples Read() override;
 | 
			
		||||
    Samples Read() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct Impl;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,11 +32,4 @@ private:
 | 
			
		|||
 | 
			
		||||
std::vector<std::string> ListCubebInputDevices();
 | 
			
		||||
 | 
			
		||||
class CubebFactory final : public Frontend::Mic::RealMicFactory {
 | 
			
		||||
public:
 | 
			
		||||
    ~CubebFactory() override;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Frontend::Mic::Interface> Create(std::string mic_device_name) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,7 @@
 | 
			
		|||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,9 +16,8 @@ namespace AudioCore {
 | 
			
		|||
DspInterface::DspInterface() = default;
 | 
			
		||||
DspInterface::~DspInterface() = default;
 | 
			
		||||
 | 
			
		||||
void DspInterface::SetSink(std::string_view sink_id, std::string_view audio_device) {
 | 
			
		||||
    sink = CreateSinkFromID(Settings::values.sink_id.GetValue(),
 | 
			
		||||
                            Settings::values.audio_device_id.GetValue());
 | 
			
		||||
void DspInterface::SetSink(AudioCore::SinkType sink_type, std::string_view audio_device) {
 | 
			
		||||
    sink = CreateSinkFromID(sink_type, audio_device);
 | 
			
		||||
    sink->SetCallback(
 | 
			
		||||
        [this](s16* buffer, std::size_t num_frames) { OutputCallback(buffer, num_frames); });
 | 
			
		||||
    time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,6 +20,7 @@ class DSP_DSP;
 | 
			
		|||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class Sink;
 | 
			
		||||
enum class SinkType : u32;
 | 
			
		||||
 | 
			
		||||
class DspInterface {
 | 
			
		||||
public:
 | 
			
		||||
| 
						 | 
				
			
			@ -93,8 +94,8 @@ public:
 | 
			
		|||
    /// Unloads the DSP program
 | 
			
		||||
    virtual void UnloadComponent() = 0;
 | 
			
		||||
 | 
			
		||||
    /// Select the sink to use based on sink id.
 | 
			
		||||
    void SetSink(std::string_view sink_id, std::string_view audio_device);
 | 
			
		||||
    /// Select the sink to use based on sink type.
 | 
			
		||||
    void SetSink(SinkType sink_type, std::string_view audio_device);
 | 
			
		||||
    /// Get the current sink
 | 
			
		||||
    Sink& GetSink();
 | 
			
		||||
    /// Enable/Disable audio stretching.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										85
									
								
								src/audio_core/input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/audio_core/input.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
enum class Signedness : u8 {
 | 
			
		||||
    Signed,
 | 
			
		||||
    Unsigned,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using Samples = std::vector<u8>;
 | 
			
		||||
 | 
			
		||||
struct InputParameters {
 | 
			
		||||
    Signedness sign;
 | 
			
		||||
    u8 sample_size;
 | 
			
		||||
    bool buffer_loop;
 | 
			
		||||
    u32 sample_rate;
 | 
			
		||||
    u32 buffer_offset;
 | 
			
		||||
    u32 buffer_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Input {
 | 
			
		||||
public:
 | 
			
		||||
    Input() = default;
 | 
			
		||||
 | 
			
		||||
    virtual ~Input() = default;
 | 
			
		||||
 | 
			
		||||
    /// Starts the microphone. Called by Core
 | 
			
		||||
    virtual void StartSampling(const InputParameters& params) = 0;
 | 
			
		||||
 | 
			
		||||
    /// Stops the microphone. Called by Core
 | 
			
		||||
    virtual void StopSampling() = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called from the actual event timing at a constant period under a given sample rate.
 | 
			
		||||
     * When sampling is enabled this function is expected to return a buffer of 16 samples in ideal
 | 
			
		||||
     * conditions, but can be lax if the data is coming in from another source like a real mic.
 | 
			
		||||
     */
 | 
			
		||||
    virtual Samples Read() = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adjusts the Parameters. Implementations should update the parameters field in addition to
 | 
			
		||||
     * changing the mic to sample according to the new parameters. Called by Core
 | 
			
		||||
     */
 | 
			
		||||
    virtual void AdjustSampleRate(u32 sample_rate) = 0;
 | 
			
		||||
 | 
			
		||||
    /// Value from 0 - 100 to adjust the mic gain setting. Called by Core
 | 
			
		||||
    virtual void SetGain(u8 mic_gain) {
 | 
			
		||||
        gain = mic_gain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u8 GetGain() const {
 | 
			
		||||
        return gain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetPower(bool power) {
 | 
			
		||||
        powered = power;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GetPower() const {
 | 
			
		||||
        return powered;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsSampling() const {
 | 
			
		||||
        return is_sampling;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const InputParameters& GetParameters() const {
 | 
			
		||||
        return parameters;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    InputParameters parameters;
 | 
			
		||||
    u8 gain = 0;
 | 
			
		||||
    bool is_sampling = false;
 | 
			
		||||
    bool powered = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										108
									
								
								src/audio_core/input_details.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/audio_core/input_details.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,108 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "audio_core/input_details.h"
 | 
			
		||||
#include "audio_core/null_input.h"
 | 
			
		||||
#include "audio_core/static_input.h"
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
#include "audio_core/cubeb_input.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_OPENAL
 | 
			
		||||
#include "audio_core/openal_input.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
namespace {
 | 
			
		||||
struct InputDetails {
 | 
			
		||||
    using FactoryFn = std::unique_ptr<Input> (*)(std::string_view);
 | 
			
		||||
    using ListDevicesFn = std::vector<std::string> (*)();
 | 
			
		||||
 | 
			
		||||
    /// Type of this input.
 | 
			
		||||
    InputType type;
 | 
			
		||||
    /// Name for this input.
 | 
			
		||||
    std::string_view name;
 | 
			
		||||
    /// A method to call to construct an instance of this type of input.
 | 
			
		||||
    FactoryFn factory;
 | 
			
		||||
    /// A method to call to list available devices.
 | 
			
		||||
    ListDevicesFn list_devices;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// input_details is ordered in terms of desirability, with the best choice at the top.
 | 
			
		||||
constexpr std::array input_details = {
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
    InputDetails{InputType::Cubeb, "Real Device (Cubeb)",
 | 
			
		||||
                 [](std::string_view device_id) -> std::unique_ptr<Input> {
 | 
			
		||||
                     if (!Core::System::GetInstance().HasMicPermission()) {
 | 
			
		||||
                         LOG_WARNING(Audio,
 | 
			
		||||
                                     "Microphone permission denied, falling back to null input.");
 | 
			
		||||
                         return std::make_unique<NullInput>();
 | 
			
		||||
                     }
 | 
			
		||||
                     return std::make_unique<CubebInput>(std::string(device_id));
 | 
			
		||||
                 },
 | 
			
		||||
                 &ListCubebInputDevices},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_OPENAL
 | 
			
		||||
    InputDetails{InputType::OpenAL, "Real Device (OpenAL)",
 | 
			
		||||
                 [](std::string_view device_id) -> std::unique_ptr<Input> {
 | 
			
		||||
                     if (!Core::System::GetInstance().HasMicPermission()) {
 | 
			
		||||
                         LOG_WARNING(Audio,
 | 
			
		||||
                                     "Microphone permission denied, falling back to null input.");
 | 
			
		||||
                         return std::make_unique<NullInput>();
 | 
			
		||||
                     }
 | 
			
		||||
                     return std::make_unique<OpenALInput>(std::string(device_id));
 | 
			
		||||
                 },
 | 
			
		||||
                 &ListOpenALInputDevices},
 | 
			
		||||
#endif
 | 
			
		||||
    InputDetails{InputType::Static, "Static Noise",
 | 
			
		||||
                 [](std::string_view device_id) -> std::unique_ptr<Input> {
 | 
			
		||||
                     return std::make_unique<StaticInput>();
 | 
			
		||||
                 },
 | 
			
		||||
                 [] { return std::vector<std::string>{"Static Noise"}; }},
 | 
			
		||||
    InputDetails{InputType::Null, "None",
 | 
			
		||||
                 [](std::string_view device_id) -> std::unique_ptr<Input> {
 | 
			
		||||
                     return std::make_unique<NullInput>();
 | 
			
		||||
                 },
 | 
			
		||||
                 [] { return std::vector<std::string>{"None"}; }},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const InputDetails& GetInputDetails(InputType input_type) {
 | 
			
		||||
    auto iter = std::find_if(
 | 
			
		||||
        input_details.begin(), input_details.end(),
 | 
			
		||||
        [input_type](const auto& input_detail) { return input_detail.type == input_type; });
 | 
			
		||||
 | 
			
		||||
    if (input_type == InputType::Auto || iter == input_details.end()) {
 | 
			
		||||
        if (input_type != InputType::Auto) {
 | 
			
		||||
            LOG_ERROR(Audio, "AudioCore::GetInputDetails given invalid input_type {}", input_type);
 | 
			
		||||
        }
 | 
			
		||||
        // Auto-select.
 | 
			
		||||
        // input_details is ordered in terms of desirability, with the best choice at the front.
 | 
			
		||||
        iter = input_details.begin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *iter;
 | 
			
		||||
}
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
std::string_view GetInputName(InputType input_type) {
 | 
			
		||||
    if (input_type == InputType::Auto) {
 | 
			
		||||
        return "Auto";
 | 
			
		||||
    }
 | 
			
		||||
    return GetInputDetails(input_type).name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> GetDeviceListForInput(InputType input_type) {
 | 
			
		||||
    return GetInputDetails(input_type).list_devices();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Input> CreateInputFromID(InputType input_type, std::string_view device_id) {
 | 
			
		||||
    return GetInputDetails(input_type).factory(device_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										36
									
								
								src/audio_core/input_details.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/audio_core/input_details.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class Input;
 | 
			
		||||
 | 
			
		||||
enum class InputType : u32 {
 | 
			
		||||
    Auto = 0,
 | 
			
		||||
    Null = 1,
 | 
			
		||||
    Static = 2,
 | 
			
		||||
    Cubeb = 3,
 | 
			
		||||
    OpenAL = 4,
 | 
			
		||||
 | 
			
		||||
    NumInputTypes,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Gets the name of a input type.
 | 
			
		||||
std::string_view GetInputName(InputType input_type);
 | 
			
		||||
 | 
			
		||||
/// Gets the list of devices for a particular input identified by the given ID.
 | 
			
		||||
std::vector<std::string> GetDeviceListForInput(InputType input_type);
 | 
			
		||||
 | 
			
		||||
/// Creates an audio input identified by the given device ID.
 | 
			
		||||
std::unique_ptr<Input> CreateInputFromID(InputType input_type, std::string_view device_id);
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										35
									
								
								src/audio_core/null_input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/audio_core/null_input.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "common/threadsafe_queue.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class NullInput final : public Input {
 | 
			
		||||
public:
 | 
			
		||||
    void StartSampling(const InputParameters& params) override {
 | 
			
		||||
        parameters = params;
 | 
			
		||||
        is_sampling = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void StopSampling() override {
 | 
			
		||||
        is_sampling = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void AdjustSampleRate(u32 sample_rate) override {
 | 
			
		||||
        parameters.sample_rate = sample_rate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Samples Read() override {
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										133
									
								
								src/audio_core/openal_input.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/audio_core/openal_input.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,133 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <AL/al.h>
 | 
			
		||||
#include <AL/alc.h>
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
#include "audio_core/openal_input.h"
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
struct OpenALInput::Impl {
 | 
			
		||||
    ALCdevice* device = nullptr;
 | 
			
		||||
    u8 sample_size_in_bytes = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OpenALInput::OpenALInput(std::string device_id)
 | 
			
		||||
    : impl(std::make_unique<Impl>()), device_id(std::move(device_id)) {}
 | 
			
		||||
 | 
			
		||||
OpenALInput::~OpenALInput() {
 | 
			
		||||
    StopSampling();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenALInput::StartSampling(const InputParameters& params) {
 | 
			
		||||
    if (is_sampling) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // OpenAL supports unsigned 8-bit and signed 16-bit PCM.
 | 
			
		||||
    // TODO: Re-sample the stream.
 | 
			
		||||
    if ((params.sample_size == 8 && params.sign == Signedness::Signed) ||
 | 
			
		||||
        (params.sample_size == 16 && params.sign == Signedness::Unsigned)) {
 | 
			
		||||
        LOG_WARNING(Audio, "Application requested unsupported unsigned PCM format. Falling back to "
 | 
			
		||||
                           "supported format.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    parameters = params;
 | 
			
		||||
    impl->sample_size_in_bytes = params.sample_size / 8;
 | 
			
		||||
 | 
			
		||||
    auto format = params.sample_size == 16 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8;
 | 
			
		||||
    impl->device = alcCaptureOpenDevice(
 | 
			
		||||
        device_id != auto_device_name && !device_id.empty() ? device_id.c_str() : nullptr,
 | 
			
		||||
        params.sample_rate, format, static_cast<ALsizei>(params.buffer_size));
 | 
			
		||||
    if (!impl->device) {
 | 
			
		||||
        LOG_CRITICAL(Audio, "alcCaptureOpenDevice failed.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alcCaptureStart(impl->device);
 | 
			
		||||
    auto error = alcGetError(impl->device);
 | 
			
		||||
    if (error != ALC_NO_ERROR) {
 | 
			
		||||
        LOG_CRITICAL(Audio, "alcCaptureStart failed: {}", error);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    is_sampling = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenALInput::StopSampling() {
 | 
			
		||||
    if (impl->device) {
 | 
			
		||||
        alcCaptureStop(impl->device);
 | 
			
		||||
        alcCaptureCloseDevice(impl->device);
 | 
			
		||||
        impl->device = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    is_sampling = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenALInput::AdjustSampleRate(u32 sample_rate) {
 | 
			
		||||
    if (!is_sampling) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto new_params = parameters;
 | 
			
		||||
    new_params.sample_rate = sample_rate;
 | 
			
		||||
    StopSampling();
 | 
			
		||||
    StartSampling(new_params);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Samples OpenALInput::Read() {
 | 
			
		||||
    if (!is_sampling) {
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ALCint samples_captured = 0;
 | 
			
		||||
    alcGetIntegerv(impl->device, ALC_CAPTURE_SAMPLES, 1, &samples_captured);
 | 
			
		||||
    auto error = alcGetError(impl->device);
 | 
			
		||||
    if (error != ALC_NO_ERROR) {
 | 
			
		||||
        LOG_WARNING(Audio, "alcGetIntegerv(ALC_CAPTURE_SAMPLES) failed: {}", error);
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto num_samples = std::min(samples_captured, static_cast<ALsizei>(parameters.buffer_size /
 | 
			
		||||
                                                                       impl->sample_size_in_bytes));
 | 
			
		||||
    Samples samples(num_samples * impl->sample_size_in_bytes);
 | 
			
		||||
 | 
			
		||||
    alcCaptureSamples(impl->device, samples.data(), num_samples);
 | 
			
		||||
    error = alcGetError(impl->device);
 | 
			
		||||
    if (error != ALC_NO_ERROR) {
 | 
			
		||||
        LOG_WARNING(Audio, "alcCaptureSamples failed: {}", error);
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return samples;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ListOpenALInputDevices() {
 | 
			
		||||
    const char* devices_str;
 | 
			
		||||
    if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") != AL_FALSE) {
 | 
			
		||||
        devices_str = alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER);
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_WARNING(
 | 
			
		||||
            Audio,
 | 
			
		||||
            "Missing OpenAL device enumeration extensions, cannot list audio capture devices.");
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!devices_str || *devices_str == '\0') {
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> device_list;
 | 
			
		||||
    while (*devices_str != '\0') {
 | 
			
		||||
        device_list.emplace_back(devices_str);
 | 
			
		||||
        devices_str += strlen(devices_str) + 1;
 | 
			
		||||
    }
 | 
			
		||||
    return device_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										35
									
								
								src/audio_core/openal_input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/audio_core/openal_input.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class OpenALInput final : public Input {
 | 
			
		||||
public:
 | 
			
		||||
    explicit OpenALInput(std::string device_id);
 | 
			
		||||
    ~OpenALInput() override;
 | 
			
		||||
 | 
			
		||||
    void StartSampling(const InputParameters& params) override;
 | 
			
		||||
 | 
			
		||||
    void StopSampling() override;
 | 
			
		||||
 | 
			
		||||
    void AdjustSampleRate(u32 sample_rate) override;
 | 
			
		||||
 | 
			
		||||
    Samples Read() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct Impl;
 | 
			
		||||
    std::unique_ptr<Impl> impl;
 | 
			
		||||
    std::string device_id;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ListOpenALInputDevices();
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										176
									
								
								src/audio_core/openal_sink.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								src/audio_core/openal_sink.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,176 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <AL/al.h>
 | 
			
		||||
#include <AL/alc.h>
 | 
			
		||||
#include <AL/alext.h>
 | 
			
		||||
#include "audio_core/audio_types.h"
 | 
			
		||||
#include "audio_core/openal_sink.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
struct OpenALSink::Impl {
 | 
			
		||||
    unsigned int sample_rate = 0;
 | 
			
		||||
 | 
			
		||||
    ALCdevice* device = nullptr;
 | 
			
		||||
    ALCcontext* context = nullptr;
 | 
			
		||||
    ALuint buffer = 0;
 | 
			
		||||
    ALuint source = 0;
 | 
			
		||||
 | 
			
		||||
    std::function<void(s16*, std::size_t)> cb;
 | 
			
		||||
 | 
			
		||||
    static ALsizei Callback(void* impl_, void* buffer, ALsizei buffer_size_in_bytes);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
OpenALSink::OpenALSink(std::string device_name) : impl(std::make_unique<Impl>()) {
 | 
			
		||||
    impl->device = alcOpenDevice(
 | 
			
		||||
        device_name != auto_device_name && !device_name.empty() ? device_name.c_str() : nullptr);
 | 
			
		||||
    if (!impl->device) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alcOpenDevice failed.");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl->context = alcCreateContext(impl->device, nullptr);
 | 
			
		||||
    if (impl->context == nullptr) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alcCreateContext failed: {}", alcGetError(impl->device));
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (alcMakeContextCurrent(impl->context) == ALC_FALSE) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alcMakeContextCurrent failed: {}", alcGetError(impl->device));
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (alIsExtensionPresent("AL_SOFT_callback_buffer") == AL_FALSE) {
 | 
			
		||||
        if (alGetError() != AL_NO_ERROR) {
 | 
			
		||||
            LOG_CRITICAL(Audio_Sink, "alIsExtensionPresent failed: {}", alGetError());
 | 
			
		||||
        } else {
 | 
			
		||||
            LOG_CRITICAL(Audio_Sink, "Missing required extension AL_SOFT_callback_buffer.");
 | 
			
		||||
        }
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alGenBuffers(1, &impl->buffer);
 | 
			
		||||
    if (alGetError() != AL_NO_ERROR) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alGetError failed: {}", alGetError());
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alGenSources(1, &impl->source);
 | 
			
		||||
    if (alGetError() != AL_NO_ERROR) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alGenSources failed: {}", alGetError());
 | 
			
		||||
        alDeleteBuffers(1, &impl->buffer);
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto alBufferCallbackSOFT =
 | 
			
		||||
        reinterpret_cast<LPALBUFFERCALLBACKSOFT>(alGetProcAddress("alBufferCallbackSOFT"));
 | 
			
		||||
    alBufferCallbackSOFT(impl->buffer, AL_FORMAT_STEREO16, native_sample_rate, &Impl::Callback,
 | 
			
		||||
                         impl.get());
 | 
			
		||||
    if (alGetError() != AL_NO_ERROR) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alBufferCallbackSOFT failed: {}", alGetError());
 | 
			
		||||
        alDeleteSources(1, &impl->source);
 | 
			
		||||
        alDeleteBuffers(1, &impl->buffer);
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alSourcei(impl->source, AL_BUFFER, static_cast<ALint>(impl->buffer));
 | 
			
		||||
    if (alGetError() != AL_NO_ERROR) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alSourcei failed: {}", alGetError());
 | 
			
		||||
        alDeleteSources(1, &impl->source);
 | 
			
		||||
        alDeleteBuffers(1, &impl->buffer);
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    alSourcePlay(impl->source);
 | 
			
		||||
    if (alGetError() != AL_NO_ERROR) {
 | 
			
		||||
        LOG_CRITICAL(Audio_Sink, "alSourcePlay failed: {}", alGetError());
 | 
			
		||||
        alDeleteSources(1, &impl->source);
 | 
			
		||||
        alDeleteBuffers(1, &impl->buffer);
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
OpenALSink::~OpenALSink() {
 | 
			
		||||
    if (impl->source) {
 | 
			
		||||
        alSourceStop(impl->source);
 | 
			
		||||
        alDeleteSources(1, &impl->source);
 | 
			
		||||
        impl->source = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (impl->buffer) {
 | 
			
		||||
        alDeleteBuffers(1, &impl->buffer);
 | 
			
		||||
        impl->buffer = 0;
 | 
			
		||||
    }
 | 
			
		||||
    if (impl->context) {
 | 
			
		||||
        alcDestroyContext(impl->context);
 | 
			
		||||
        impl->context = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    if (impl->device) {
 | 
			
		||||
        alcCloseDevice(impl->device);
 | 
			
		||||
        impl->device = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsigned int OpenALSink::GetNativeSampleRate() const {
 | 
			
		||||
    return native_sample_rate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void OpenALSink::SetCallback(std::function<void(s16*, std::size_t)> cb) {
 | 
			
		||||
    impl->cb = cb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ALsizei OpenALSink::Impl::Callback(void* impl_, void* buffer, ALsizei buffer_size_in_bytes) {
 | 
			
		||||
    auto impl = reinterpret_cast<Impl*>(impl_);
 | 
			
		||||
    if (!impl || !impl->cb) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const size_t num_frames = buffer_size_in_bytes / (2 * sizeof(s16));
 | 
			
		||||
    impl->cb(reinterpret_cast<s16*>(buffer), num_frames);
 | 
			
		||||
 | 
			
		||||
    return buffer_size_in_bytes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ListOpenALSinkDevices() {
 | 
			
		||||
    const char* devices_str;
 | 
			
		||||
    if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
 | 
			
		||||
        devices_str = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER);
 | 
			
		||||
    } else if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT") != AL_FALSE) {
 | 
			
		||||
        devices_str = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_WARNING(Audio_Sink,
 | 
			
		||||
                    "Missing OpenAL device enumeration extensions, cannot list audio devices.");
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!devices_str || *devices_str == '\0') {
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<std::string> device_list;
 | 
			
		||||
    while (*devices_str != '\0') {
 | 
			
		||||
        device_list.emplace_back(devices_str);
 | 
			
		||||
        devices_str += strlen(devices_str) + 1;
 | 
			
		||||
    }
 | 
			
		||||
    return device_list;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										30
									
								
								src/audio_core/openal_sink.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/audio_core/openal_sink.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
// Copyright 2023 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class OpenALSink final : public Sink {
 | 
			
		||||
public:
 | 
			
		||||
    explicit OpenALSink(std::string device_id);
 | 
			
		||||
    ~OpenALSink() override;
 | 
			
		||||
 | 
			
		||||
    unsigned int GetNativeSampleRate() const override;
 | 
			
		||||
 | 
			
		||||
    void SetCallback(std::function<void(s16*, std::size_t)> cb) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    struct Impl;
 | 
			
		||||
    std::unique_ptr<Impl> impl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> ListOpenALSinkDevices();
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +14,9 @@
 | 
			
		|||
#ifdef HAVE_CUBEB
 | 
			
		||||
#include "audio_core/cubeb_sink.h"
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_OPENAL
 | 
			
		||||
#include "audio_core/openal_sink.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
| 
						 | 
				
			
			@ -22,8 +25,10 @@ struct SinkDetails {
 | 
			
		|||
    using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view);
 | 
			
		||||
    using ListDevicesFn = std::vector<std::string> (*)();
 | 
			
		||||
 | 
			
		||||
    /// Type of this sink.
 | 
			
		||||
    SinkType type;
 | 
			
		||||
    /// Name for this sink.
 | 
			
		||||
    const char* id;
 | 
			
		||||
    std::string_view name;
 | 
			
		||||
    /// A method to call to construct an instance of this type of sink.
 | 
			
		||||
    FactoryFn factory;
 | 
			
		||||
    /// A method to call to list available devices.
 | 
			
		||||
| 
						 | 
				
			
			@ -31,61 +36,66 @@ struct SinkDetails {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
// sink_details is ordered in terms of desirability, with the best choice at the top.
 | 
			
		||||
constexpr SinkDetails sink_details[] = {
 | 
			
		||||
constexpr std::array sink_details = {
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
    SinkDetails{"cubeb",
 | 
			
		||||
    SinkDetails{SinkType::Cubeb, "Cubeb",
 | 
			
		||||
                [](std::string_view device_id) -> std::unique_ptr<Sink> {
 | 
			
		||||
                    return std::make_unique<CubebSink>(device_id);
 | 
			
		||||
                },
 | 
			
		||||
                &ListCubebSinkDevices},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_OPENAL
 | 
			
		||||
    SinkDetails{SinkType::OpenAL, "OpenAL",
 | 
			
		||||
                [](std::string_view device_id) -> std::unique_ptr<Sink> {
 | 
			
		||||
                    return std::make_unique<OpenALSink>(std::string(device_id));
 | 
			
		||||
                },
 | 
			
		||||
                &ListOpenALSinkDevices},
 | 
			
		||||
#endif
 | 
			
		||||
#ifdef HAVE_SDL2
 | 
			
		||||
    SinkDetails{"sdl2",
 | 
			
		||||
    SinkDetails{SinkType::SDL2, "SDL2",
 | 
			
		||||
                [](std::string_view device_id) -> std::unique_ptr<Sink> {
 | 
			
		||||
                    return std::make_unique<SDL2Sink>(std::string(device_id));
 | 
			
		||||
                },
 | 
			
		||||
                &ListSDL2SinkDevices},
 | 
			
		||||
#endif
 | 
			
		||||
    SinkDetails{"null",
 | 
			
		||||
    SinkDetails{SinkType::Null, "None",
 | 
			
		||||
                [](std::string_view device_id) -> std::unique_ptr<Sink> {
 | 
			
		||||
                    return std::make_unique<NullSink>(device_id);
 | 
			
		||||
                },
 | 
			
		||||
                [] { return std::vector<std::string>{"null"}; }},
 | 
			
		||||
                [] { return std::vector<std::string>{"None"}; }},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const SinkDetails& GetSinkDetails(std::string_view sink_id) {
 | 
			
		||||
    auto iter =
 | 
			
		||||
        std::find_if(std::begin(sink_details), std::end(sink_details),
 | 
			
		||||
                     [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
 | 
			
		||||
const SinkDetails& GetSinkDetails(SinkType sink_type) {
 | 
			
		||||
    auto iter = std::find_if(
 | 
			
		||||
        sink_details.begin(), sink_details.end(),
 | 
			
		||||
        [sink_type](const auto& sink_detail) { return sink_detail.type == sink_type; });
 | 
			
		||||
 | 
			
		||||
    if (sink_id == "auto" || iter == std::end(sink_details)) {
 | 
			
		||||
        if (sink_id != "auto") {
 | 
			
		||||
            LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id);
 | 
			
		||||
    if (sink_type == SinkType::Auto || iter == sink_details.end()) {
 | 
			
		||||
        if (sink_type != SinkType::Auto) {
 | 
			
		||||
            LOG_ERROR(Audio, "AudioCore::GetSinkDetails given invalid sink_type {}", sink_type);
 | 
			
		||||
        }
 | 
			
		||||
        // Auto-select.
 | 
			
		||||
        // sink_details is ordered in terms of desirability, with the best choice at the front.
 | 
			
		||||
        iter = std::begin(sink_details);
 | 
			
		||||
        iter = sink_details.begin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return *iter;
 | 
			
		||||
}
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
std::vector<const char*> GetSinkIDs() {
 | 
			
		||||
    std::vector<const char*> sink_ids(std::size(sink_details));
 | 
			
		||||
 | 
			
		||||
    std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids),
 | 
			
		||||
                   [](const auto& sink) { return sink.id; });
 | 
			
		||||
 | 
			
		||||
    return sink_ids;
 | 
			
		||||
std::string_view GetSinkName(SinkType sink_type) {
 | 
			
		||||
    if (sink_type == SinkType::Auto) {
 | 
			
		||||
        return "Auto";
 | 
			
		||||
    }
 | 
			
		||||
    return GetSinkDetails(sink_type).name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<std::string> GetDeviceListForSink(std::string_view sink_id) {
 | 
			
		||||
    return GetSinkDetails(sink_id).list_devices();
 | 
			
		||||
std::vector<std::string> GetDeviceListForSink(SinkType sink_type) {
 | 
			
		||||
    return GetSinkDetails(sink_type).list_devices();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) {
 | 
			
		||||
    return GetSinkDetails(sink_id).factory(device_id);
 | 
			
		||||
std::unique_ptr<Sink> CreateSinkFromID(SinkType sink_type, std::string_view device_id) {
 | 
			
		||||
    return GetSinkDetails(sink_type).factory(device_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,21 +4,33 @@
 | 
			
		|||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class Sink;
 | 
			
		||||
 | 
			
		||||
/// Retrieves the IDs for all available audio sinks.
 | 
			
		||||
std::vector<const char*> GetSinkIDs();
 | 
			
		||||
enum class SinkType : u32 {
 | 
			
		||||
    Auto = 0,
 | 
			
		||||
    Null = 1,
 | 
			
		||||
    Cubeb = 2,
 | 
			
		||||
    OpenAL = 3,
 | 
			
		||||
    SDL2 = 4,
 | 
			
		||||
 | 
			
		||||
    NumSinkTypes,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Gets the name of a sink type.
 | 
			
		||||
std::string_view GetSinkName(SinkType sink_type);
 | 
			
		||||
 | 
			
		||||
/// Gets the list of devices for a particular sink identified by the given ID.
 | 
			
		||||
std::vector<std::string> GetDeviceListForSink(std::string_view sink_id);
 | 
			
		||||
std::vector<std::string> GetDeviceListForSink(SinkType sink_type);
 | 
			
		||||
 | 
			
		||||
/// Creates an audio sink identified by the given device ID.
 | 
			
		||||
std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id);
 | 
			
		||||
std::unique_ptr<Sink> CreateSinkFromID(SinkType sink_type, std::string_view device_id);
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										42
									
								
								src/audio_core/static_input.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/audio_core/static_input.cpp
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
#include "audio_core/static_input.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
constexpr std::array<u8, 16> NOISE_SAMPLE_8_BIT = {0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
			
		||||
                                                   0xFF, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF};
 | 
			
		||||
 | 
			
		||||
constexpr std::array<u8, 32> NOISE_SAMPLE_16_BIT = {
 | 
			
		||||
    0x64, 0x61, 0x74, 0x61, 0x56, 0xD7, 0x00, 0x00, 0x48, 0xF7, 0x86, 0x05, 0x77, 0x1A, 0xF4, 0x1F,
 | 
			
		||||
    0x28, 0x0F, 0x6B, 0xEB, 0x1C, 0xC0, 0xCB, 0x9D, 0x46, 0x90, 0xDF, 0x98, 0xEA, 0xAE, 0xB5, 0xC4};
 | 
			
		||||
 | 
			
		||||
StaticInput::StaticInput()
 | 
			
		||||
    : CACHE_8_BIT{NOISE_SAMPLE_8_BIT.begin(), NOISE_SAMPLE_8_BIT.end()},
 | 
			
		||||
      CACHE_16_BIT{NOISE_SAMPLE_16_BIT.begin(), NOISE_SAMPLE_16_BIT.end()} {}
 | 
			
		||||
 | 
			
		||||
StaticInput::~StaticInput() = default;
 | 
			
		||||
 | 
			
		||||
void StaticInput::StartSampling(const InputParameters& params) {
 | 
			
		||||
    sample_rate = params.sample_rate;
 | 
			
		||||
    sample_size = params.sample_size;
 | 
			
		||||
 | 
			
		||||
    parameters = params;
 | 
			
		||||
    is_sampling = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StaticInput::StopSampling() {
 | 
			
		||||
    is_sampling = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StaticInput::AdjustSampleRate(u32 sample_rate) {}
 | 
			
		||||
 | 
			
		||||
Samples StaticInput::Read() {
 | 
			
		||||
    return (sample_size == 8) ? CACHE_8_BIT : CACHE_16_BIT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
							
								
								
									
										33
									
								
								src/audio_core/static_input.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/audio_core/static_input.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "common/threadsafe_queue.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
 | 
			
		||||
class StaticInput final : public Input {
 | 
			
		||||
public:
 | 
			
		||||
    StaticInput();
 | 
			
		||||
    ~StaticInput() override;
 | 
			
		||||
 | 
			
		||||
    void StartSampling(const InputParameters& params) override;
 | 
			
		||||
    void StopSampling() override;
 | 
			
		||||
    void AdjustSampleRate(u32 sample_rate) override;
 | 
			
		||||
 | 
			
		||||
    Samples Read() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    u16 sample_rate = 0;
 | 
			
		||||
    u8 sample_size = 0;
 | 
			
		||||
    std::vector<u8> CACHE_8_BIT;
 | 
			
		||||
    std::vector<u8> CACHE_16_BIT;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +15,6 @@
 | 
			
		|||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/param_package.h"
 | 
			
		||||
#include "common/settings.h"
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "core/hle/service/service.h"
 | 
			
		||||
#include "input_common/main.h"
 | 
			
		||||
#include "input_common/udp/client.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -179,12 +178,12 @@ void Config::ReadValues() {
 | 
			
		|||
 | 
			
		||||
    // Audio
 | 
			
		||||
    ReadSetting("Audio", Settings::values.audio_emulation);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.sink_id);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.enable_audio_stretching);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.audio_device_id);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.volume);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.mic_input_device);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.mic_input_type);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.output_type);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.output_device);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.input_type);
 | 
			
		||||
    ReadSetting("Audio", Settings::values.input_device);
 | 
			
		||||
 | 
			
		||||
    // Data Storage
 | 
			
		||||
    ReadSetting("Data Storage", Settings::values.use_virtual_sd);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -245,25 +245,32 @@ enable_dsp_lle =
 | 
			
		|||
# 0 (default): No, 1: Yes
 | 
			
		||||
enable_dsp_lle_thread =
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Which audio output engine to use.
 | 
			
		||||
# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
 | 
			
		||||
output_engine =
 | 
			
		||||
 | 
			
		||||
# Whether or not to enable the audio-stretching post-processing effect.
 | 
			
		||||
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
 | 
			
		||||
# at the cost of increasing audio latency.
 | 
			
		||||
# 0: No, 1 (default): Yes
 | 
			
		||||
enable_audio_stretching =
 | 
			
		||||
 | 
			
		||||
# Which audio device to use.
 | 
			
		||||
# auto (default): Auto-select
 | 
			
		||||
output_device =
 | 
			
		||||
 | 
			
		||||
# Output volume.
 | 
			
		||||
# 1.0 (default): 100%, 0.0; mute
 | 
			
		||||
volume =
 | 
			
		||||
 | 
			
		||||
# Which audio output type to use.
 | 
			
		||||
# 0 (default): Auto-select, 1: No audio output, 2: Cubeb (if available), 3: OpenAL (if available), 4: SDL2 (if available)
 | 
			
		||||
output_type =
 | 
			
		||||
 | 
			
		||||
# Which audio output device to use.
 | 
			
		||||
# auto (default): Auto-select
 | 
			
		||||
output_device =
 | 
			
		||||
 | 
			
		||||
# Which audio input type to use.
 | 
			
		||||
# 0 (default): Auto-select, 1: No audio input, 2: Static noise, 3: Cubeb (if available), 4: OpenAL (if available)
 | 
			
		||||
input_type =
 | 
			
		||||
 | 
			
		||||
# Which audio input device to use.
 | 
			
		||||
# auto (default): Auto-select
 | 
			
		||||
input_device =
 | 
			
		||||
 | 
			
		||||
[Data Storage]
 | 
			
		||||
# Whether to create a virtual SD card.
 | 
			
		||||
# 1 (default): Yes, 0: No
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,6 @@
 | 
			
		|||
#include "citra_qt/configuration/config.h"
 | 
			
		||||
#include "common/file_util.h"
 | 
			
		||||
#include "common/settings.h"
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "core/hle/service/service.h"
 | 
			
		||||
#include "input_common/main.h"
 | 
			
		||||
#include "input_common/udp/client.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -276,10 +275,10 @@ void Config::ReadAudioValues() {
 | 
			
		|||
    ReadGlobalSetting(Settings::values.volume);
 | 
			
		||||
 | 
			
		||||
    if (global) {
 | 
			
		||||
        ReadBasicSetting(Settings::values.sink_id);
 | 
			
		||||
        ReadBasicSetting(Settings::values.audio_device_id);
 | 
			
		||||
        ReadBasicSetting(Settings::values.mic_input_device);
 | 
			
		||||
        ReadBasicSetting(Settings::values.mic_input_type);
 | 
			
		||||
        ReadBasicSetting(Settings::values.output_type);
 | 
			
		||||
        ReadBasicSetting(Settings::values.output_device);
 | 
			
		||||
        ReadBasicSetting(Settings::values.input_type);
 | 
			
		||||
        ReadBasicSetting(Settings::values.input_device);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
| 
						 | 
				
			
			@ -847,10 +846,10 @@ void Config::SaveAudioValues() {
 | 
			
		|||
    WriteGlobalSetting(Settings::values.volume);
 | 
			
		||||
 | 
			
		||||
    if (global) {
 | 
			
		||||
        WriteBasicSetting(Settings::values.sink_id);
 | 
			
		||||
        WriteBasicSetting(Settings::values.audio_device_id);
 | 
			
		||||
        WriteBasicSetting(Settings::values.mic_input_device);
 | 
			
		||||
        WriteBasicSetting(Settings::values.mic_input_type);
 | 
			
		||||
        WriteBasicSetting(Settings::values.output_type);
 | 
			
		||||
        WriteBasicSetting(Settings::values.output_device);
 | 
			
		||||
        WriteBasicSetting(Settings::values.input_type);
 | 
			
		||||
        WriteBasicSetting(Settings::values.input_device);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,32 +4,27 @@
 | 
			
		|||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <QtGlobal>
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
#include "audio_core/cubeb_input.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "audio_core/input_details.h"
 | 
			
		||||
#include "audio_core/sink.h"
 | 
			
		||||
#include "audio_core/sink_details.h"
 | 
			
		||||
#include "citra_qt/configuration/configuration_shared.h"
 | 
			
		||||
#include "citra_qt/configuration/configure_audio.h"
 | 
			
		||||
#include "common/settings.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "ui_configure_audio.h"
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
#include "citra_qt/macos_authorization.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
constexpr int DEFAULT_INPUT_DEVICE_INDEX = 0;
 | 
			
		||||
 | 
			
		||||
ConfigureAudio::ConfigureAudio(QWidget* parent)
 | 
			
		||||
    : QWidget(parent), ui(std::make_unique<Ui::ConfigureAudio>()) {
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
 | 
			
		||||
    ui->output_sink_combo_box->clear();
 | 
			
		||||
    ui->output_sink_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
 | 
			
		||||
    for (const char* id : AudioCore::GetSinkIDs()) {
 | 
			
		||||
        ui->output_sink_combo_box->addItem(QString::fromUtf8(id));
 | 
			
		||||
    ui->output_type_combo_box->clear();
 | 
			
		||||
    for (u32 type = 0; type < static_cast<u32>(AudioCore::SinkType::NumSinkTypes); type++) {
 | 
			
		||||
        ui->output_type_combo_box->addItem(QString::fromUtf8(
 | 
			
		||||
            AudioCore::GetSinkName(static_cast<AudioCore::SinkType>(type)).data()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const bool is_running = Core::System::GetInstance().IsPoweredOn();
 | 
			
		||||
| 
						 | 
				
			
			@ -38,17 +33,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
 | 
			
		|||
    connect(ui->volume_slider, &QSlider::valueChanged, this,
 | 
			
		||||
            &ConfigureAudio::SetVolumeIndicatorText);
 | 
			
		||||
 | 
			
		||||
    ui->input_device_combo_box->clear();
 | 
			
		||||
    ui->input_device_combo_box->addItem(tr("Default"));
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
    for (const auto& device : AudioCore::ListCubebInputDevices()) {
 | 
			
		||||
        ui->input_device_combo_box->addItem(QString::fromStdString(device));
 | 
			
		||||
    ui->input_type_combo_box->clear();
 | 
			
		||||
    for (u32 type = 0; type < static_cast<u32>(AudioCore::InputType::NumInputTypes); type++) {
 | 
			
		||||
        ui->input_type_combo_box->addItem(QString::fromUtf8(
 | 
			
		||||
            AudioCore::GetInputName(static_cast<AudioCore::InputType>(type)).data()));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    connect(ui->input_type_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
 | 
			
		||||
            &ConfigureAudio::UpdateAudioInputDevices);
 | 
			
		||||
 | 
			
		||||
    ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
 | 
			
		||||
    ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
 | 
			
		||||
| 
						 | 
				
			
			@ -56,18 +45,23 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
 | 
			
		|||
    SetupPerGameUI();
 | 
			
		||||
    SetConfiguration();
 | 
			
		||||
 | 
			
		||||
    connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
 | 
			
		||||
    connect(ui->output_type_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
 | 
			
		||||
            &ConfigureAudio::UpdateAudioOutputDevices);
 | 
			
		||||
    connect(ui->input_type_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
 | 
			
		||||
            &ConfigureAudio::UpdateAudioInputDevices);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureAudio::~ConfigureAudio() {}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::SetConfiguration() {
 | 
			
		||||
    SetOutputSinkFromSinkID();
 | 
			
		||||
    SetOutputTypeFromSinkType();
 | 
			
		||||
    SetInputTypeFromInputType();
 | 
			
		||||
 | 
			
		||||
    // The device list cannot be pre-populated (nor listed) until the output sink is known.
 | 
			
		||||
    UpdateAudioOutputDevices(ui->output_sink_combo_box->currentIndex());
 | 
			
		||||
    SetAudioDeviceFromDeviceID();
 | 
			
		||||
    UpdateAudioOutputDevices(ui->output_type_combo_box->currentIndex());
 | 
			
		||||
    UpdateAudioInputDevices(ui->input_type_combo_box->currentIndex());
 | 
			
		||||
    SetOutputDeviceFromDeviceID();
 | 
			
		||||
    SetInputDeviceFromDeviceID();
 | 
			
		||||
 | 
			
		||||
    ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,39 +88,44 @@ void ConfigureAudio::SetConfiguration() {
 | 
			
		|||
        s32 selection = static_cast<s32>(Settings::values.audio_emulation.GetValue());
 | 
			
		||||
        ui->emulation_combo_box->setCurrentIndex(selection);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    s32 index = static_cast<s32>(Settings::values.mic_input_type.GetValue());
 | 
			
		||||
    ui->input_type_combo_box->setCurrentIndex(index);
 | 
			
		||||
 | 
			
		||||
    UpdateAudioInputDevices(index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::SetOutputSinkFromSinkID() {
 | 
			
		||||
    int new_sink_index = 0;
 | 
			
		||||
 | 
			
		||||
    const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue());
 | 
			
		||||
    for (int index = 0; index < ui->output_sink_combo_box->count(); index++) {
 | 
			
		||||
        if (ui->output_sink_combo_box->itemText(index) == sink_id) {
 | 
			
		||||
            new_sink_index = index;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
 | 
			
		||||
void ConfigureAudio::SetOutputTypeFromSinkType() {
 | 
			
		||||
    ui->output_type_combo_box->setCurrentIndex(
 | 
			
		||||
        static_cast<int>(Settings::values.output_type.GetValue()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::SetAudioDeviceFromDeviceID() {
 | 
			
		||||
void ConfigureAudio::SetOutputDeviceFromDeviceID() {
 | 
			
		||||
    int new_device_index = -1;
 | 
			
		||||
 | 
			
		||||
    const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue());
 | 
			
		||||
    for (int index = 0; index < ui->audio_device_combo_box->count(); index++) {
 | 
			
		||||
        if (ui->audio_device_combo_box->itemText(index) == device_id) {
 | 
			
		||||
    const QString device_id = QString::fromStdString(Settings::values.output_device.GetValue());
 | 
			
		||||
    for (int index = 0; index < ui->output_device_combo_box->count(); index++) {
 | 
			
		||||
        if (ui->output_device_combo_box->itemText(index) == device_id) {
 | 
			
		||||
            new_device_index = index;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ui->audio_device_combo_box->setCurrentIndex(new_device_index);
 | 
			
		||||
    ui->output_device_combo_box->setCurrentIndex(new_device_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::SetInputTypeFromInputType() {
 | 
			
		||||
    ui->input_type_combo_box->setCurrentIndex(
 | 
			
		||||
        static_cast<int>(Settings::values.input_type.GetValue()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::SetInputDeviceFromDeviceID() {
 | 
			
		||||
    int new_device_index = -1;
 | 
			
		||||
 | 
			
		||||
    const QString device_id = QString::fromStdString(Settings::values.input_device.GetValue());
 | 
			
		||||
    for (int index = 0; index < ui->input_device_combo_box->count(); index++) {
 | 
			
		||||
        if (ui->input_device_combo_box->itemText(index) == device_id) {
 | 
			
		||||
            new_device_index = index;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ui->input_device_combo_box->setCurrentIndex(new_device_index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
 | 
			
		||||
| 
						 | 
				
			
			@ -144,43 +143,40 @@ void ConfigureAudio::ApplyConfiguration() {
 | 
			
		|||
        });
 | 
			
		||||
 | 
			
		||||
    if (Settings::IsConfiguringGlobal()) {
 | 
			
		||||
        Settings::values.sink_id =
 | 
			
		||||
            ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
 | 
			
		||||
                .toStdString();
 | 
			
		||||
        Settings::values.audio_device_id =
 | 
			
		||||
            ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
 | 
			
		||||
                .toStdString();
 | 
			
		||||
        Settings::values.mic_input_type =
 | 
			
		||||
            static_cast<Settings::MicInputType>(ui->input_type_combo_box->currentIndex());
 | 
			
		||||
 | 
			
		||||
        if (ui->input_device_combo_box->currentIndex() == DEFAULT_INPUT_DEVICE_INDEX) {
 | 
			
		||||
            Settings::values.mic_input_device = Frontend::Mic::default_device_name;
 | 
			
		||||
        } else {
 | 
			
		||||
            Settings::values.mic_input_device =
 | 
			
		||||
                ui->input_device_combo_box->currentText().toStdString();
 | 
			
		||||
        }
 | 
			
		||||
        Settings::values.output_type =
 | 
			
		||||
            static_cast<AudioCore::SinkType>(ui->output_type_combo_box->currentIndex());
 | 
			
		||||
        Settings::values.output_device = ui->output_device_combo_box->currentText().toStdString();
 | 
			
		||||
        Settings::values.input_type =
 | 
			
		||||
            static_cast<AudioCore::InputType>(ui->input_type_combo_box->currentIndex());
 | 
			
		||||
        Settings::values.input_device = ui->input_device_combo_box->currentText().toStdString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::UpdateAudioOutputDevices(int sink_index) {
 | 
			
		||||
    ui->audio_device_combo_box->clear();
 | 
			
		||||
    ui->audio_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
 | 
			
		||||
    auto sink_type = static_cast<AudioCore::SinkType>(sink_index);
 | 
			
		||||
 | 
			
		||||
    const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
 | 
			
		||||
    for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) {
 | 
			
		||||
        ui->audio_device_combo_box->addItem(QString::fromStdString(device));
 | 
			
		||||
    ui->output_device_combo_box->clear();
 | 
			
		||||
    ui->output_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
 | 
			
		||||
 | 
			
		||||
    for (const auto& device : AudioCore::GetDeviceListForSink(sink_type)) {
 | 
			
		||||
        ui->output_device_combo_box->addItem(QString::fromStdString(device));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::UpdateAudioInputDevices(int index) {
 | 
			
		||||
void ConfigureAudio::UpdateAudioInputDevices(int input_index) {
 | 
			
		||||
    auto input_type = static_cast<AudioCore::InputType>(input_index);
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
    if (index == 1) {
 | 
			
		||||
    if (input_type != AudioCore::InputType::Null && input_type != AudioCore::InputType::Static) {
 | 
			
		||||
        AppleAuthorization::CheckAuthorizationForMicrophone();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    if (Settings::values.mic_input_device.GetValue() != Frontend::Mic::default_device_name) {
 | 
			
		||||
        ui->input_device_combo_box->setCurrentText(
 | 
			
		||||
            QString::fromStdString(Settings::values.mic_input_device.GetValue()));
 | 
			
		||||
 | 
			
		||||
    ui->input_device_combo_box->clear();
 | 
			
		||||
    ui->input_device_combo_box->addItem(QString::fromUtf8(AudioCore::auto_device_name));
 | 
			
		||||
 | 
			
		||||
    for (const auto& device : AudioCore::GetDeviceListForInput(input_type)) {
 | 
			
		||||
        ui->input_device_combo_box->addItem(QString::fromStdString(device));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -194,15 +190,15 @@ void ConfigureAudio::SetupPerGameUI() {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ui->output_sink_combo_box->setVisible(false);
 | 
			
		||||
    ui->output_sink_label->setVisible(false);
 | 
			
		||||
    ui->audio_device_combo_box->setVisible(false);
 | 
			
		||||
    ui->audio_device_label->setVisible(false);
 | 
			
		||||
    ui->output_type_combo_box->setVisible(false);
 | 
			
		||||
    ui->output_type_label->setVisible(false);
 | 
			
		||||
    ui->output_device_combo_box->setVisible(false);
 | 
			
		||||
    ui->output_device_label->setVisible(false);
 | 
			
		||||
    ui->input_type_label->setVisible(false);
 | 
			
		||||
    ui->input_type_combo_box->setVisible(false);
 | 
			
		||||
    ui->input_device_label->setVisible(false);
 | 
			
		||||
    ui->input_device_combo_box->setVisible(false);
 | 
			
		||||
    ui->microphone_layout->setVisible(false);
 | 
			
		||||
    ui->input_layout->setVisible(false);
 | 
			
		||||
 | 
			
		||||
    connect(ui->volume_combo_box, qOverload<int>(&QComboBox::activated), this, [this](int index) {
 | 
			
		||||
        ui->volume_slider->setEnabled(index == 1);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,8 +30,10 @@ private:
 | 
			
		|||
    void UpdateAudioOutputDevices(int sink_index);
 | 
			
		||||
    void UpdateAudioInputDevices(int index);
 | 
			
		||||
 | 
			
		||||
    void SetOutputSinkFromSinkID();
 | 
			
		||||
    void SetAudioDeviceFromDeviceID();
 | 
			
		||||
    void SetOutputTypeFromSinkType();
 | 
			
		||||
    void SetOutputDeviceFromDeviceID();
 | 
			
		||||
    void SetInputTypeFromInputType();
 | 
			
		||||
    void SetInputDeviceFromDeviceID();
 | 
			
		||||
    void SetVolumeIndicatorText(int percentage);
 | 
			
		||||
 | 
			
		||||
    void SetupPerGameUI();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@
 | 
			
		|||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="groupBox">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Audio</string>
 | 
			
		||||
      <string>Output</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QVBoxLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
| 
						 | 
				
			
			@ -62,16 +62,26 @@
 | 
			
		|||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <layout class="QHBoxLayout" name="output_engine_layout">
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QLabel" name="output_sink_label">
 | 
			
		||||
       <layout class="QGridLayout" name="output_layout" columnstretch="1,1">
 | 
			
		||||
        <item row="0" column="0">
 | 
			
		||||
         <widget class="QLabel" name="output_type_label">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Output Engine</string>
 | 
			
		||||
           <string>Output Type</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QComboBox" name="output_sink_combo_box"/>
 | 
			
		||||
        <item row="0" column="1">
 | 
			
		||||
         <widget class="QComboBox" name="output_type_combo_box"/>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item row="1" column="0">
 | 
			
		||||
         <widget class="QLabel" name="output_device_label">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Output Device</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item row="1" column="1">
 | 
			
		||||
         <widget class="QComboBox" name="output_device_combo_box"/>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </item>
 | 
			
		||||
| 
						 | 
				
			
			@ -85,20 +95,6 @@
 | 
			
		|||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <layout class="QHBoxLayout" name="audio_device_layout">
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QLabel" name="audio_device_label">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Audio Device</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QComboBox" name="audio_device_combo_box"/>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QWidget" name="volume_layout" native="true">
 | 
			
		||||
        <layout class="QHBoxLayout" name="horizontalLayout_2">
 | 
			
		||||
| 
						 | 
				
			
			@ -190,54 +186,30 @@
 | 
			
		|||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="microphone_layout">
 | 
			
		||||
    <widget class="QGroupBox" name="input_layout">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Microphone</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QVBoxLayout" name="verticalLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <layout class="QHBoxLayout" name="horizontalLayout">
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QLabel" name="input_type_label">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Input Type</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QComboBox" name="input_type_combo_box">
 | 
			
		||||
          <item>
 | 
			
		||||
           <property name="text">
 | 
			
		||||
            <string>None</string>
 | 
			
		||||
           </property>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <property name="text">
 | 
			
		||||
            <string>Real Device</string>
 | 
			
		||||
           </property>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <property name="text">
 | 
			
		||||
            <string>Static Noise</string>
 | 
			
		||||
           </property>
 | 
			
		||||
          </item>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
     <layout class="QGridLayout" name="input_inner_layout" columnstretch="1,1">
 | 
			
		||||
      <item row="0" column="0">
 | 
			
		||||
       <widget class="QLabel" name="input_type_label">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Input Type</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <layout class="QHBoxLayout" name="horizontalLayout_3">
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QLabel" name="input_device_label">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Input Device</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QComboBox" name="input_device_combo_box"/>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      <item row="0" column="1">
 | 
			
		||||
       <widget class="QComboBox" name="input_type_combo_box"/>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item row="1" column="0">
 | 
			
		||||
       <widget class="QLabel" name="input_device_label">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Input Device</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item row="1" column="1">
 | 
			
		||||
       <widget class="QComboBox" name="input_device_combo_box"/>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
| 
						 | 
				
			
			@ -259,10 +231,10 @@
 | 
			
		|||
 </widget>
 | 
			
		||||
 <tabstops>
 | 
			
		||||
  <tabstop>emulation_combo_box</tabstop>
 | 
			
		||||
  <tabstop>output_sink_combo_box</tabstop>
 | 
			
		||||
  <tabstop>toggle_audio_stretching</tabstop>
 | 
			
		||||
  <tabstop>audio_device_combo_box</tabstop>
 | 
			
		||||
  <tabstop>volume_slider</tabstop>
 | 
			
		||||
  <tabstop>output_type_combo_box</tabstop>
 | 
			
		||||
  <tabstop>output_device_combo_box</tabstop>
 | 
			
		||||
  <tabstop>input_type_combo_box</tabstop>
 | 
			
		||||
  <tabstop>input_device_combo_box</tabstop>
 | 
			
		||||
 </tabstops>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,6 +101,10 @@
 | 
			
		|||
#include "video_core/renderer_base.h"
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
#include "macos_authorization.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef USE_DISCORD_PRESENCE
 | 
			
		||||
#include "citra_qt/discord_impl.h"
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -2779,6 +2783,11 @@ int main(int argc, char* argv[]) {
 | 
			
		|||
    // Register Qt image interface
 | 
			
		||||
    system.RegisterImageInterface(std::make_shared<QtImageInterface>());
 | 
			
		||||
 | 
			
		||||
#ifdef __APPLE__
 | 
			
		||||
    // Register microphone permission check.
 | 
			
		||||
    system.RegisterMicPermissionCheck(&AppleAuthorization::CheckAuthorizationForMicrophone);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    main_window.show();
 | 
			
		||||
 | 
			
		||||
    QObject::connect(&app, &QGuiApplication::applicationStateChanged, &main_window,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ void Apply() {
 | 
			
		|||
    auto& system = Core::System::GetInstance();
 | 
			
		||||
    if (system.IsPoweredOn()) {
 | 
			
		||||
        system.CoreTiming().UpdateClockSpeed(values.cpu_clock_percentage.GetValue());
 | 
			
		||||
        Core::DSP().SetSink(values.sink_id.GetValue(), values.audio_device_id.GetValue());
 | 
			
		||||
        Core::DSP().SetSink(values.output_type.GetValue(), values.output_device.GetValue());
 | 
			
		||||
        Core::DSP().EnableStretching(values.enable_audio_stretching.GetValue());
 | 
			
		||||
 | 
			
		||||
        auto hid = Service::HID::GetModule(system);
 | 
			
		||||
| 
						 | 
				
			
			@ -156,11 +156,11 @@ void LogSettings() {
 | 
			
		|||
    log_setting("Utility_CustomTextures", values.custom_textures.GetValue());
 | 
			
		||||
    log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache.GetValue());
 | 
			
		||||
    log_setting("Audio_Emulation", GetAudioEmulationName(values.audio_emulation.GetValue()));
 | 
			
		||||
    log_setting("Audio_OutputEngine", values.sink_id.GetValue());
 | 
			
		||||
    log_setting("Audio_OutputType", values.output_type.GetValue());
 | 
			
		||||
    log_setting("Audio_OutputDevice", values.output_device.GetValue());
 | 
			
		||||
    log_setting("Audio_InputType", values.input_type.GetValue());
 | 
			
		||||
    log_setting("Audio_InputDevice", values.input_device.GetValue());
 | 
			
		||||
    log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
 | 
			
		||||
    log_setting("Audio_OutputDevice", values.audio_device_id.GetValue());
 | 
			
		||||
    log_setting("Audio_InputDeviceType", values.mic_input_type.GetValue());
 | 
			
		||||
    log_setting("Audio_InputDevice", values.mic_input_device.GetValue());
 | 
			
		||||
    using namespace Service::CAM;
 | 
			
		||||
    log_setting("Camera_OuterRightName", values.camera_name[OuterRightCamera]);
 | 
			
		||||
    log_setting("Camera_OuterRightConfig", values.camera_config[OuterRightCamera]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,8 @@
 | 
			
		|||
#include <string>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "audio_core/input_details.h"
 | 
			
		||||
#include "audio_core/sink_details.h"
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "core/hle/service/cam/cam_params.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,12 +44,6 @@ enum class LayoutOption : u32 {
 | 
			
		|||
    MobileLandscape,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class MicInputType : u32 {
 | 
			
		||||
    None = 0,
 | 
			
		||||
    Real = 1,
 | 
			
		||||
    Static = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class StereoRenderOption : u32 {
 | 
			
		||||
    Off = 0,
 | 
			
		||||
    SideBySide = 1,
 | 
			
		||||
| 
						 | 
				
			
			@ -482,12 +478,12 @@ struct Values {
 | 
			
		|||
    // Audio
 | 
			
		||||
    bool audio_muted;
 | 
			
		||||
    SwitchableSetting<AudioEmulation> audio_emulation{AudioEmulation::HLE, "audio_emulation"};
 | 
			
		||||
    Setting<std::string> sink_id{"auto", "output_engine"};
 | 
			
		||||
    SwitchableSetting<bool> enable_audio_stretching{true, "enable_audio_stretching"};
 | 
			
		||||
    Setting<std::string> audio_device_id{"auto", "output_device"};
 | 
			
		||||
    SwitchableSetting<float, true> volume{1.f, 0.f, 1.f, "volume"};
 | 
			
		||||
    Setting<MicInputType> mic_input_type{MicInputType::None, "mic_input_type"};
 | 
			
		||||
    Setting<std::string> mic_input_device{"Default", "mic_input_device"};
 | 
			
		||||
    Setting<AudioCore::SinkType> output_type{AudioCore::SinkType::Auto, "output_type"};
 | 
			
		||||
    Setting<std::string> output_device{"auto", "output_device"};
 | 
			
		||||
    Setting<AudioCore::InputType> input_type{AudioCore::InputType::Auto, "input_type"};
 | 
			
		||||
    Setting<std::string> input_device{"auto", "input_device"};
 | 
			
		||||
 | 
			
		||||
    // Camera
 | 
			
		||||
    std::array<std::string, Service::CAM::NumCameras> camera_name;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -110,8 +110,6 @@ add_library(citra_core STATIC
 | 
			
		|||
    frontend/image_interface.cpp
 | 
			
		||||
    frontend/image_interface.h
 | 
			
		||||
    frontend/input.h
 | 
			
		||||
    frontend/mic.cpp
 | 
			
		||||
    frontend/mic.h
 | 
			
		||||
    gdbstub/gdbstub.cpp
 | 
			
		||||
    gdbstub/gdbstub.h
 | 
			
		||||
    gdbstub/hio.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -411,8 +411,8 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window,
 | 
			
		|||
 | 
			
		||||
    memory->SetDSP(*dsp_core);
 | 
			
		||||
 | 
			
		||||
    dsp_core->SetSink(Settings::values.sink_id.GetValue(),
 | 
			
		||||
                      Settings::values.audio_device_id.GetValue());
 | 
			
		||||
    dsp_core->SetSink(Settings::values.output_type.GetValue(),
 | 
			
		||||
                      Settings::values.output_device.GetValue());
 | 
			
		||||
    dsp_core->EnableStretching(Settings::values.enable_audio_stretching.GetValue());
 | 
			
		||||
 | 
			
		||||
    telemetry_session = std::make_unique<Core::TelemetrySession>();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -304,6 +304,17 @@ public:
 | 
			
		|||
        return registered_image_interface;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Function for checking OS microphone permissions.
 | 
			
		||||
 | 
			
		||||
    void RegisterMicPermissionCheck(const std::function<bool()>& permission_func) {
 | 
			
		||||
        mic_permission_func = permission_func;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [[nodiscard]] bool HasMicPermission() {
 | 
			
		||||
        return !mic_permission_func || mic_permission_granted ||
 | 
			
		||||
               (mic_permission_granted = mic_permission_func());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SaveState(u32 slot) const;
 | 
			
		||||
 | 
			
		||||
    void LoadState(u32 slot);
 | 
			
		||||
| 
						 | 
				
			
			@ -397,6 +408,9 @@ private:
 | 
			
		|||
    Signal current_signal;
 | 
			
		||||
    u32 signal_param;
 | 
			
		||||
 | 
			
		||||
    std::function<bool()> mic_permission_func;
 | 
			
		||||
    bool mic_permission_granted = false;
 | 
			
		||||
 | 
			
		||||
    friend class boost::serialization::access;
 | 
			
		||||
    template <typename Archive>
 | 
			
		||||
    void serialize(Archive& ar, const unsigned int file_version);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,86 +0,0 @@
 | 
			
		|||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
#include "audio_core/cubeb_input.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
namespace Frontend::Mic {
 | 
			
		||||
 | 
			
		||||
constexpr std::array<u8, 16> NOISE_SAMPLE_8_BIT = {0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
 | 
			
		||||
                                                   0xFF, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF};
 | 
			
		||||
 | 
			
		||||
constexpr std::array<u8, 32> NOISE_SAMPLE_16_BIT = {
 | 
			
		||||
    0x64, 0x61, 0x74, 0x61, 0x56, 0xD7, 0x00, 0x00, 0x48, 0xF7, 0x86, 0x05, 0x77, 0x1A, 0xF4, 0x1F,
 | 
			
		||||
    0x28, 0x0F, 0x6B, 0xEB, 0x1C, 0xC0, 0xCB, 0x9D, 0x46, 0x90, 0xDF, 0x98, 0xEA, 0xAE, 0xB5, 0xC4};
 | 
			
		||||
 | 
			
		||||
Interface::~Interface() = default;
 | 
			
		||||
 | 
			
		||||
void NullMic::StartSampling(const Parameters& params) {
 | 
			
		||||
    parameters = params;
 | 
			
		||||
    is_sampling = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NullMic::StopSampling() {
 | 
			
		||||
    is_sampling = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NullMic::AdjustSampleRate(u32 sample_rate) {
 | 
			
		||||
    parameters.sample_rate = sample_rate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Samples NullMic::Read() {
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
StaticMic::StaticMic()
 | 
			
		||||
    : CACHE_8_BIT{NOISE_SAMPLE_8_BIT.begin(), NOISE_SAMPLE_8_BIT.end()},
 | 
			
		||||
      CACHE_16_BIT{NOISE_SAMPLE_16_BIT.begin(), NOISE_SAMPLE_16_BIT.end()} {}
 | 
			
		||||
 | 
			
		||||
StaticMic::~StaticMic() = default;
 | 
			
		||||
 | 
			
		||||
void StaticMic::StartSampling(const Parameters& params) {
 | 
			
		||||
    sample_rate = params.sample_rate;
 | 
			
		||||
    sample_size = params.sample_size;
 | 
			
		||||
 | 
			
		||||
    parameters = params;
 | 
			
		||||
    is_sampling = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StaticMic::StopSampling() {
 | 
			
		||||
    is_sampling = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StaticMic::AdjustSampleRate(u32 sample_rate) {}
 | 
			
		||||
 | 
			
		||||
Samples StaticMic::Read() {
 | 
			
		||||
    return (sample_size == 8) ? CACHE_8_BIT : CACHE_16_BIT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RealMicFactory::~RealMicFactory() = default;
 | 
			
		||||
 | 
			
		||||
NullFactory::~NullFactory() = default;
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Interface> NullFactory::Create([[maybe_unused]] std::string mic_device_name) {
 | 
			
		||||
    return std::make_unique<NullMic>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CUBEB
 | 
			
		||||
static std::unique_ptr<RealMicFactory> g_factory = std::make_unique<AudioCore::CubebFactory>();
 | 
			
		||||
#else
 | 
			
		||||
static std::unique_ptr<RealMicFactory> g_factory = std::make_unique<NullFactory>();
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
void RegisterRealMicFactory(std::unique_ptr<RealMicFactory> factory) {
 | 
			
		||||
    g_factory = std::move(factory);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Interface> CreateRealMic(std::string mic_device_name) {
 | 
			
		||||
    return g_factory->Create(std::move(mic_device_name));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Frontend::Mic
 | 
			
		||||
| 
						 | 
				
			
			@ -1,137 +0,0 @@
 | 
			
		|||
// Copyright 2019 Citra Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "common/swap.h"
 | 
			
		||||
#include "common/threadsafe_queue.h"
 | 
			
		||||
 | 
			
		||||
namespace Frontend::Mic {
 | 
			
		||||
 | 
			
		||||
constexpr char default_device_name[] = "Default";
 | 
			
		||||
 | 
			
		||||
enum class Signedness : u8 {
 | 
			
		||||
    Signed,
 | 
			
		||||
    Unsigned,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using Samples = std::vector<u8>;
 | 
			
		||||
 | 
			
		||||
struct Parameters {
 | 
			
		||||
    Signedness sign;
 | 
			
		||||
    u8 sample_size;
 | 
			
		||||
    bool buffer_loop;
 | 
			
		||||
    u32 sample_rate;
 | 
			
		||||
    u32 buffer_offset;
 | 
			
		||||
    u32 buffer_size;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Interface {
 | 
			
		||||
public:
 | 
			
		||||
    Interface() = default;
 | 
			
		||||
 | 
			
		||||
    virtual ~Interface();
 | 
			
		||||
 | 
			
		||||
    /// Starts the microphone. Called by Core
 | 
			
		||||
    virtual void StartSampling(const Parameters& params) = 0;
 | 
			
		||||
 | 
			
		||||
    /// Stops the microphone. Called by Core
 | 
			
		||||
    virtual void StopSampling() = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called from the actual event timing at a constant period under a given sample rate.
 | 
			
		||||
     * When sampling is enabled this function is expected to return a buffer of 16 samples in ideal
 | 
			
		||||
     * conditions, but can be lax if the data is coming in from another source like a real mic.
 | 
			
		||||
     */
 | 
			
		||||
    virtual Samples Read() = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adjusts the Parameters. Implementations should update the parameters field in addition to
 | 
			
		||||
     * changing the mic to sample according to the new parameters. Called by Core
 | 
			
		||||
     */
 | 
			
		||||
    virtual void AdjustSampleRate(u32 sample_rate) = 0;
 | 
			
		||||
 | 
			
		||||
    /// Value from 0 - 100 to adjust the mic gain setting. Called by Core
 | 
			
		||||
    virtual void SetGain(u8 mic_gain) {
 | 
			
		||||
        gain = mic_gain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u8 GetGain() const {
 | 
			
		||||
        return gain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void SetPower(bool power) {
 | 
			
		||||
        powered = power;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GetPower() const {
 | 
			
		||||
        return powered;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool IsSampling() const {
 | 
			
		||||
        return is_sampling;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const Parameters& GetParameters() const {
 | 
			
		||||
        return parameters;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    Parameters parameters;
 | 
			
		||||
    u8 gain = 0;
 | 
			
		||||
    bool is_sampling = false;
 | 
			
		||||
    bool powered = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NullMic final : public Interface {
 | 
			
		||||
public:
 | 
			
		||||
    void StartSampling(const Parameters& params) override;
 | 
			
		||||
 | 
			
		||||
    void StopSampling() override;
 | 
			
		||||
 | 
			
		||||
    void AdjustSampleRate(u32 sample_rate) override;
 | 
			
		||||
 | 
			
		||||
    Samples Read() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class StaticMic final : public Interface {
 | 
			
		||||
public:
 | 
			
		||||
    StaticMic();
 | 
			
		||||
    ~StaticMic() override;
 | 
			
		||||
 | 
			
		||||
    void StartSampling(const Parameters& params) override;
 | 
			
		||||
    void StopSampling() override;
 | 
			
		||||
    void AdjustSampleRate(u32 sample_rate) override;
 | 
			
		||||
 | 
			
		||||
    Samples Read() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    u16 sample_rate = 0;
 | 
			
		||||
    u8 sample_size = 0;
 | 
			
		||||
    std::vector<u8> CACHE_8_BIT;
 | 
			
		||||
    std::vector<u8> CACHE_16_BIT;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Factory for creating a real Mic input device;
 | 
			
		||||
class RealMicFactory {
 | 
			
		||||
public:
 | 
			
		||||
    virtual ~RealMicFactory();
 | 
			
		||||
 | 
			
		||||
    virtual std::unique_ptr<Interface> Create(std::string mic_device_name) = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NullFactory final : public RealMicFactory {
 | 
			
		||||
public:
 | 
			
		||||
    ~NullFactory() override;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Interface> Create(std::string mic_device_name) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void RegisterRealMicFactory(std::unique_ptr<RealMicFactory> factory);
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Interface> CreateRealMic(std::string mic_device_name);
 | 
			
		||||
 | 
			
		||||
} // namespace Frontend::Mic
 | 
			
		||||
| 
						 | 
				
			
			@ -3,11 +3,12 @@
 | 
			
		|||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <boost/serialization/weak_ptr.hpp>
 | 
			
		||||
#include "audio_core/input.h"
 | 
			
		||||
#include "audio_core/input_details.h"
 | 
			
		||||
#include "common/archives.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/settings.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/ipc_helpers.h"
 | 
			
		||||
#include "core/hle/kernel/event.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -167,7 +168,7 @@ struct MIC_U::Impl {
 | 
			
		|||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Frontend::Mic::Samples samples = mic->Read();
 | 
			
		||||
        AudioCore::Samples samples = mic->Read();
 | 
			
		||||
        if (!samples.empty()) {
 | 
			
		||||
            // write the samples to sharedmem page
 | 
			
		||||
            state.WriteSamples(samples);
 | 
			
		||||
| 
						 | 
				
			
			@ -180,8 +181,8 @@ struct MIC_U::Impl {
 | 
			
		|||
 | 
			
		||||
    void StartSampling() {
 | 
			
		||||
        auto sign = encoding == Encoding::PCM8Signed || encoding == Encoding::PCM16Signed
 | 
			
		||||
                        ? Frontend::Mic::Signedness::Signed
 | 
			
		||||
                        : Frontend::Mic::Signedness::Unsigned;
 | 
			
		||||
                        ? AudioCore::Signedness::Signed
 | 
			
		||||
                        : AudioCore::Signedness::Unsigned;
 | 
			
		||||
        mic->StartSampling({sign, state.sample_size, state.looped_buffer,
 | 
			
		||||
                            GetSampleRateInHz(state.sample_rate), state.initial_offset,
 | 
			
		||||
                            static_cast<u32>(state.size)});
 | 
			
		||||
| 
						 | 
				
			
			@ -349,21 +350,9 @@ struct MIC_U::Impl {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    void CreateMic() {
 | 
			
		||||
        std::unique_ptr<Frontend::Mic::Interface> new_mic;
 | 
			
		||||
        switch (Settings::values.mic_input_type.GetValue()) {
 | 
			
		||||
        case Settings::MicInputType::None:
 | 
			
		||||
            new_mic = std::make_unique<Frontend::Mic::NullMic>();
 | 
			
		||||
            break;
 | 
			
		||||
        case Settings::MicInputType::Real:
 | 
			
		||||
            new_mic = Frontend::Mic::CreateRealMic(Settings::values.mic_input_device.GetValue());
 | 
			
		||||
            break;
 | 
			
		||||
        case Settings::MicInputType::Static:
 | 
			
		||||
            new_mic = std::make_unique<Frontend::Mic::StaticMic>();
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            LOG_CRITICAL(Audio, "Mic type not found. Defaulting to null mic");
 | 
			
		||||
            new_mic = std::make_unique<Frontend::Mic::NullMic>();
 | 
			
		||||
        }
 | 
			
		||||
        std::unique_ptr<AudioCore::Input> new_mic = AudioCore::CreateInputFromID(
 | 
			
		||||
            Settings::values.input_type.GetValue(), Settings::values.input_device.GetValue());
 | 
			
		||||
 | 
			
		||||
        // If theres already a mic, copy over any data to the new mic impl
 | 
			
		||||
        if (mic) {
 | 
			
		||||
            new_mic->SetGain(mic->GetGain());
 | 
			
		||||
| 
						 | 
				
			
			@ -386,7 +375,7 @@ struct MIC_U::Impl {
 | 
			
		|||
    u32 client_version = 0;
 | 
			
		||||
    bool allow_shell_closed = false;
 | 
			
		||||
    bool clamp = false;
 | 
			
		||||
    std::unique_ptr<Frontend::Mic::Interface> mic;
 | 
			
		||||
    std::unique_ptr<AudioCore::Input> mic;
 | 
			
		||||
    Core::Timing& timing;
 | 
			
		||||
    State state{};
 | 
			
		||||
    Encoding encoding{};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,6 @@
 | 
			
		|||
#include "common/settings.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/file_sys/plugin_3gx.h"
 | 
			
		||||
#include "core/frontend/mic.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/ipc_helpers.h"
 | 
			
		||||
#include "core/hle/kernel/event.h"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -124,7 +124,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
 | 
			
		|||
    Telemetry::AppendOSInfo(field_collection);
 | 
			
		||||
 | 
			
		||||
    // Log user configuration information
 | 
			
		||||
    AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id.GetValue());
 | 
			
		||||
    AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId",
 | 
			
		||||
             static_cast<int>(Settings::values.output_type.GetValue()));
 | 
			
		||||
    AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
 | 
			
		||||
             Settings::values.enable_audio_stretching.GetValue());
 | 
			
		||||
    AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue