early-access version 2840
This commit is contained in:
parent
b866bff3f2
commit
e8706a3c92
77 changed files with 2720 additions and 1054 deletions
|
@ -244,7 +244,7 @@ if(ENABLE_QT)
|
||||||
# Check for system Qt on Linux, fallback to bundled Qt
|
# Check for system Qt on Linux, fallback to bundled Qt
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
if (NOT YUZU_USE_BUNDLED_QT)
|
if (NOT YUZU_USE_BUNDLED_QT)
|
||||||
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus)
|
find_package(Qt5 ${QT_VERSION} COMPONENTS Widgets DBus Multimedia)
|
||||||
endif()
|
endif()
|
||||||
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
if (NOT Qt5_FOUND OR YUZU_USE_BUNDLED_QT)
|
||||||
# Check for dependencies, then enable bundled Qt download
|
# Check for dependencies, then enable bundled Qt download
|
||||||
|
@ -351,9 +351,9 @@ if(ENABLE_QT)
|
||||||
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
set(YUZU_QT_NO_CMAKE_SYSTEM_PATH "NO_CMAKE_SYSTEM_PATH")
|
||||||
endif()
|
endif()
|
||||||
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
|
if ((${CMAKE_SYSTEM_NAME} STREQUAL "Linux") AND YUZU_USE_BUNDLED_QT)
|
||||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets DBus ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets DBus Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||||
else()
|
else()
|
||||||
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
find_package(Qt5 ${QT_VERSION} REQUIRED COMPONENTS Widgets Multimedia ${QT_PREFIX_HINT} ${YUZU_QT_NO_CMAKE_SYSTEM_PATH})
|
||||||
endif()
|
endif()
|
||||||
if (YUZU_USE_QT_WEB_ENGINE)
|
if (YUZU_USE_QT_WEB_ENGINE)
|
||||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||||
|
@ -460,7 +460,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||||
if(ENABLE_QT)
|
if(ENABLE_QT)
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||||
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
|
list(APPEND CMAKE_PREFIX_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||||
find_package(Qt5 5.15 REQUIRED COMPONENTS Widgets)
|
find_package(Qt5 5.15 REQUIRED COMPONENTS Widgets Multimedia)
|
||||||
if (YUZU_USE_QT_WEB_ENGINE)
|
if (YUZU_USE_QT_WEB_ENGINE)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
find_package(Qt5 REQUIRED COMPONENTS WebEngineCore WebEngineWidgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -10,11 +10,13 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||||
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
||||||
set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
|
set(Qt5_PLATFORMTHEMES_DIR "${Qt5_DIR}/../../../plugins/platformthemes/")
|
||||||
set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
|
set(Qt5_PLATFORMINPUTCONTEXTS_DIR "${Qt5_DIR}/../../../plugins/platforminputcontexts/")
|
||||||
|
set(Qt5_MEDIASERVICE_DIR "${Qt5_DIR}/../../../plugins/mediaservice/")
|
||||||
set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
|
set(Qt5_XCBGLINTEGRATIONS_DIR "${Qt5_DIR}/../../../plugins/xcbglintegrations/")
|
||||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||||
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
||||||
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
|
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
|
||||||
|
set(MEDIASERVICE ${DLL_DEST}mediaservice/)
|
||||||
set(STYLES ${DLL_DEST}plugins/styles/)
|
set(STYLES ${DLL_DEST}plugins/styles/)
|
||||||
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
|
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
@ -25,6 +27,8 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||||
Qt5Core$<$<CONFIG:Debug>:d>.*
|
Qt5Core$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
Qt5Gui$<$<CONFIG:Debug>:d>.*
|
||||||
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
Qt5Widgets$<$<CONFIG:Debug>:d>.*
|
||||||
|
Qt5Multimedia$<$<CONFIG:Debug>:d>.*
|
||||||
|
Qt5Network$<$<CONFIG:Debug>:d>.*
|
||||||
)
|
)
|
||||||
|
|
||||||
if (YUZU_USE_QT_WEB_ENGINE)
|
if (YUZU_USE_QT_WEB_ENGINE)
|
||||||
|
@ -56,7 +60,11 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
|
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
|
||||||
qjpeg$<$<CONFIG:Debug>:d>.*
|
qjpeg$<$<CONFIG:Debug>:d>.*
|
||||||
qgif$<$<CONFIG:Debug>:d>.*
|
qgif$<$<CONFIG:Debug>:d>.*
|
||||||
)
|
)
|
||||||
|
windows_copy_files(yuzu ${Qt5_MEDIASERVICE_DIR} ${MEDIASERVICE}
|
||||||
|
dsengine$<$<CONFIG:Debug>:d>.*
|
||||||
|
wmfengine$<$<CONFIG:Debug>:d>.*
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
set(Qt5_DLLS
|
set(Qt5_DLLS
|
||||||
"${Qt5_DLL_DIR}libQt5Core.so.5"
|
"${Qt5_DLL_DIR}libQt5Core.so.5"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 2839.
|
This is the source code for early-access 2840.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
10
externals/cubeb/.github/workflows/build.yml
vendored
10
externals/cubeb/.github/workflows/build.yml
vendored
|
@ -9,11 +9,11 @@ jobs:
|
||||||
BUILD_TYPE: ${{ matrix.type }}
|
BUILD_TYPE: ${{ matrix.type }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, windows-2019, macos-11]
|
os: [ubuntu-20.04, windows-2019, macos-10.15]
|
||||||
type: [Release, Debug]
|
type: [Release, Debug]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
|
@ -46,13 +46,13 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
type: [Release, Debug]
|
type: [Release, Debug]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Configure CMake
|
- name: Configure CMake
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=android-28
|
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=android-26
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -61,7 +61,7 @@ jobs:
|
||||||
check_format:
|
check_format:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
|
|
25
externals/cubeb/CMakeLists.txt
vendored
25
externals/cubeb/CMakeLists.txt
vendored
|
@ -139,7 +139,7 @@ if(NOT BUNDLE_SPEEX)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT TARGET speex)
|
if(NOT TARGET speex)
|
||||||
add_library(speex OBJECT subprojects/speex/resample.c)
|
add_library(speex STATIC subprojects/speex/resample.c)
|
||||||
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
|
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
|
||||||
target_include_directories(speex INTERFACE subprojects)
|
target_include_directories(speex INTERFACE subprojects)
|
||||||
target_compile_definitions(speex PUBLIC
|
target_compile_definitions(speex PUBLIC
|
||||||
|
@ -259,7 +259,7 @@ if(USE_WASAPI)
|
||||||
target_sources(cubeb PRIVATE
|
target_sources(cubeb PRIVATE
|
||||||
src/cubeb_wasapi.cpp)
|
src/cubeb_wasapi.cpp)
|
||||||
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
|
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
|
||||||
target_link_libraries(cubeb PRIVATE avrt ole32 ksuser)
|
target_link_libraries(cubeb PRIVATE avrt ole32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_files("windows.h;mmsystem.h" USE_WINMM)
|
check_include_files("windows.h;mmsystem.h" USE_WINMM)
|
||||||
|
@ -284,22 +284,9 @@ if(HAVE_SYS_SOUNDCARD_H)
|
||||||
try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests"
|
try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests"
|
||||||
${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c)
|
${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c)
|
||||||
if(USE_OSS)
|
if(USE_OSS)
|
||||||
# strlcpy is not available on BSD systems that use glibc,
|
target_sources(cubeb PRIVATE
|
||||||
# like Debian kfreebsd, so try using libbsd if available
|
src/cubeb_oss.c)
|
||||||
include(CheckSymbolExists)
|
target_compile_definitions(cubeb PRIVATE USE_OSS)
|
||||||
check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
|
|
||||||
if(NOT HAVE_STRLCPY)
|
|
||||||
pkg_check_modules(libbsd-overlay IMPORTED_TARGET libbsd-overlay)
|
|
||||||
if(libbsd-overlay_FOUND)
|
|
||||||
target_link_libraries(cubeb PRIVATE PkgConfig::libbsd-overlay)
|
|
||||||
set(HAVE_STRLCPY true)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if (HAVE_STRLCPY)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_oss.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_OSS)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -380,7 +367,7 @@ if(BUILD_TESTS)
|
||||||
add_executable(test_${NAME} test/test_${NAME}.cpp)
|
add_executable(test_${NAME} test/test_${NAME}.cpp)
|
||||||
target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include src)
|
target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include src)
|
||||||
target_link_libraries(test_${NAME} PRIVATE cubeb gtest_main)
|
target_link_libraries(test_${NAME} PRIVATE cubeb gtest_main)
|
||||||
add_test(${NAME} test_${NAME} --gtest_death_test_style=threadsafe)
|
add_test(${NAME} test_${NAME})
|
||||||
add_sanitizers(test_${NAME})
|
add_sanitizers(test_${NAME})
|
||||||
install(TARGETS test_${NAME})
|
install(TARGETS test_${NAME})
|
||||||
endmacro(cubeb_add_test)
|
endmacro(cubeb_add_test)
|
||||||
|
|
12
externals/cubeb/INSTALL.md
vendored
12
externals/cubeb/INSTALL.md
vendored
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
You must have CMake v3.1 or later installed.
|
You must have CMake v3.1 or later installed.
|
||||||
|
|
||||||
1. `git clone --recursive https://github.com/mozilla/cubeb.git`
|
1. `git clone --recursive https://github.com/kinetiknz/cubeb.git`
|
||||||
2. `mkdir cubeb-build`
|
2. `mkdir cubeb-build`
|
||||||
3. `cd cubeb-build`
|
3. `cd cubeb-build`
|
||||||
4. `cmake ../cubeb`
|
3. `cmake ../cubeb`
|
||||||
5. `cmake --build .`
|
4. `cmake --build .`
|
||||||
6. `ctest`
|
5. `ctest`
|
||||||
|
|
||||||
# Windows build notes
|
# Windows build notes
|
||||||
|
|
||||||
|
@ -41,6 +41,6 @@ To build with MinGW-w64, install the following items:
|
||||||
- Download and install MinGW-w64 with Win32 threads.
|
- Download and install MinGW-w64 with Win32 threads.
|
||||||
- Download and install CMake.
|
- Download and install CMake.
|
||||||
- Run MinGW-w64 Terminal from the Start Menu.
|
- Run MinGW-w64 Terminal from the Start Menu.
|
||||||
- Follow the build steps at the top of this file, but at step 4 run:
|
- Follow the build steps at the top of this file, but at step 3 run:
|
||||||
`cmake -G "MinGW Makefiles" ../cubeb`
|
`cmake -G "MinGW Makefiles" ..`
|
||||||
- Continue the build steps at the top of this file.
|
- Continue the build steps at the top of this file.
|
||||||
|
|
2
externals/cubeb/README.md
vendored
2
externals/cubeb/README.md
vendored
|
@ -2,6 +2,6 @@
|
||||||
|
|
||||||
See INSTALL.md for build instructions.
|
See INSTALL.md for build instructions.
|
||||||
|
|
||||||
See [Backend Support](https://github.com/mozilla/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
|
See [Backend Support](https://github.com/kinetiknz/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
|
||||||
|
|
||||||
Licensed under an ISC-style license. See LICENSE for details.
|
Licensed under an ISC-style license. See LICENSE for details.
|
||||||
|
|
3
externals/cubeb/src/cubeb_aaudio.cpp
vendored
3
externals/cubeb/src/cubeb_aaudio.cpp
vendored
|
@ -935,8 +935,7 @@ aaudio_stream_init_impl(cubeb_stream * stm, cubeb_devid input_device,
|
||||||
stm->resampler = cubeb_resampler_create(
|
stm->resampler = cubeb_resampler_create(
|
||||||
stm, input_stream_params ? &in_params : NULL,
|
stm, input_stream_params ? &in_params : NULL,
|
||||||
output_stream_params ? &out_params : NULL, target_sample_rate,
|
output_stream_params ? &out_params : NULL, target_sample_rate,
|
||||||
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT,
|
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
LOG("Failed to create resampler");
|
LOG("Failed to create resampler");
|
||||||
|
|
17
externals/cubeb/src/cubeb_audiounit.cpp
vendored
17
externals/cubeb/src/cubeb_audiounit.cpp
vendored
|
@ -541,13 +541,6 @@ audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||||
long outframes = cubeb_resampler_fill(stm->resampler.get(),
|
long outframes = cubeb_resampler_fill(stm->resampler.get(),
|
||||||
stm->input_linear_buffer->data(),
|
stm->input_linear_buffer->data(),
|
||||||
&total_input_frames, NULL, 0);
|
&total_input_frames, NULL, 0);
|
||||||
if (outframes < 0) {
|
|
||||||
stm->shutdown = true;
|
|
||||||
OSStatus r = AudioOutputUnitStop(stm->input_unit);
|
|
||||||
assert(r == 0);
|
|
||||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
stm->draining = outframes < total_input_frames;
|
stm->draining = outframes < total_input_frames;
|
||||||
|
|
||||||
// Reset input buffer
|
// Reset input buffer
|
||||||
|
@ -1449,13 +1442,6 @@ audiounit_destroy(cubeb * ctx)
|
||||||
audiounit_active_streams(ctx));
|
audiounit_active_streams(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroying a cubeb context with device collection callbacks registered
|
|
||||||
// is misuse of the API, assert then attempt to clean up.
|
|
||||||
assert(!ctx->input_collection_changed_callback &&
|
|
||||||
!ctx->input_collection_changed_user_ptr &&
|
|
||||||
!ctx->output_collection_changed_callback &&
|
|
||||||
!ctx->output_collection_changed_user_ptr);
|
|
||||||
|
|
||||||
/* Unregister the callback if necessary. */
|
/* Unregister the callback if necessary. */
|
||||||
if (ctx->input_collection_changed_callback) {
|
if (ctx->input_collection_changed_callback) {
|
||||||
audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
|
audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
|
||||||
|
@ -2714,8 +2700,7 @@ audiounit_setup_stream(cubeb_stream * stm)
|
||||||
stm->resampler.reset(cubeb_resampler_create(
|
stm->resampler.reset(cubeb_resampler_create(
|
||||||
stm, has_input(stm) ? &input_unconverted_params : NULL,
|
stm, has_input(stm) ? &input_unconverted_params : NULL,
|
||||||
has_output(stm) ? &stm->output_stream_params : NULL, target_sample_rate,
|
has_output(stm) ? &stm->output_stream_params : NULL, target_sample_rate,
|
||||||
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP));
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE));
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
LOG("(%p) Could not create resampler.", stm);
|
LOG("(%p) Could not create resampler.", stm);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
|
9
externals/cubeb/src/cubeb_jack.cpp
vendored
9
externals/cubeb/src/cubeb_jack.cpp
vendored
|
@ -925,18 +925,15 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
if (stm->devs == DUPLEX) {
|
if (stm->devs == DUPLEX) {
|
||||||
stm->resampler = cubeb_resampler_create(
|
stm->resampler = cubeb_resampler_create(
|
||||||
stm, &stm->in_params, &stm->out_params, stream_actual_rate,
|
stm, &stm->in_params, &stm->out_params, stream_actual_rate,
|
||||||
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
} else if (stm->devs == IN_ONLY) {
|
} else if (stm->devs == IN_ONLY) {
|
||||||
stm->resampler = cubeb_resampler_create(
|
stm->resampler = cubeb_resampler_create(
|
||||||
stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback,
|
stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback,
|
||||||
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
} else if (stm->devs == OUT_ONLY) {
|
} else if (stm->devs == OUT_ONLY) {
|
||||||
stm->resampler = cubeb_resampler_create(
|
stm->resampler = cubeb_resampler_create(
|
||||||
stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback,
|
stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback,
|
||||||
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
|
|
2
externals/cubeb/src/cubeb_log.cpp
vendored
2
externals/cubeb/src/cubeb_log.cpp
vendored
|
@ -123,7 +123,7 @@ cubeb_async_log(char const * fmt, ...)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cubeb_async_log_reset_threads(void)
|
cubeb_async_log_reset_threads()
|
||||||
{
|
{
|
||||||
if (!g_cubeb_log_callback) {
|
if (!g_cubeb_log_callback) {
|
||||||
return;
|
return;
|
||||||
|
|
2
externals/cubeb/src/cubeb_log.h
vendored
2
externals/cubeb/src/cubeb_log.h
vendored
|
@ -35,7 +35,7 @@ extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
|
||||||
void
|
void
|
||||||
cubeb_async_log(const char * fmt, ...);
|
cubeb_async_log(const char * fmt, ...);
|
||||||
void
|
void
|
||||||
cubeb_async_log_reset_threads(void);
|
cubeb_async_log_reset_threads();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
3
externals/cubeb/src/cubeb_opensl.c
vendored
3
externals/cubeb/src/cubeb_opensl.c
vendored
|
@ -1479,8 +1479,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream,
|
||||||
stm->resampler = cubeb_resampler_create(
|
stm->resampler = cubeb_resampler_create(
|
||||||
stm, input_stream_params ? &input_params : NULL,
|
stm, input_stream_params ? &input_params : NULL,
|
||||||
output_stream_params ? &output_params : NULL, target_sample_rate,
|
output_stream_params ? &output_params : NULL, target_sample_rate,
|
||||||
data_callback, user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT,
|
data_callback, user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
LOG("Failed to create resampler");
|
LOG("Failed to create resampler");
|
||||||
opensl_stream_destroy(stm);
|
opensl_stream_destroy(stm);
|
||||||
|
|
68
externals/cubeb/src/cubeb_oss.c
vendored
68
externals/cubeb/src/cubeb_oss.c
vendored
|
@ -96,8 +96,9 @@ struct oss_stream {
|
||||||
oss_devnode_t name;
|
oss_devnode_t name;
|
||||||
int fd;
|
int fd;
|
||||||
void * buf;
|
void * buf;
|
||||||
|
unsigned int nfr; /* Number of frames allocated */
|
||||||
|
unsigned int nfrags;
|
||||||
unsigned int bufframes;
|
unsigned int bufframes;
|
||||||
unsigned int maxframes;
|
|
||||||
|
|
||||||
struct stream_info {
|
struct stream_info {
|
||||||
int channels;
|
int channels;
|
||||||
|
@ -823,9 +824,9 @@ retry:
|
||||||
pfds[0].fd = s->play.fd;
|
pfds[0].fd = s->play.fd;
|
||||||
pfds[1].fd = -1;
|
pfds[1].fd = -1;
|
||||||
goto retry;
|
goto retry;
|
||||||
} else if (tnfr > (long)s->play.maxframes) {
|
} else if (tnfr > (long)s->play.bufframes) {
|
||||||
/* too many frames available - limit */
|
/* too many frames available - limit */
|
||||||
tnfr = (long)s->play.maxframes;
|
tnfr = (long)s->play.bufframes;
|
||||||
}
|
}
|
||||||
if (nfr > tnfr) {
|
if (nfr > tnfr) {
|
||||||
nfr = tnfr;
|
nfr = tnfr;
|
||||||
|
@ -841,9 +842,9 @@ retry:
|
||||||
pfds[0].fd = -1;
|
pfds[0].fd = -1;
|
||||||
pfds[1].fd = s->record.fd;
|
pfds[1].fd = s->record.fd;
|
||||||
goto retry;
|
goto retry;
|
||||||
} else if (tnfr > (long)s->record.maxframes) {
|
} else if (tnfr > (long)s->record.bufframes) {
|
||||||
/* too many frames available - limit */
|
/* too many frames available - limit */
|
||||||
tnfr = (long)s->record.maxframes;
|
tnfr = (long)s->record.bufframes;
|
||||||
}
|
}
|
||||||
if (nfr > tnfr) {
|
if (nfr > tnfr) {
|
||||||
nfr = tnfr;
|
nfr = tnfr;
|
||||||
|
@ -1060,8 +1061,6 @@ oss_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
}
|
}
|
||||||
if (input_stream_params != NULL) {
|
if (input_stream_params != NULL) {
|
||||||
unsigned int nb_channels;
|
unsigned int nb_channels;
|
||||||
uint32_t minframes;
|
|
||||||
|
|
||||||
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||||
LOG("Loopback not supported");
|
LOG("Loopback not supported");
|
||||||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
|
@ -1090,17 +1089,18 @@ oss_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||||
s->record.frame_size =
|
s->record.frame_size =
|
||||||
s->record.info.channels * (s->record.info.precision / 8);
|
s->record.info.channels * (s->record.info.precision / 8);
|
||||||
s->record.bufframes = latency_frames;
|
s->record.nfrags = OSS_NFRAGS;
|
||||||
|
s->record.nfr = latency_frames / OSS_NFRAGS;
|
||||||
oss_get_min_latency(context, *input_stream_params, &minframes);
|
s->record.bufframes = s->record.nfrags * s->record.nfr;
|
||||||
if (s->record.bufframes < minframes) {
|
uint32_t minnfr;
|
||||||
s->record.bufframes = minframes;
|
oss_get_min_latency(context, *input_stream_params, &minnfr);
|
||||||
|
if (s->record.nfr < minnfr) {
|
||||||
|
s->record.nfr = minnfr;
|
||||||
|
s->record.nfrags = latency_frames / minnfr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (output_stream_params != NULL) {
|
if (output_stream_params != NULL) {
|
||||||
unsigned int nb_channels;
|
unsigned int nb_channels;
|
||||||
uint32_t minframes;
|
|
||||||
|
|
||||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||||
LOG("Loopback not supported");
|
LOG("Loopback not supported");
|
||||||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
|
@ -1128,16 +1128,19 @@ oss_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
}
|
}
|
||||||
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||||
s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8);
|
s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8);
|
||||||
s->play.bufframes = latency_frames;
|
s->play.nfrags = OSS_NFRAGS;
|
||||||
|
s->play.nfr = latency_frames / OSS_NFRAGS;
|
||||||
oss_get_min_latency(context, *output_stream_params, &minframes);
|
uint32_t minnfr;
|
||||||
if (s->play.bufframes < minframes) {
|
oss_get_min_latency(context, *output_stream_params, &minnfr);
|
||||||
s->play.bufframes = minframes;
|
if (s->play.nfr < minnfr) {
|
||||||
|
s->play.nfr = minnfr;
|
||||||
|
s->play.nfrags = latency_frames / minnfr;
|
||||||
}
|
}
|
||||||
|
s->play.bufframes = s->play.nfrags * s->play.nfr;
|
||||||
}
|
}
|
||||||
if (s->play.fd != -1) {
|
if (s->play.fd != -1) {
|
||||||
int frag = oss_get_frag_params(
|
int frag = oss_get_frag_params(
|
||||||
oss_calc_frag_shift(s->play.bufframes, s->play.frame_size));
|
oss_calc_frag_shift(s->play.nfr, s->play.frame_size));
|
||||||
if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
||||||
LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
||||||
frag);
|
frag);
|
||||||
|
@ -1145,28 +1148,19 @@ oss_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi))
|
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi))
|
||||||
LOG("Failed to get play fd's buffer info.");
|
LOG("Failed to get play fd's buffer info.");
|
||||||
else {
|
else {
|
||||||
s->play.bufframes = (bi.fragsize * bi.fragstotal) / s->play.frame_size;
|
s->play.nfr = bi.fragsize / s->play.frame_size;
|
||||||
|
s->play.nfrags = bi.fragments;
|
||||||
|
s->play.bufframes = s->play.nfr * s->play.nfrags;
|
||||||
}
|
}
|
||||||
int lw;
|
|
||||||
|
|
||||||
/*
|
int lw = s->play.frame_size;
|
||||||
* Force 32 ms service intervals at most, or when recording is
|
|
||||||
* active, use the recording service intervals as a reference.
|
|
||||||
*/
|
|
||||||
s->play.maxframes = (32 * output_stream_params->rate) / 1000;
|
|
||||||
if (s->record.fd != -1 || s->play.maxframes >= s->play.bufframes) {
|
|
||||||
lw = s->play.frame_size; /* Feed data when possible. */
|
|
||||||
s->play.maxframes = s->play.bufframes;
|
|
||||||
} else {
|
|
||||||
lw = (s->play.bufframes - s->play.maxframes) * s->play.frame_size;
|
|
||||||
}
|
|
||||||
if (ioctl(s->play.fd, SNDCTL_DSP_LOW_WATER, &lw))
|
if (ioctl(s->play.fd, SNDCTL_DSP_LOW_WATER, &lw))
|
||||||
LOG("Audio device \"%s\" (play) could not set trigger threshold",
|
LOG("Audio device \"%s\" (play) could not set trigger threshold",
|
||||||
s->play.name);
|
s->play.name);
|
||||||
}
|
}
|
||||||
if (s->record.fd != -1) {
|
if (s->record.fd != -1) {
|
||||||
int frag = oss_get_frag_params(
|
int frag = oss_get_frag_params(
|
||||||
oss_calc_frag_shift(s->record.bufframes, s->record.frame_size));
|
oss_calc_frag_shift(s->record.nfr, s->record.frame_size));
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
||||||
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
||||||
frag);
|
frag);
|
||||||
|
@ -1174,11 +1168,11 @@ oss_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi))
|
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi))
|
||||||
LOG("Failed to get record fd's buffer info.");
|
LOG("Failed to get record fd's buffer info.");
|
||||||
else {
|
else {
|
||||||
s->record.bufframes =
|
s->record.nfr = bi.fragsize / s->record.frame_size;
|
||||||
(bi.fragsize * bi.fragstotal) / s->record.frame_size;
|
s->record.nfrags = bi.fragments;
|
||||||
|
s->record.bufframes = s->record.nfr * s->record.nfrags;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->record.maxframes = s->record.bufframes;
|
|
||||||
int lw = s->record.frame_size;
|
int lw = s->record.frame_size;
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_LOW_WATER, &lw))
|
if (ioctl(s->record.fd, SNDCTL_DSP_LOW_WATER, &lw))
|
||||||
LOG("Audio device \"%s\" (record) could not set trigger threshold",
|
LOG("Audio device \"%s\" (record) could not set trigger threshold",
|
||||||
|
|
8
externals/cubeb/src/cubeb_pulse.c
vendored
8
externals/cubeb/src/cubeb_pulse.c
vendored
|
@ -280,7 +280,6 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes,
|
||||||
if (got < 0) {
|
if (got < 0) {
|
||||||
WRAP(pa_stream_cancel_write)(s);
|
WRAP(pa_stream_cancel_write)(s);
|
||||||
stm->shutdown = 1;
|
stm->shutdown = 1;
|
||||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If more iterations move offset of read buffer
|
// If more iterations move offset of read buffer
|
||||||
|
@ -393,9 +392,6 @@ stream_read_callback(pa_stream * s, size_t nbytes, void * u)
|
||||||
if (got < 0 || (size_t)got != read_frames) {
|
if (got < 0 || (size_t)got != read_frames) {
|
||||||
WRAP(pa_stream_cancel_write)(s);
|
WRAP(pa_stream_cancel_write)(s);
|
||||||
stm->shutdown = 1;
|
stm->shutdown = 1;
|
||||||
if (got < 0) {
|
|
||||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -787,10 +783,6 @@ pulse_context_destroy(cubeb * ctx)
|
||||||
static void
|
static void
|
||||||
pulse_destroy(cubeb * ctx)
|
pulse_destroy(cubeb * ctx)
|
||||||
{
|
{
|
||||||
assert(!ctx->input_collection_changed_callback &&
|
|
||||||
!ctx->input_collection_changed_user_ptr &&
|
|
||||||
!ctx->output_collection_changed_callback &&
|
|
||||||
!ctx->output_collection_changed_user_ptr);
|
|
||||||
free(ctx->context_name);
|
free(ctx->context_name);
|
||||||
if (ctx->context) {
|
if (ctx->context) {
|
||||||
pulse_context_destroy(ctx);
|
pulse_context_destroy(ctx);
|
||||||
|
|
15
externals/cubeb/src/cubeb_resampler.cpp
vendored
15
externals/cubeb/src/cubeb_resampler.cpp
vendored
|
@ -323,8 +323,7 @@ cubeb_resampler_create(cubeb_stream * stream,
|
||||||
cubeb_stream_params * input_params,
|
cubeb_stream_params * input_params,
|
||||||
cubeb_stream_params * output_params,
|
cubeb_stream_params * output_params,
|
||||||
unsigned int target_rate, cubeb_data_callback callback,
|
unsigned int target_rate, cubeb_data_callback callback,
|
||||||
void * user_ptr, cubeb_resampler_quality quality,
|
void * user_ptr, cubeb_resampler_quality quality)
|
||||||
cubeb_resampler_reclock reclock)
|
|
||||||
{
|
{
|
||||||
cubeb_sample_format format;
|
cubeb_sample_format format;
|
||||||
|
|
||||||
|
@ -338,13 +337,13 @@ cubeb_resampler_create(cubeb_stream * stream,
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case CUBEB_SAMPLE_S16NE:
|
case CUBEB_SAMPLE_S16NE:
|
||||||
return cubeb_resampler_create_internal<short>(
|
return cubeb_resampler_create_internal<short>(stream, input_params,
|
||||||
stream, input_params, output_params, target_rate, callback, user_ptr,
|
output_params, target_rate,
|
||||||
quality, reclock);
|
callback, user_ptr, quality);
|
||||||
case CUBEB_SAMPLE_FLOAT32NE:
|
case CUBEB_SAMPLE_FLOAT32NE:
|
||||||
return cubeb_resampler_create_internal<float>(
|
return cubeb_resampler_create_internal<float>(stream, input_params,
|
||||||
stream, input_params, output_params, target_rate, callback, user_ptr,
|
output_params, target_rate,
|
||||||
quality, reclock);
|
callback, user_ptr, quality);
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
8
externals/cubeb/src/cubeb_resampler.h
vendored
8
externals/cubeb/src/cubeb_resampler.h
vendored
|
@ -21,11 +21,6 @@ typedef enum {
|
||||||
CUBEB_RESAMPLER_QUALITY_DESKTOP
|
CUBEB_RESAMPLER_QUALITY_DESKTOP
|
||||||
} cubeb_resampler_quality;
|
} cubeb_resampler_quality;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE,
|
|
||||||
CUBEB_RESAMPLER_RECLOCK_INPUT
|
|
||||||
} cubeb_resampler_reclock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a resampler to adapt the requested sample rate into something that
|
* Create a resampler to adapt the requested sample rate into something that
|
||||||
* is accepted by the audio backend.
|
* is accepted by the audio backend.
|
||||||
|
@ -49,8 +44,7 @@ cubeb_resampler_create(cubeb_stream * stream,
|
||||||
cubeb_stream_params * input_params,
|
cubeb_stream_params * input_params,
|
||||||
cubeb_stream_params * output_params,
|
cubeb_stream_params * output_params,
|
||||||
unsigned int target_rate, cubeb_data_callback callback,
|
unsigned int target_rate, cubeb_data_callback callback,
|
||||||
void * user_ptr, cubeb_resampler_quality quality,
|
void * user_ptr, cubeb_resampler_quality quality);
|
||||||
cubeb_resampler_reclock reclock);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill the buffer with frames acquired using the data callback. Resampling will
|
* Fill the buffer with frames acquired using the data callback. Resampling will
|
||||||
|
|
|
@ -496,8 +496,7 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
||||||
cubeb_stream_params * output_params,
|
cubeb_stream_params * output_params,
|
||||||
unsigned int target_rate,
|
unsigned int target_rate,
|
||||||
cubeb_data_callback callback, void * user_ptr,
|
cubeb_data_callback callback, void * user_ptr,
|
||||||
cubeb_resampler_quality quality,
|
cubeb_resampler_quality quality)
|
||||||
cubeb_resampler_reclock reclock)
|
|
||||||
{
|
{
|
||||||
std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
|
std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
|
||||||
std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
|
std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
|
||||||
|
|
10
externals/cubeb/src/cubeb_ringbuffer.h
vendored
10
externals/cubeb/src/cubeb_ringbuffer.h
vendored
|
@ -110,8 +110,8 @@ public:
|
||||||
assert_correct_thread(producer_id);
|
assert_correct_thread(producer_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int rd_idx = read_index_.load(std::memory_order_relaxed);
|
||||||
int wr_idx = write_index_.load(std::memory_order_relaxed);
|
int wr_idx = write_index_.load(std::memory_order_relaxed);
|
||||||
int rd_idx = read_index_.load(std::memory_order_acquire);
|
|
||||||
|
|
||||||
if (full_internal(rd_idx, wr_idx)) {
|
if (full_internal(rd_idx, wr_idx)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -154,8 +154,8 @@ public:
|
||||||
assert_correct_thread(consumer_id);
|
assert_correct_thread(consumer_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int rd_idx = read_index_.load(std::memory_order_relaxed);
|
|
||||||
int wr_idx = write_index_.load(std::memory_order_acquire);
|
int wr_idx = write_index_.load(std::memory_order_acquire);
|
||||||
|
int rd_idx = read_index_.load(std::memory_order_relaxed);
|
||||||
|
|
||||||
if (empty_internal(rd_idx, wr_idx)) {
|
if (empty_internal(rd_idx, wr_idx)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -172,7 +172,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
read_index_.store(increment_index(rd_idx, to_read),
|
read_index_.store(increment_index(rd_idx, to_read),
|
||||||
std::memory_order_release);
|
std::memory_order_relaxed);
|
||||||
|
|
||||||
return to_read;
|
return to_read;
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
return available_read_internal(
|
return available_read_internal(
|
||||||
read_index_.load(std::memory_order_relaxed),
|
read_index_.load(std::memory_order_relaxed),
|
||||||
write_index_.load(std::memory_order_acquire));
|
write_index_.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get the number of available elements for consuming.
|
* Get the number of available elements for consuming.
|
||||||
|
@ -205,7 +205,7 @@ public:
|
||||||
assert_correct_thread(producer_id);
|
assert_correct_thread(producer_id);
|
||||||
#endif
|
#endif
|
||||||
return available_write_internal(
|
return available_write_internal(
|
||||||
read_index_.load(std::memory_order_acquire),
|
read_index_.load(std::memory_order_relaxed),
|
||||||
write_index_.load(std::memory_order_relaxed));
|
write_index_.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
|
17
externals/cubeb/src/cubeb_utils_win.h
vendored
17
externals/cubeb/src/cubeb_utils_win.h
vendored
|
@ -11,23 +11,24 @@
|
||||||
#include "cubeb-internal.h"
|
#include "cubeb-internal.h"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
/* This wraps an SRWLock to track the owner in debug mode, adapted from
|
/* This wraps a critical section to track the owner in debug mode, adapted from
|
||||||
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx
|
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx
|
||||||
*/
|
*/
|
||||||
class owned_critical_section {
|
class owned_critical_section {
|
||||||
public:
|
public:
|
||||||
owned_critical_section()
|
owned_critical_section()
|
||||||
: srwlock(SRWLOCK_INIT)
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
,
|
: owner(0)
|
||||||
owner(0)
|
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
InitializeCriticalSection(&critical_section);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~owned_critical_section() { DeleteCriticalSection(&critical_section); }
|
||||||
|
|
||||||
void lock()
|
void lock()
|
||||||
{
|
{
|
||||||
AcquireSRWLockExclusive(&srwlock);
|
EnterCriticalSection(&critical_section);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
|
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
|
||||||
owner = GetCurrentThreadId();
|
owner = GetCurrentThreadId();
|
||||||
|
@ -40,7 +41,7 @@ public:
|
||||||
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
|
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
|
||||||
owner = 0;
|
owner = 0;
|
||||||
#endif
|
#endif
|
||||||
ReleaseSRWLockExclusive(&srwlock);
|
LeaveCriticalSection(&critical_section);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
|
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
|
||||||
|
@ -54,12 +55,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SRWLOCK srwlock;
|
CRITICAL_SECTION critical_section;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
DWORD owner;
|
DWORD owner;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Disallow copy and assignment because SRWLock cannot be copied.
|
// Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
|
||||||
owned_critical_section(const owned_critical_section &);
|
owned_critical_section(const owned_critical_section &);
|
||||||
owned_critical_section & operator=(const owned_critical_section &);
|
owned_critical_section & operator=(const owned_critical_section &);
|
||||||
};
|
};
|
||||||
|
|
593
externals/cubeb/src/cubeb_wasapi.cpp
vendored
593
externals/cubeb/src/cubeb_wasapi.cpp
vendored
File diff suppressed because it is too large
Load diff
11
externals/cubeb/src/cubeb_winmm.c
vendored
11
externals/cubeb/src/cubeb_winmm.c
vendored
|
@ -73,6 +73,17 @@
|
||||||
#define CUBEB_STREAM_MAX 32
|
#define CUBEB_STREAM_MAX 32
|
||||||
#define NBUFS 4
|
#define NBUFS 4
|
||||||
|
|
||||||
|
const GUID KSDATAFORMAT_SUBTYPE_PCM = {
|
||||||
|
0x00000001,
|
||||||
|
0x0000,
|
||||||
|
0x0010,
|
||||||
|
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||||
|
const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
|
||||||
|
0x00000003,
|
||||||
|
0x0000,
|
||||||
|
0x0010,
|
||||||
|
{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||||
|
|
||||||
struct cubeb_stream_item {
|
struct cubeb_stream_item {
|
||||||
SLIST_ENTRY head;
|
SLIST_ENTRY head;
|
||||||
cubeb_stream * stream;
|
cubeb_stream * stream;
|
||||||
|
|
38
externals/cubeb/test/test_callback_ret.cpp
vendored
38
externals/cubeb/test/test_callback_ret.cpp
vendored
|
@ -34,7 +34,6 @@ enum test_direction {
|
||||||
struct user_state_callback_ret {
|
struct user_state_callback_ret {
|
||||||
std::atomic<int> cb_count{ 0 };
|
std::atomic<int> cb_count{ 0 };
|
||||||
std::atomic<int> expected_cb_count{ 0 };
|
std::atomic<int> expected_cb_count{ 0 };
|
||||||
std::atomic<int> error_state{ 0 };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Data callback that always returns 0
|
// Data callback that always returns 0
|
||||||
|
@ -99,30 +98,10 @@ long data_cb_ret_nframes(cubeb_stream * stream, void * user, const void * inputb
|
||||||
return nframes;
|
return nframes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data callback that always returns CUBEB_ERROR
|
void state_cb_ret(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
||||||
long
|
|
||||||
data_cb_ret_error(cubeb_stream * stream, void * user, const void * inputbuffer,
|
|
||||||
void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
user_state_callback_ret * u = (user_state_callback_ret *)user;
|
|
||||||
// If this is the first time the callback has been called set our expected
|
|
||||||
// callback count
|
|
||||||
if (u->cb_count == 0) {
|
|
||||||
u->expected_cb_count = 1;
|
|
||||||
}
|
|
||||||
u->cb_count++;
|
|
||||||
if (nframes < 1) {
|
|
||||||
// This shouldn't happen
|
|
||||||
EXPECT_TRUE(false) << "nframes should not be 0 in data callback!";
|
|
||||||
}
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state)
|
|
||||||
{
|
{
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return;
|
return;
|
||||||
user_state_callback_ret * u = (user_state_callback_ret *)user;
|
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case CUBEB_STATE_STARTED:
|
case CUBEB_STATE_STARTED:
|
||||||
|
@ -131,13 +110,11 @@ void state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state)
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
fprintf(stderr, "stream stopped\n"); break;
|
||||||
case CUBEB_STATE_DRAINED:
|
case CUBEB_STATE_DRAINED:
|
||||||
fprintf(stderr, "stream drained\n"); break;
|
fprintf(stderr, "stream drained\n"); break;
|
||||||
case CUBEB_STATE_ERROR:
|
|
||||||
fprintf(stderr, "stream error\n");
|
|
||||||
u->error_state.fetch_add(1);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unknown stream state %d\n", state);
|
fprintf(stderr, "unknown stream state %d\n", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_test_callback(test_direction direction,
|
void run_test_callback(test_direction direction,
|
||||||
|
@ -206,10 +183,6 @@ void run_test_callback(test_direction direction,
|
||||||
|
|
||||||
ASSERT_EQ(user_state.expected_cb_count, user_state.cb_count) <<
|
ASSERT_EQ(user_state.expected_cb_count, user_state.cb_count) <<
|
||||||
"Callback called unexpected number of times for " << test_desc << "!";
|
"Callback called unexpected number of times for " << test_desc << "!";
|
||||||
// TODO: On some test configurations, the data_callback is never called.
|
|
||||||
if (data_cb == data_cb_ret_error && user_state.cb_count != 0) {
|
|
||||||
ASSERT_EQ(user_state.error_state, 1) << "Callback expected error state";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(cubeb, test_input_callback)
|
TEST(cubeb, test_input_callback)
|
||||||
|
@ -217,8 +190,6 @@ TEST(cubeb, test_input_callback)
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_zero, "input only, return 0");
|
run_test_callback(INPUT_ONLY, data_cb_ret_zero, "input only, return 0");
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_nframes_minus_one, "input only, return nframes - 1");
|
run_test_callback(INPUT_ONLY, data_cb_ret_nframes_minus_one, "input only, return nframes - 1");
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_nframes, "input only, return nframes");
|
run_test_callback(INPUT_ONLY, data_cb_ret_nframes, "input only, return nframes");
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_error,
|
|
||||||
"input only, return CUBEB_ERROR");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(cubeb, test_output_callback)
|
TEST(cubeb, test_output_callback)
|
||||||
|
@ -226,8 +197,6 @@ TEST(cubeb, test_output_callback)
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_zero, "output only, return 0");
|
run_test_callback(OUTPUT_ONLY, data_cb_ret_zero, "output only, return 0");
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes_minus_one, "output only, return nframes - 1");
|
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes_minus_one, "output only, return nframes - 1");
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes, "output only, return nframes");
|
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes, "output only, return nframes");
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_error,
|
|
||||||
"output only, return CUBEB_ERROR");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(cubeb, test_duplex_callback)
|
TEST(cubeb, test_duplex_callback)
|
||||||
|
@ -235,5 +204,4 @@ TEST(cubeb, test_duplex_callback)
|
||||||
run_test_callback(DUPLEX, data_cb_ret_zero, "duplex, return 0");
|
run_test_callback(DUPLEX, data_cb_ret_zero, "duplex, return 0");
|
||||||
run_test_callback(DUPLEX, data_cb_ret_nframes_minus_one, "duplex, return nframes - 1");
|
run_test_callback(DUPLEX, data_cb_ret_nframes_minus_one, "duplex, return nframes - 1");
|
||||||
run_test_callback(DUPLEX, data_cb_ret_nframes, "duplex, return nframes");
|
run_test_callback(DUPLEX, data_cb_ret_nframes, "duplex, return nframes");
|
||||||
run_test_callback(DUPLEX, data_cb_ret_error, "duplex, return CUBEB_ERROR");
|
|
||||||
}
|
}
|
||||||
|
|
54
externals/cubeb/test/test_duplex.cpp
vendored
54
externals/cubeb/test/test_duplex.cpp
vendored
|
@ -137,20 +137,26 @@ void device_collection_changed_callback(cubeb * context, void * user)
|
||||||
" called when opening a stream";
|
" called when opening a stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
TEST(cubeb, duplex_collection_change)
|
||||||
duplex_collection_change_impl(cubeb * ctx)
|
|
||||||
{
|
{
|
||||||
cubeb_stream * stream;
|
cubeb *ctx;
|
||||||
|
cubeb_stream *stream;
|
||||||
cubeb_stream_params input_params;
|
cubeb_stream_params input_params;
|
||||||
cubeb_stream_params output_params;
|
cubeb_stream_params output_params;
|
||||||
int r;
|
int r;
|
||||||
uint32_t latency_frames = 0;
|
uint32_t latency_frames = 0;
|
||||||
|
|
||||||
r = cubeb_register_device_collection_changed(
|
r = common_init(&ctx, "Cubeb duplex example with collection change");
|
||||||
ctx, static_cast<cubeb_device_type>(CUBEB_DEVICE_TYPE_INPUT),
|
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
||||||
device_collection_changed_callback, nullptr);
|
|
||||||
|
r = cubeb_register_device_collection_changed(ctx,
|
||||||
|
static_cast<cubeb_device_type>(CUBEB_DEVICE_TYPE_INPUT),
|
||||||
|
device_collection_changed_callback,
|
||||||
|
nullptr);
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||||
|
|
||||||
|
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
||||||
|
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
||||||
|
|
||||||
/* typical user-case: mono input, stereo output, low latency. */
|
/* typical user-case: mono input, stereo output, low latency. */
|
||||||
input_params.format = STREAM_FORMAT;
|
input_params.format = STREAM_FORMAT;
|
||||||
|
@ -167,43 +173,13 @@ duplex_collection_change_impl(cubeb * ctx)
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex", NULL, &input_params, NULL,
|
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
||||||
&output_params, latency_frames, data_cb_duplex,
|
NULL, &input_params, NULL, &output_params,
|
||||||
state_cb_duplex, nullptr);
|
latency_frames, data_cb_duplex, state_cb_duplex, nullptr);
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
||||||
cubeb_stream_destroy(stream);
|
cubeb_stream_destroy(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(cubeb, duplex_collection_change)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb duplex example with collection change");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
|
|
||||||
ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
duplex_collection_change_impl(ctx);
|
|
||||||
r = cubeb_register_device_collection_changed(
|
|
||||||
ctx, static_cast<cubeb_device_type>(CUBEB_DEVICE_TYPE_INPUT), nullptr,
|
|
||||||
nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, duplex_collection_change_no_unregister)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb duplex example with collection change");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
|
|
||||||
ctx, [](cubeb * p) noexcept { EXPECT_DEATH(cubeb_destroy(p), ""); });
|
|
||||||
|
|
||||||
duplex_collection_change_impl(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
long data_cb_input(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
long data_cb_input(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
||||||
{
|
{
|
||||||
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
|
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
|
||||||
|
|
21
externals/cubeb/test/test_resampler.cpp
vendored
21
externals/cubeb/test/test_resampler.cpp
vendored
|
@ -338,8 +338,7 @@ void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
|
||||||
|
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params, target_rate,
|
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params, target_rate,
|
||||||
data_cb_resampler, (void*)&state, CUBEB_RESAMPLER_QUALITY_VOIP,
|
data_cb_resampler, (void*)&state, CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
|
|
||||||
long latency = cubeb_resampler_latency(resampler);
|
long latency = cubeb_resampler_latency(resampler);
|
||||||
|
|
||||||
|
@ -485,8 +484,8 @@ TEST(cubeb, resampler_output_only_noop)
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
|
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
|
||||||
test_output_only_noop_data_cb, nullptr,
|
test_output_only_noop_data_cb, nullptr,
|
||||||
CUBEB_RESAMPLER_QUALITY_VOIP,
|
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
const long out_frames = 128;
|
const long out_frames = 128;
|
||||||
float out_buffer[out_frames];
|
float out_buffer[out_frames];
|
||||||
long got;
|
long got;
|
||||||
|
@ -524,8 +523,7 @@ TEST(cubeb, resampler_drain)
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
|
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
|
||||||
test_drain_data_cb, &cb_count,
|
test_drain_data_cb, &cb_count,
|
||||||
CUBEB_RESAMPLER_QUALITY_VOIP,
|
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
|
|
||||||
const long out_frames = 128;
|
const long out_frames = 128;
|
||||||
float out_buffer[out_frames];
|
float out_buffer[out_frames];
|
||||||
|
@ -574,8 +572,7 @@ TEST(cubeb, resampler_passthrough_output_only)
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params,
|
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params,
|
||||||
target_rate, cb_passthrough_resampler_output, nullptr,
|
target_rate, cb_passthrough_resampler_output, nullptr,
|
||||||
CUBEB_RESAMPLER_QUALITY_VOIP,
|
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
|
|
||||||
float output_buffer[output_channels * 256];
|
float output_buffer[output_channels * 256];
|
||||||
|
|
||||||
|
@ -619,8 +616,7 @@ TEST(cubeb, resampler_passthrough_input_only)
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, nullptr,
|
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, nullptr,
|
||||||
target_rate, cb_passthrough_resampler_input, nullptr,
|
target_rate, cb_passthrough_resampler_input, nullptr,
|
||||||
CUBEB_RESAMPLER_QUALITY_VOIP,
|
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
|
|
||||||
float input_buffer[input_channels * 256];
|
float input_buffer[input_channels * 256];
|
||||||
|
|
||||||
|
@ -741,8 +737,7 @@ TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
|
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
|
||||||
target_rate, cb_passthrough_resampler_duplex, &c,
|
target_rate, cb_passthrough_resampler_duplex, &c,
|
||||||
CUBEB_RESAMPLER_QUALITY_VOIP,
|
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
CUBEB_RESAMPLER_RECLOCK_NONE);
|
|
||||||
|
|
||||||
const long BUF_BASE_SIZE = 256;
|
const long BUF_BASE_SIZE = 256;
|
||||||
float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
|
float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
|
||||||
|
@ -825,7 +820,7 @@ TEST(cubeb, resampler_drift_drop_data)
|
||||||
cubeb_resampler * resampler =
|
cubeb_resampler * resampler =
|
||||||
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
|
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
|
||||||
target_rate, cb_passthrough_resampler_duplex, &c,
|
target_rate, cb_passthrough_resampler_duplex, &c,
|
||||||
CUBEB_RESAMPLER_QUALITY_VOIP, CUBEB_RESAMPLER_RECLOCK_NONE);
|
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||||
|
|
||||||
const long BUF_BASE_SIZE = 256;
|
const long BUF_BASE_SIZE = 256;
|
||||||
|
|
||||||
|
|
87
externals/cubeb/tools/cubeb-test.cpp
vendored
87
externals/cubeb/tools/cubeb-test.cpp
vendored
|
@ -7,7 +7,6 @@
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <objbase.h> // Used by CoInitialize()
|
#include <objbase.h> // Used by CoInitialize()
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,32 +35,6 @@ static const char* state_to_string(cubeb_state state) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* device_type_to_string(cubeb_device_type type) {
|
|
||||||
switch (type) {
|
|
||||||
case CUBEB_DEVICE_TYPE_INPUT:
|
|
||||||
return "input";
|
|
||||||
case CUBEB_DEVICE_TYPE_OUTPUT:
|
|
||||||
return "output";
|
|
||||||
case CUBEB_DEVICE_TYPE_UNKNOWN:
|
|
||||||
return "unknown";
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* device_state_to_string(cubeb_device_state state) {
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_DEVICE_STATE_DISABLED:
|
|
||||||
return "disabled";
|
|
||||||
case CUBEB_DEVICE_STATE_UNPLUGGED:
|
|
||||||
return "unplugged";
|
|
||||||
case CUBEB_DEVICE_STATE_ENABLED:
|
|
||||||
return "enabled";
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_log(const char* msg, ...) {
|
void print_log(const char* msg, ...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, msg);
|
va_start(args, msg);
|
||||||
|
@ -75,7 +48,6 @@ public:
|
||||||
~cubeb_client() {}
|
~cubeb_client() {}
|
||||||
|
|
||||||
bool init(char const * backend_name = nullptr);
|
bool init(char const * backend_name = nullptr);
|
||||||
cubeb_devid select_device(cubeb_device_type type) const;
|
|
||||||
bool init_stream();
|
bool init_stream();
|
||||||
bool start_stream();
|
bool start_stream();
|
||||||
bool stop_stream();
|
bool stop_stream();
|
||||||
|
@ -98,10 +70,7 @@ public:
|
||||||
bool unregister_device_collection_changed(cubeb_device_type devtype) const;
|
bool unregister_device_collection_changed(cubeb_device_type devtype) const;
|
||||||
|
|
||||||
cubeb_stream_params output_params = {};
|
cubeb_stream_params output_params = {};
|
||||||
cubeb_devid output_device = nullptr;
|
|
||||||
|
|
||||||
cubeb_stream_params input_params = {};
|
cubeb_stream_params input_params = {};
|
||||||
cubeb_devid input_device = nullptr;
|
|
||||||
|
|
||||||
void force_drain() { _force_drain = true; }
|
void force_drain() { _force_drain = true; }
|
||||||
|
|
||||||
|
@ -112,6 +81,8 @@ private:
|
||||||
cubeb* context = nullptr;
|
cubeb* context = nullptr;
|
||||||
|
|
||||||
cubeb_stream* stream = nullptr;
|
cubeb_stream* stream = nullptr;
|
||||||
|
cubeb_devid output_device = nullptr;
|
||||||
|
cubeb_devid input_device = nullptr;
|
||||||
|
|
||||||
/* Accessed only from client and audio thread. */
|
/* Accessed only from client and audio thread. */
|
||||||
std::atomic<uint32_t> _rate = {0};
|
std::atomic<uint32_t> _rate = {0};
|
||||||
|
@ -521,56 +492,6 @@ bool choose_action(cubeb_client& cl, operation_data * op, int c) {
|
||||||
return true; // Loop up
|
return true; // Loop up
|
||||||
}
|
}
|
||||||
|
|
||||||
cubeb_devid cubeb_client::select_device(cubeb_device_type type) const
|
|
||||||
{
|
|
||||||
assert(type == CUBEB_DEVICE_TYPE_INPUT || type == CUBEB_DEVICE_TYPE_OUTPUT);
|
|
||||||
|
|
||||||
cubeb_device_collection collection;
|
|
||||||
if (cubeb_enumerate_devices(context, type, &collection) ==
|
|
||||||
CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Not support %s device selection. Force to use default device\n",
|
|
||||||
device_type_to_string(type));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(collection.count);
|
|
||||||
fprintf(stderr, "Found %zu %s devices. Choose one:\n", collection.count,
|
|
||||||
device_type_to_string(type));
|
|
||||||
|
|
||||||
std::vector<cubeb_devid> devices;
|
|
||||||
devices.emplace_back(nullptr);
|
|
||||||
fprintf(stderr, "# 0\n\tname: system default device\n");
|
|
||||||
for (size_t i = 0; i < collection.count; i++) {
|
|
||||||
assert(collection.device[i].type == type);
|
|
||||||
fprintf(stderr,
|
|
||||||
"# %zu %s\n"
|
|
||||||
"\tname: %s\n"
|
|
||||||
"\tdevice id: %s\n"
|
|
||||||
"\tmax channels: %u\n"
|
|
||||||
"\tstate: %s\n",
|
|
||||||
devices.size(),
|
|
||||||
collection.device[i].preferred ? " (PREFERRED)" : "",
|
|
||||||
collection.device[i].friendly_name, collection.device[i].device_id,
|
|
||||||
collection.device[i].max_channels,
|
|
||||||
device_state_to_string(collection.device[i].state));
|
|
||||||
devices.emplace_back(collection.device[i].devid);
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_device_collection_destroy(context, &collection);
|
|
||||||
|
|
||||||
size_t number;
|
|
||||||
std::cout << "Enter device number: ";
|
|
||||||
std::cin >> number;
|
|
||||||
while (!std::cin || number >= devices.size()) {
|
|
||||||
std::cin.clear();
|
|
||||||
std::cin.ignore(100, '\n');
|
|
||||||
std::cout << "Error: Please enter a valid numeric input. Enter again: ";
|
|
||||||
std::cin >> number;
|
|
||||||
}
|
|
||||||
return devices[number];
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
CoInitialize(nullptr);
|
CoInitialize(nullptr);
|
||||||
|
@ -602,7 +523,7 @@ int main(int argc, char* argv[]) {
|
||||||
|
|
||||||
bool res = false;
|
bool res = false;
|
||||||
cubeb_client cl;
|
cubeb_client cl;
|
||||||
cl.activate_log(CUBEB_LOG_NORMAL);
|
cl.activate_log(CUBEB_LOG_DISABLED);
|
||||||
fprintf(stderr, "Log level is DISABLED\n");
|
fprintf(stderr, "Log level is DISABLED\n");
|
||||||
cl.init(/* default backend */);
|
cl.init(/* default backend */);
|
||||||
|
|
||||||
|
@ -619,12 +540,10 @@ int main(int argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (op.pm == PLAYBACK || op.pm == DUPLEX || op.pm == LATENCY_TESTING) {
|
if (op.pm == PLAYBACK || op.pm == DUPLEX || op.pm == LATENCY_TESTING) {
|
||||||
cl.output_device = cl.select_device(CUBEB_DEVICE_TYPE_OUTPUT);
|
|
||||||
cl.output_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_OUTPUT_CHANNELS,
|
cl.output_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_OUTPUT_CHANNELS,
|
||||||
CUBEB_LAYOUT_STEREO, CUBEB_STREAM_PREF_NONE};
|
CUBEB_LAYOUT_STEREO, CUBEB_STREAM_PREF_NONE};
|
||||||
}
|
}
|
||||||
if (op.pm == RECORD || op.pm == DUPLEX || op.pm == LATENCY_TESTING) {
|
if (op.pm == RECORD || op.pm == DUPLEX || op.pm == LATENCY_TESTING) {
|
||||||
cl.input_device = cl.select_device(CUBEB_DEVICE_TYPE_INPUT);
|
|
||||||
cl.input_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_INPUT_CHANNELS, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE};
|
cl.input_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_INPUT_CHANNELS, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE};
|
||||||
}
|
}
|
||||||
if (op.pm == LATENCY_TESTING) {
|
if (op.pm == LATENCY_TESTING) {
|
||||||
|
|
|
@ -28,7 +28,7 @@ enum class InputType {
|
||||||
Color,
|
Color,
|
||||||
Vibration,
|
Vibration,
|
||||||
Nfc,
|
Nfc,
|
||||||
Ir,
|
IrSensor,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Internal battery charge level
|
// Internal battery charge level
|
||||||
|
@ -53,6 +53,15 @@ enum class PollingMode {
|
||||||
IR,
|
IR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CameraFormat {
|
||||||
|
Size320x240,
|
||||||
|
Size160x120,
|
||||||
|
Size80x60,
|
||||||
|
Size40x30,
|
||||||
|
Size20x15,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
// Vibration reply from the controller
|
// Vibration reply from the controller
|
||||||
enum class VibrationError {
|
enum class VibrationError {
|
||||||
None,
|
None,
|
||||||
|
@ -68,6 +77,13 @@ enum class PollingError {
|
||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ir camera reply from the controller
|
||||||
|
enum class CameraError {
|
||||||
|
None,
|
||||||
|
NotSupported,
|
||||||
|
Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
// Hint for amplification curve to be used
|
// Hint for amplification curve to be used
|
||||||
enum class VibrationAmplificationType {
|
enum class VibrationAmplificationType {
|
||||||
Linear,
|
Linear,
|
||||||
|
@ -176,6 +192,12 @@ struct LedStatus {
|
||||||
bool led_4{};
|
bool led_4{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Raw data fom camera
|
||||||
|
struct CameraStatus {
|
||||||
|
CameraFormat format{CameraFormat::None};
|
||||||
|
std::vector<u8> data{};
|
||||||
|
};
|
||||||
|
|
||||||
// List of buttons to be passed to Qt that can be translated
|
// List of buttons to be passed to Qt that can be translated
|
||||||
enum class ButtonNames {
|
enum class ButtonNames {
|
||||||
Undefined,
|
Undefined,
|
||||||
|
@ -233,6 +255,7 @@ struct CallbackStatus {
|
||||||
BodyColorStatus color_status{};
|
BodyColorStatus color_status{};
|
||||||
BatteryStatus battery_status{};
|
BatteryStatus battery_status{};
|
||||||
VibrationStatus vibration_status{};
|
VibrationStatus vibration_status{};
|
||||||
|
CameraStatus camera_status{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Triggered once every input change
|
// Triggered once every input change
|
||||||
|
@ -281,6 +304,10 @@ public:
|
||||||
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
|
virtual PollingError SetPollingMode([[maybe_unused]] PollingMode polling_mode) {
|
||||||
return PollingError::NotSupported;
|
return PollingError::NotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CameraError SetCameraFormat([[maybe_unused]] CameraFormat camera_format) {
|
||||||
|
return CameraError::NotSupported;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An abstract class template for a factory that can create input devices.
|
/// An abstract class template for a factory that can create input devices.
|
||||||
|
|
|
@ -106,7 +106,7 @@ struct ResolutionScalingInfo {
|
||||||
* configurations. Specifying a default value and label is required. A minimum and maximum range can
|
* configurations. Specifying a default value and label is required. A minimum and maximum range can
|
||||||
* be specified for sanitization.
|
* be specified for sanitization.
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged = false>
|
||||||
class Setting {
|
class Setting {
|
||||||
protected:
|
protected:
|
||||||
Setting() = default;
|
Setting() = default;
|
||||||
|
@ -126,8 +126,8 @@ public:
|
||||||
* @param default_val Intial value of the setting, and default value of the setting
|
* @param default_val Intial value of the setting, and default value of the setting
|
||||||
* @param name Label for the setting
|
* @param name Label for the setting
|
||||||
*/
|
*/
|
||||||
explicit Setting(const Type& default_val, const std::string& name)
|
explicit Setting(const Type& default_val, const std::string& name) requires(!ranged)
|
||||||
: value{default_val}, default_value{default_val}, ranged{false}, label{name} {}
|
: value{default_val}, default_value{default_val}, label{name} {}
|
||||||
virtual ~Setting() = default;
|
virtual ~Setting() = default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,9 +139,9 @@ public:
|
||||||
* @param name Label for the setting
|
* @param name Label for the setting
|
||||||
*/
|
*/
|
||||||
explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
|
explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val,
|
||||||
const std::string& name)
|
const std::string& name) requires(ranged)
|
||||||
: value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val},
|
: value{default_val},
|
||||||
ranged{true}, label{name} {}
|
default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a reference to the setting's value.
|
* Returns a reference to the setting's value.
|
||||||
|
@ -158,7 +158,7 @@ public:
|
||||||
* @param val The desired value
|
* @param val The desired value
|
||||||
*/
|
*/
|
||||||
virtual void SetValue(const Type& val) {
|
virtual void SetValue(const Type& val) {
|
||||||
Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val};
|
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
|
||||||
std::swap(value, temp);
|
std::swap(value, temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ public:
|
||||||
* @returns A reference to the setting
|
* @returns A reference to the setting
|
||||||
*/
|
*/
|
||||||
virtual const Type& operator=(const Type& val) {
|
virtual const Type& operator=(const Type& val) {
|
||||||
Type temp{(ranged) ? std::clamp(val, minimum, maximum) : val};
|
Type temp{ranged ? std::clamp(val, minimum, maximum) : val};
|
||||||
std::swap(value, temp);
|
std::swap(value, temp);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,6 @@ protected:
|
||||||
const Type default_value{}; ///< The default value
|
const Type default_value{}; ///< The default value
|
||||||
const Type maximum{}; ///< Maximum allowed value of the setting
|
const Type maximum{}; ///< Maximum allowed value of the setting
|
||||||
const Type minimum{}; ///< Minimum allowed value of the setting
|
const Type minimum{}; ///< Minimum allowed value of the setting
|
||||||
const bool ranged; ///< The setting has sanitization ranges
|
|
||||||
const std::string label{}; ///< The setting's label
|
const std::string label{}; ///< The setting's label
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,8 +218,8 @@ protected:
|
||||||
*
|
*
|
||||||
* By default, the global setting is used.
|
* By default, the global setting is used.
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged = false>
|
||||||
class SwitchableSetting : virtual public Setting<Type> {
|
class SwitchableSetting : virtual public Setting<Type, ranged> {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Sets a default value, label, and setting value.
|
* Sets a default value, label, and setting value.
|
||||||
|
@ -228,7 +227,7 @@ public:
|
||||||
* @param default_val Intial value of the setting, and default value of the setting
|
* @param default_val Intial value of the setting, and default value of the setting
|
||||||
* @param name Label for the setting
|
* @param name Label for the setting
|
||||||
*/
|
*/
|
||||||
explicit SwitchableSetting(const Type& default_val, const std::string& name)
|
explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged)
|
||||||
: Setting<Type>{default_val, name} {}
|
: Setting<Type>{default_val, name} {}
|
||||||
virtual ~SwitchableSetting() = default;
|
virtual ~SwitchableSetting() = default;
|
||||||
|
|
||||||
|
@ -241,8 +240,8 @@ public:
|
||||||
* @param name Label for the setting
|
* @param name Label for the setting
|
||||||
*/
|
*/
|
||||||
explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
|
explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val,
|
||||||
const std::string& name)
|
const std::string& name) requires(ranged)
|
||||||
: Setting<Type>{default_val, min_val, max_val, name} {}
|
: Setting<Type, true>{default_val, min_val, max_val, name} {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells this setting to represent either the global or custom setting when other member
|
* Tells this setting to represent either the global or custom setting when other member
|
||||||
|
@ -290,7 +289,7 @@ public:
|
||||||
* @param val The new value
|
* @param val The new value
|
||||||
*/
|
*/
|
||||||
void SetValue(const Type& val) override {
|
void SetValue(const Type& val) override {
|
||||||
Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val};
|
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
|
||||||
if (use_global) {
|
if (use_global) {
|
||||||
std::swap(this->value, temp);
|
std::swap(this->value, temp);
|
||||||
} else {
|
} else {
|
||||||
|
@ -306,7 +305,7 @@ public:
|
||||||
* @returns A reference to the current setting value
|
* @returns A reference to the current setting value
|
||||||
*/
|
*/
|
||||||
const Type& operator=(const Type& val) override {
|
const Type& operator=(const Type& val) override {
|
||||||
Type temp{(this->ranged) ? std::clamp(val, this->minimum, this->maximum) : val};
|
Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val};
|
||||||
if (use_global) {
|
if (use_global) {
|
||||||
std::swap(this->value, temp);
|
std::swap(this->value, temp);
|
||||||
return this->value;
|
return this->value;
|
||||||
|
@ -375,7 +374,7 @@ struct Values {
|
||||||
Setting<std::string> audio_output_device_id{"auto", "output_device"};
|
Setting<std::string> audio_output_device_id{"auto", "output_device"};
|
||||||
Setting<std::string> audio_input_device_id{"auto", "input_device"};
|
Setting<std::string> audio_input_device_id{"auto", "input_device"};
|
||||||
Setting<bool> audio_muted{false, "audio_muted"};
|
Setting<bool> audio_muted{false, "audio_muted"};
|
||||||
SwitchableSetting<u8> volume{100, 0, 100, "volume"};
|
SwitchableSetting<u8, true> volume{100, 0, 100, "volume"};
|
||||||
Setting<bool> dump_audio_commands{false, "dump_audio_commands"};
|
Setting<bool> dump_audio_commands{false, "dump_audio_commands"};
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
|
@ -383,8 +382,8 @@ struct Values {
|
||||||
SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
|
SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
|
||||||
|
|
||||||
// Cpu
|
// Cpu
|
||||||
SwitchableSetting<CPUAccuracy> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
|
SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
|
||||||
CPUAccuracy::Paranoid, "cpu_accuracy"};
|
CPUAccuracy::Paranoid, "cpu_accuracy"};
|
||||||
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
|
// TODO: remove cpu_accuracy_first_time, migration setting added 8 July 2021
|
||||||
Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
|
Setting<bool> cpu_accuracy_first_time{true, "cpu_accuracy_first_time"};
|
||||||
Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
|
Setting<bool> cpu_debug_mode{false, "cpu_debug_mode"};
|
||||||
|
@ -411,7 +410,7 @@ struct Values {
|
||||||
true, "cpuopt_unsafe_ignore_global_monitor"};
|
true, "cpuopt_unsafe_ignore_global_monitor"};
|
||||||
|
|
||||||
// Renderer
|
// Renderer
|
||||||
SwitchableSetting<RendererBackend> renderer_backend{
|
SwitchableSetting<RendererBackend, true> renderer_backend{
|
||||||
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"};
|
||||||
Setting<bool> renderer_debug{false, "debug"};
|
Setting<bool> renderer_debug{false, "debug"};
|
||||||
Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
Setting<bool> renderer_shader_feedback{false, "shader_feedback"};
|
||||||
|
@ -425,26 +424,26 @@ struct Values {
|
||||||
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
|
SwitchableSetting<AntiAliasing> anti_aliasing{AntiAliasing::None, "anti_aliasing"};
|
||||||
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
// *nix platforms may have issues with the borderless windowed fullscreen mode.
|
||||||
// Default to exclusive fullscreen on these platforms for now.
|
// Default to exclusive fullscreen on these platforms for now.
|
||||||
SwitchableSetting<FullscreenMode> fullscreen_mode{
|
SwitchableSetting<FullscreenMode, true> fullscreen_mode{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FullscreenMode::Borderless,
|
FullscreenMode::Borderless,
|
||||||
#else
|
#else
|
||||||
FullscreenMode::Exclusive,
|
FullscreenMode::Exclusive,
|
||||||
#endif
|
#endif
|
||||||
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
|
FullscreenMode::Borderless, FullscreenMode::Exclusive, "fullscreen_mode"};
|
||||||
SwitchableSetting<int> aspect_ratio{0, 0, 3, "aspect_ratio"};
|
SwitchableSetting<int, true> aspect_ratio{0, 0, 3, "aspect_ratio"};
|
||||||
SwitchableSetting<int> max_anisotropy{0, 0, 5, "max_anisotropy"};
|
SwitchableSetting<int, true> max_anisotropy{0, 0, 5, "max_anisotropy"};
|
||||||
SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
|
SwitchableSetting<bool> use_speed_limit{true, "use_speed_limit"};
|
||||||
SwitchableSetting<u16> speed_limit{100, 0, 9999, "speed_limit"};
|
SwitchableSetting<u16, true> speed_limit{100, 0, 9999, "speed_limit"};
|
||||||
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
SwitchableSetting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"};
|
||||||
SwitchableSetting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
|
SwitchableSetting<GPUAccuracy, true> gpu_accuracy{GPUAccuracy::High, GPUAccuracy::Normal,
|
||||||
GPUAccuracy::Extreme, "gpu_accuracy"};
|
GPUAccuracy::Extreme, "gpu_accuracy"};
|
||||||
SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
SwitchableSetting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"};
|
||||||
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
|
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
|
||||||
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
|
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
|
||||||
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
|
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
|
||||||
SwitchableSetting<ShaderBackend> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
|
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLASM, ShaderBackend::GLSL,
|
||||||
ShaderBackend::SPIRV, "shader_backend"};
|
ShaderBackend::SPIRV, "shader_backend"};
|
||||||
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
|
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
|
||||||
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
|
SwitchableSetting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
|
||||||
|
|
||||||
|
@ -460,10 +459,10 @@ struct Values {
|
||||||
s64 custom_rtc_differential;
|
s64 custom_rtc_differential;
|
||||||
|
|
||||||
Setting<s32> current_user{0, "current_user"};
|
Setting<s32> current_user{0, "current_user"};
|
||||||
SwitchableSetting<s32> language_index{1, 0, 17, "language_index"};
|
SwitchableSetting<s32, true> language_index{1, 0, 17, "language_index"};
|
||||||
SwitchableSetting<s32> region_index{1, 0, 6, "region_index"};
|
SwitchableSetting<s32, true> region_index{1, 0, 6, "region_index"};
|
||||||
SwitchableSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
|
SwitchableSetting<s32, true> time_zone_index{0, 0, 45, "time_zone_index"};
|
||||||
SwitchableSetting<s32> sound_index{1, 0, 2, "sound_index"};
|
SwitchableSetting<s32, true> sound_index{1, 0, 2, "sound_index"};
|
||||||
|
|
||||||
// Controls
|
// Controls
|
||||||
InputSetting<std::array<PlayerInput, 10>> players;
|
InputSetting<std::array<PlayerInput, 10>> players;
|
||||||
|
@ -485,7 +484,7 @@ struct Values {
|
||||||
Setting<bool> tas_loop{false, "tas_loop"};
|
Setting<bool> tas_loop{false, "tas_loop"};
|
||||||
|
|
||||||
Setting<bool> mouse_panning{false, "mouse_panning"};
|
Setting<bool> mouse_panning{false, "mouse_panning"};
|
||||||
Setting<u8> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
|
Setting<u8, true> mouse_panning_sensitivity{10, 1, 100, "mouse_panning_sensitivity"};
|
||||||
Setting<bool> mouse_enabled{false, "mouse_enabled"};
|
Setting<bool> mouse_enabled{false, "mouse_enabled"};
|
||||||
|
|
||||||
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
|
Setting<bool> emulate_analog_keyboard{false, "emulate_analog_keyboard"};
|
||||||
|
@ -504,6 +503,9 @@ struct Values {
|
||||||
Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
|
Setting<bool> enable_ring_controller{true, "enable_ring_controller"};
|
||||||
RingconRaw ringcon_analogs;
|
RingconRaw ringcon_analogs;
|
||||||
|
|
||||||
|
Setting<bool> enable_ir_sensor{false, "enable_ir_sensor"};
|
||||||
|
Setting<std::string> ir_sensor_device{"auto", "ir_sensor_device"};
|
||||||
|
|
||||||
// Data Storage
|
// Data Storage
|
||||||
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
|
Setting<bool> use_virtual_sd{true, "use_virtual_sd"};
|
||||||
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
|
Setting<bool> gamecard_inserted{false, "gamecard_inserted"};
|
||||||
|
|
|
@ -156,6 +156,7 @@ add_library(core STATIC
|
||||||
hid/input_converter.h
|
hid/input_converter.h
|
||||||
hid/input_interpreter.cpp
|
hid/input_interpreter.cpp
|
||||||
hid/input_interpreter.h
|
hid/input_interpreter.h
|
||||||
|
hid/irs_types.h
|
||||||
hid/motion_input.cpp
|
hid/motion_input.cpp
|
||||||
hid/motion_input.h
|
hid/motion_input.h
|
||||||
hle/api_version.h
|
hle/api_version.h
|
||||||
|
@ -475,6 +476,20 @@ add_library(core STATIC
|
||||||
hle/service/hid/hidbus/starlink.h
|
hle/service/hid/hidbus/starlink.h
|
||||||
hle/service/hid/hidbus/stubbed.cpp
|
hle/service/hid/hidbus/stubbed.cpp
|
||||||
hle/service/hid/hidbus/stubbed.h
|
hle/service/hid/hidbus/stubbed.h
|
||||||
|
hle/service/hid/irsensor/clustering_processor.cpp
|
||||||
|
hle/service/hid/irsensor/clustering_processor.h
|
||||||
|
hle/service/hid/irsensor/image_transfer_processor.cpp
|
||||||
|
hle/service/hid/irsensor/image_transfer_processor.h
|
||||||
|
hle/service/hid/irsensor/ir_led_processor.cpp
|
||||||
|
hle/service/hid/irsensor/ir_led_processor.h
|
||||||
|
hle/service/hid/irsensor/moment_processor.cpp
|
||||||
|
hle/service/hid/irsensor/moment_processor.h
|
||||||
|
hle/service/hid/irsensor/pointing_processor.cpp
|
||||||
|
hle/service/hid/irsensor/pointing_processor.h
|
||||||
|
hle/service/hid/irsensor/processor_base.cpp
|
||||||
|
hle/service/hid/irsensor/processor_base.h
|
||||||
|
hle/service/hid/irsensor/tera_plugin_processor.cpp
|
||||||
|
hle/service/hid/irsensor/tera_plugin_processor.h
|
||||||
hle/service/jit/jit_context.cpp
|
hle/service/jit/jit_context.cpp
|
||||||
hle/service/jit/jit_context.h
|
hle/service/jit/jit_context.h
|
||||||
hle/service/jit/jit.cpp
|
hle/service/jit/jit.cpp
|
||||||
|
|
|
@ -126,10 +126,14 @@ void EmulatedController::LoadDevices() {
|
||||||
battery_params[LeftIndex].Set("battery", true);
|
battery_params[LeftIndex].Set("battery", true);
|
||||||
battery_params[RightIndex].Set("battery", true);
|
battery_params[RightIndex].Set("battery", true);
|
||||||
|
|
||||||
|
camera_params = Common::ParamPackage{"engine:camera,camera:1"};
|
||||||
|
|
||||||
output_params[LeftIndex] = left_joycon;
|
output_params[LeftIndex] = left_joycon;
|
||||||
output_params[RightIndex] = right_joycon;
|
output_params[RightIndex] = right_joycon;
|
||||||
|
output_params[2] = camera_params;
|
||||||
output_params[LeftIndex].Set("output", true);
|
output_params[LeftIndex].Set("output", true);
|
||||||
output_params[RightIndex].Set("output", true);
|
output_params[RightIndex].Set("output", true);
|
||||||
|
output_params[2].Set("output", true);
|
||||||
|
|
||||||
LoadTASParams();
|
LoadTASParams();
|
||||||
|
|
||||||
|
@ -146,6 +150,7 @@ void EmulatedController::LoadDevices() {
|
||||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||||
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
std::transform(battery_params.begin(), battery_params.end(), battery_devices.begin(),
|
||||||
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
Common::Input::CreateDevice<Common::Input::InputDevice>);
|
||||||
|
camera_devices = Common::Input::CreateDevice<Common::Input::InputDevice>(camera_params);
|
||||||
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
std::transform(output_params.begin(), output_params.end(), output_devices.begin(),
|
||||||
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
Common::Input::CreateDevice<Common::Input::OutputDevice>);
|
||||||
|
|
||||||
|
@ -267,6 +272,14 @@ void EmulatedController::ReloadInput() {
|
||||||
motion_devices[index]->ForceUpdate();
|
motion_devices[index]->ForceUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (camera_devices) {
|
||||||
|
camera_devices->SetCallback({
|
||||||
|
.on_change =
|
||||||
|
[this](const Common::Input::CallbackStatus& callback) { SetCamera(callback); },
|
||||||
|
});
|
||||||
|
camera_devices->ForceUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
// Use a common UUID for TAS
|
// Use a common UUID for TAS
|
||||||
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
static constexpr Common::UUID TAS_UUID = Common::UUID{
|
||||||
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xA5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}};
|
||||||
|
@ -851,6 +864,25 @@ void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callbac
|
||||||
TriggerOnChange(ControllerTriggerType::Battery, true);
|
TriggerOnChange(ControllerTriggerType::Battery, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback) {
|
||||||
|
std::unique_lock lock{mutex};
|
||||||
|
controller.camera_values = TransformToCamera(callback);
|
||||||
|
|
||||||
|
if (is_configuring) {
|
||||||
|
lock.unlock();
|
||||||
|
TriggerOnChange(ControllerTriggerType::IrSensor, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.camera_state.sample++;
|
||||||
|
controller.camera_state.format =
|
||||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
|
||||||
|
controller.camera_state.data = controller.camera_values.data;
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
TriggerOnChange(ControllerTriggerType::IrSensor, true);
|
||||||
|
}
|
||||||
|
|
||||||
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
|
bool EmulatedController::SetVibration(std::size_t device_index, VibrationValue vibration) {
|
||||||
if (device_index >= output_devices.size()) {
|
if (device_index >= output_devices.size()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -928,6 +960,23 @@ bool EmulatedController::SetPollingMode(Common::Input::PollingMode polling_mode)
|
||||||
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
return output_device->SetPollingMode(polling_mode) == Common::Input::PollingError::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EmulatedController::SetCameraFormat(
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat camera_format) {
|
||||||
|
LOG_INFO(Service_HID, "Set camera format {}", camera_format);
|
||||||
|
|
||||||
|
auto& right_output_device = output_devices[static_cast<std::size_t>(DeviceIndex::Right)];
|
||||||
|
auto& camera_output_device = output_devices[2];
|
||||||
|
|
||||||
|
if (right_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
|
||||||
|
camera_format)) == Common::Input::CameraError::None) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to Qt camera if native device doesn't have support
|
||||||
|
return camera_output_device->SetCameraFormat(static_cast<Common::Input::CameraFormat>(
|
||||||
|
camera_format)) == Common::Input::CameraError::None;
|
||||||
|
}
|
||||||
|
|
||||||
void EmulatedController::SetLedPattern() {
|
void EmulatedController::SetLedPattern() {
|
||||||
for (auto& device : output_devices) {
|
for (auto& device : output_devices) {
|
||||||
if (!device) {
|
if (!device) {
|
||||||
|
@ -1163,6 +1212,11 @@ BatteryValues EmulatedController::GetBatteryValues() const {
|
||||||
return controller.battery_values;
|
return controller.battery_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CameraValues EmulatedController::GetCameraValues() const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return controller.camera_values;
|
||||||
|
}
|
||||||
|
|
||||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
if (is_configuring) {
|
if (is_configuring) {
|
||||||
|
@ -1251,6 +1305,11 @@ BatteryLevelState EmulatedController::GetBattery() const {
|
||||||
return controller.battery_state;
|
return controller.battery_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CameraState& EmulatedController::GetCamera() const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
return controller.camera_state;
|
||||||
|
}
|
||||||
|
|
||||||
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
void EmulatedController::TriggerOnChange(ControllerTriggerType type, bool is_npad_service_update) {
|
||||||
std::scoped_lock lock{callback_mutex};
|
std::scoped_lock lock{callback_mutex};
|
||||||
for (const auto& poller_pair : callback_list) {
|
for (const auto& poller_pair : callback_list) {
|
||||||
|
|
|
@ -15,10 +15,12 @@
|
||||||
#include "common/settings.h"
|
#include "common/settings.h"
|
||||||
#include "common/vector_math.h"
|
#include "common/vector_math.h"
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
#include "core/hid/motion_input.h"
|
#include "core/hid/motion_input.h"
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core::HID {
|
||||||
const std::size_t max_emulated_controllers = 2;
|
const std::size_t max_emulated_controllers = 2;
|
||||||
|
const std::size_t output_devices = 3;
|
||||||
struct ControllerMotionInfo {
|
struct ControllerMotionInfo {
|
||||||
Common::Input::MotionStatus raw_status{};
|
Common::Input::MotionStatus raw_status{};
|
||||||
MotionInput emulated{};
|
MotionInput emulated{};
|
||||||
|
@ -34,15 +36,16 @@ using TriggerDevices =
|
||||||
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
|
std::array<std::unique_ptr<Common::Input::InputDevice>, Settings::NativeTrigger::NumTriggers>;
|
||||||
using BatteryDevices =
|
using BatteryDevices =
|
||||||
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
|
std::array<std::unique_ptr<Common::Input::InputDevice>, max_emulated_controllers>;
|
||||||
using OutputDevices =
|
using CameraDevices = std::unique_ptr<Common::Input::InputDevice>;
|
||||||
std::array<std::unique_ptr<Common::Input::OutputDevice>, max_emulated_controllers>;
|
using OutputDevices = std::array<std::unique_ptr<Common::Input::OutputDevice>, output_devices>;
|
||||||
|
|
||||||
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
|
using ButtonParams = std::array<Common::ParamPackage, Settings::NativeButton::NumButtons>;
|
||||||
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
|
using StickParams = std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs>;
|
||||||
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
|
using ControllerMotionParams = std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions>;
|
||||||
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
|
using TriggerParams = std::array<Common::ParamPackage, Settings::NativeTrigger::NumTriggers>;
|
||||||
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
|
using BatteryParams = std::array<Common::ParamPackage, max_emulated_controllers>;
|
||||||
using OutputParams = std::array<Common::ParamPackage, max_emulated_controllers>;
|
using CameraParams = Common::ParamPackage;
|
||||||
|
using OutputParams = std::array<Common::ParamPackage, output_devices>;
|
||||||
|
|
||||||
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
|
using ButtonValues = std::array<Common::Input::ButtonStatus, Settings::NativeButton::NumButtons>;
|
||||||
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
|
using SticksValues = std::array<Common::Input::StickStatus, Settings::NativeAnalog::NumAnalogs>;
|
||||||
|
@ -51,6 +54,7 @@ using TriggerValues =
|
||||||
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
|
using ControllerMotionValues = std::array<ControllerMotionInfo, Settings::NativeMotion::NumMotions>;
|
||||||
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
|
using ColorValues = std::array<Common::Input::BodyColorStatus, max_emulated_controllers>;
|
||||||
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
|
using BatteryValues = std::array<Common::Input::BatteryStatus, max_emulated_controllers>;
|
||||||
|
using CameraValues = Common::Input::CameraStatus;
|
||||||
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
|
using VibrationValues = std::array<Common::Input::VibrationStatus, max_emulated_controllers>;
|
||||||
|
|
||||||
struct AnalogSticks {
|
struct AnalogSticks {
|
||||||
|
@ -70,6 +74,12 @@ struct BatteryLevelState {
|
||||||
NpadPowerInfo right{};
|
NpadPowerInfo right{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CameraState {
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat format{};
|
||||||
|
std::vector<u8> data{};
|
||||||
|
std::size_t sample{};
|
||||||
|
};
|
||||||
|
|
||||||
struct ControllerMotion {
|
struct ControllerMotion {
|
||||||
Common::Vec3f accel{};
|
Common::Vec3f accel{};
|
||||||
Common::Vec3f gyro{};
|
Common::Vec3f gyro{};
|
||||||
|
@ -96,6 +106,7 @@ struct ControllerStatus {
|
||||||
ColorValues color_values{};
|
ColorValues color_values{};
|
||||||
BatteryValues battery_values{};
|
BatteryValues battery_values{};
|
||||||
VibrationValues vibration_values{};
|
VibrationValues vibration_values{};
|
||||||
|
CameraValues camera_values{};
|
||||||
|
|
||||||
// Data for HID serices
|
// Data for HID serices
|
||||||
HomeButtonState home_button_state{};
|
HomeButtonState home_button_state{};
|
||||||
|
@ -107,6 +118,7 @@ struct ControllerStatus {
|
||||||
NpadGcTriggerState gc_trigger_state{};
|
NpadGcTriggerState gc_trigger_state{};
|
||||||
ControllerColors colors_state{};
|
ControllerColors colors_state{};
|
||||||
BatteryLevelState battery_state{};
|
BatteryLevelState battery_state{};
|
||||||
|
CameraState camera_state{};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ControllerTriggerType {
|
enum class ControllerTriggerType {
|
||||||
|
@ -117,6 +129,7 @@ enum class ControllerTriggerType {
|
||||||
Color,
|
Color,
|
||||||
Battery,
|
Battery,
|
||||||
Vibration,
|
Vibration,
|
||||||
|
IrSensor,
|
||||||
Connected,
|
Connected,
|
||||||
Disconnected,
|
Disconnected,
|
||||||
Type,
|
Type,
|
||||||
|
@ -269,6 +282,9 @@ public:
|
||||||
/// Returns the latest battery status from the controller with parameters
|
/// Returns the latest battery status from the controller with parameters
|
||||||
BatteryValues GetBatteryValues() const;
|
BatteryValues GetBatteryValues() const;
|
||||||
|
|
||||||
|
/// Returns the latest camera status from the controller with parameters
|
||||||
|
CameraValues GetCameraValues() const;
|
||||||
|
|
||||||
/// Returns the latest status of button input for the hid::HomeButton service
|
/// Returns the latest status of button input for the hid::HomeButton service
|
||||||
HomeButtonState GetHomeButtons() const;
|
HomeButtonState GetHomeButtons() const;
|
||||||
|
|
||||||
|
@ -296,6 +312,9 @@ public:
|
||||||
/// Returns the latest battery status from the controller
|
/// Returns the latest battery status from the controller
|
||||||
BatteryLevelState GetBattery() const;
|
BatteryLevelState GetBattery() const;
|
||||||
|
|
||||||
|
/// Returns the latest camera status from the controller
|
||||||
|
const CameraState& GetCamera() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a specific vibration to the output device
|
* Sends a specific vibration to the output device
|
||||||
* @return true if vibration had no errors
|
* @return true if vibration had no errors
|
||||||
|
@ -315,6 +334,13 @@ public:
|
||||||
*/
|
*/
|
||||||
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
bool SetPollingMode(Common::Input::PollingMode polling_mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the desired camera format to be polled from a controller
|
||||||
|
* @param camera_format size of each frame
|
||||||
|
* @return true if SetCameraFormat was successfull
|
||||||
|
*/
|
||||||
|
bool SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat camera_format);
|
||||||
|
|
||||||
/// Returns the led pattern corresponding to this emulated controller
|
/// Returns the led pattern corresponding to this emulated controller
|
||||||
LedPattern GetLedPattern() const;
|
LedPattern GetLedPattern() const;
|
||||||
|
|
||||||
|
@ -392,6 +418,12 @@ private:
|
||||||
*/
|
*/
|
||||||
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
|
void SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the camera status of the controller
|
||||||
|
* @param callback A CallbackStatus containing the camera status
|
||||||
|
*/
|
||||||
|
void SetCamera(const Common::Input::CallbackStatus& callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a callback that something has changed on the controller status
|
* Triggers a callback that something has changed on the controller status
|
||||||
* @param type Input type of the event to trigger
|
* @param type Input type of the event to trigger
|
||||||
|
@ -417,6 +449,7 @@ private:
|
||||||
ControllerMotionParams motion_params;
|
ControllerMotionParams motion_params;
|
||||||
TriggerParams trigger_params;
|
TriggerParams trigger_params;
|
||||||
BatteryParams battery_params;
|
BatteryParams battery_params;
|
||||||
|
CameraParams camera_params;
|
||||||
OutputParams output_params;
|
OutputParams output_params;
|
||||||
|
|
||||||
ButtonDevices button_devices;
|
ButtonDevices button_devices;
|
||||||
|
@ -424,6 +457,7 @@ private:
|
||||||
ControllerMotionDevices motion_devices;
|
ControllerMotionDevices motion_devices;
|
||||||
TriggerDevices trigger_devices;
|
TriggerDevices trigger_devices;
|
||||||
BatteryDevices battery_devices;
|
BatteryDevices battery_devices;
|
||||||
|
CameraDevices camera_devices;
|
||||||
OutputDevices output_devices;
|
OutputDevices output_devices;
|
||||||
|
|
||||||
// TAS related variables
|
// TAS related variables
|
||||||
|
|
|
@ -270,6 +270,20 @@ Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatu
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback) {
|
||||||
|
Common::Input::CameraStatus camera{};
|
||||||
|
switch (callback.type) {
|
||||||
|
case Common::Input::InputType::IrSensor:
|
||||||
|
camera = callback.camera_status;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Input, "Conversion from type {} to camera not implemented", callback.type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
|
void SanitizeAnalog(Common::Input::AnalogStatus& analog, bool clamp_value) {
|
||||||
const auto& properties = analog.properties;
|
const auto& properties = analog.properties;
|
||||||
float& raw_value = analog.raw_value;
|
float& raw_value = analog.raw_value;
|
||||||
|
|
|
@ -76,6 +76,14 @@ Common::Input::TriggerStatus TransformToTrigger(const Common::Input::CallbackSta
|
||||||
*/
|
*/
|
||||||
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
|
Common::Input::AnalogStatus TransformToAnalog(const Common::Input::CallbackStatus& callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts raw input data into a valid camera status.
|
||||||
|
*
|
||||||
|
* @param callback Supported callbacks: Camera.
|
||||||
|
* @return A valid CameraObject object.
|
||||||
|
*/
|
||||||
|
Common::Input::CameraStatus TransformToCamera(const Common::Input::CallbackStatus& callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts raw analog data into a valid analog value
|
* Converts raw analog data into a valid analog value
|
||||||
* @param analog An analog object containing raw data and properties
|
* @param analog An analog object containing raw data and properties
|
||||||
|
|
301
src/core/hid/irs_types.h
Executable file
301
src/core/hid/irs_types.h
Executable file
|
@ -0,0 +1,301 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/hid_types.h"
|
||||||
|
|
||||||
|
namespace Core::IrSensor {
|
||||||
|
|
||||||
|
// This is nn::irsensor::CameraAmbientNoiseLevel
|
||||||
|
enum class CameraAmbientNoiseLevel : u32 {
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
Unkown3, // This level can't be reached
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::CameraLightTarget
|
||||||
|
enum class CameraLightTarget : u32 {
|
||||||
|
AllLeds,
|
||||||
|
BrightLeds,
|
||||||
|
DimLeds,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedCameraLightTarget
|
||||||
|
enum class PackedCameraLightTarget : u8 {
|
||||||
|
AllLeds,
|
||||||
|
BrightLeds,
|
||||||
|
DimLeds,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::AdaptiveClusteringMode
|
||||||
|
enum class AdaptiveClusteringMode : u32 {
|
||||||
|
StaticFov,
|
||||||
|
DynamicFov,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::AdaptiveClusteringTargetDistance
|
||||||
|
enum class AdaptiveClusteringTargetDistance : u32 {
|
||||||
|
Near,
|
||||||
|
Middle,
|
||||||
|
Far,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::ImageTransferProcessorFormat
|
||||||
|
enum class ImageTransferProcessorFormat : u32 {
|
||||||
|
Size320x240,
|
||||||
|
Size160x120,
|
||||||
|
Size80x60,
|
||||||
|
Size40x30,
|
||||||
|
Size20x15,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedImageTransferProcessorFormat
|
||||||
|
enum class PackedImageTransferProcessorFormat : u8 {
|
||||||
|
Size320x240,
|
||||||
|
Size160x120,
|
||||||
|
Size80x60,
|
||||||
|
Size40x30,
|
||||||
|
Size20x15,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::IrCameraStatus
|
||||||
|
enum class IrCameraStatus : u32 {
|
||||||
|
Available,
|
||||||
|
Unsupported,
|
||||||
|
Unconnected,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::IrCameraInternalStatus
|
||||||
|
enum class IrCameraInternalStatus : u32 {
|
||||||
|
Stopped,
|
||||||
|
FirmwareUpdateNeeded,
|
||||||
|
Unkown2,
|
||||||
|
Unkown3,
|
||||||
|
Unkown4,
|
||||||
|
FirmwareVersionRequested,
|
||||||
|
FirmwareVersionIsInvalid,
|
||||||
|
Ready,
|
||||||
|
Setting,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::detail::StatusManager::IrSensorMode
|
||||||
|
enum class IrSensorMode : u64 {
|
||||||
|
None,
|
||||||
|
MomentProcessor,
|
||||||
|
ClusteringProcessor,
|
||||||
|
ImageTransferProcessor,
|
||||||
|
PointingProcessorMarker,
|
||||||
|
TeraPluginProcessor,
|
||||||
|
IrLedProcessor,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::ImageProcessorStatus
|
||||||
|
enum ImageProcessorStatus : u32 {
|
||||||
|
Stopped,
|
||||||
|
Running,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::HandAnalysisMode
|
||||||
|
enum class HandAnalysisMode : u32 {
|
||||||
|
None,
|
||||||
|
Silhouette,
|
||||||
|
Image,
|
||||||
|
SilhoueteAndImage,
|
||||||
|
SilhuetteOnly,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::IrSensorFunctionLevel
|
||||||
|
enum class IrSensorFunctionLevel : u8 {
|
||||||
|
unknown0,
|
||||||
|
unknown1,
|
||||||
|
unknown2,
|
||||||
|
unknown3,
|
||||||
|
unknown4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::MomentProcessorPreprocess
|
||||||
|
enum class MomentProcessorPreprocess : u32 {
|
||||||
|
Unkown0,
|
||||||
|
Unkown1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedMomentProcessorPreprocess
|
||||||
|
enum class PackedMomentProcessorPreprocess : u8 {
|
||||||
|
Unkown0,
|
||||||
|
Unkown1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is nn::irsensor::PointingStatus
|
||||||
|
enum class PointingStatus : u32 {
|
||||||
|
Unkown0,
|
||||||
|
Unkown1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IrsRect {
|
||||||
|
s16 x;
|
||||||
|
s16 y;
|
||||||
|
s16 width;
|
||||||
|
s16 height;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IrsCentroid {
|
||||||
|
f32 x;
|
||||||
|
f32 y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CameraConfig {
|
||||||
|
u64 exposure_time;
|
||||||
|
CameraLightTarget light_target;
|
||||||
|
u32 gain;
|
||||||
|
bool is_negative_used;
|
||||||
|
INSERT_PADDING_BYTES(7);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(CameraConfig) == 0x18, "CameraConfig is an invalid size");
|
||||||
|
|
||||||
|
struct PackedCameraConfig {
|
||||||
|
u64 exposure_time;
|
||||||
|
PackedCameraLightTarget light_target;
|
||||||
|
u8 gain;
|
||||||
|
bool is_negative_used;
|
||||||
|
INSERT_PADDING_BYTES(5);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedCameraConfig) == 0x10, "PackedCameraConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::IrCameraHandle
|
||||||
|
struct IrCameraHandle {
|
||||||
|
u8 npad_id{};
|
||||||
|
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
|
||||||
|
INSERT_PADDING_BYTES(2);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedMcuVersion
|
||||||
|
struct PackedMcuVersion {
|
||||||
|
u16 major;
|
||||||
|
u16 minor;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedMomentProcessorConfig
|
||||||
|
struct PackedMomentProcessorConfig {
|
||||||
|
PackedCameraConfig camera_config;
|
||||||
|
IrsRect window_of_interest;
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
PackedMomentProcessorPreprocess preprocess;
|
||||||
|
u8 preprocess_intensity_threshold;
|
||||||
|
INSERT_PADDING_BYTES(2);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
|
||||||
|
"PackedMomentProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedClusteringProcessorConfig
|
||||||
|
struct PackedClusteringProcessorConfig {
|
||||||
|
PackedCameraConfig camera_config;
|
||||||
|
IrsRect window_of_interest;
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
u32 pixel_count_min;
|
||||||
|
u32 pixel_count_max;
|
||||||
|
u8 object_intensity_min;
|
||||||
|
bool is_external_light_filter_enabled;
|
||||||
|
INSERT_PADDING_BYTES(2);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x28,
|
||||||
|
"PackedClusteringProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedImageTransferProcessorConfig
|
||||||
|
struct PackedImageTransferProcessorConfig {
|
||||||
|
PackedCameraConfig camera_config;
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
PackedImageTransferProcessorFormat format;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
|
||||||
|
"PackedImageTransferProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedTeraPluginProcessorConfig
|
||||||
|
struct PackedTeraPluginProcessorConfig {
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
u8 mode;
|
||||||
|
u8 unknown_1;
|
||||||
|
u8 unknown_2;
|
||||||
|
u8 unknown_3;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
|
||||||
|
"PackedTeraPluginProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedPointingProcessorConfig
|
||||||
|
struct PackedPointingProcessorConfig {
|
||||||
|
IrsRect window_of_interest;
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
|
||||||
|
"PackedPointingProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedFunctionLevel
|
||||||
|
struct PackedFunctionLevel {
|
||||||
|
IrSensorFunctionLevel function_level;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedImageTransferProcessorExConfig
|
||||||
|
struct PackedImageTransferProcessorExConfig {
|
||||||
|
PackedCameraConfig camera_config;
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
PackedImageTransferProcessorFormat origin_format;
|
||||||
|
PackedImageTransferProcessorFormat trimming_format;
|
||||||
|
u16 trimming_start_x;
|
||||||
|
u16 trimming_start_y;
|
||||||
|
bool is_external_light_filter_enabled;
|
||||||
|
INSERT_PADDING_BYTES(5);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
|
||||||
|
"PackedImageTransferProcessorExConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::PackedIrLedProcessorConfig
|
||||||
|
struct PackedIrLedProcessorConfig {
|
||||||
|
PackedMcuVersion required_mcu_version;
|
||||||
|
u8 light_target;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
|
||||||
|
"PackedIrLedProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::HandAnalysisConfig
|
||||||
|
struct HandAnalysisConfig {
|
||||||
|
HandAnalysisMode mode;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(HandAnalysisConfig) == 0x4, "HandAnalysisConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::detail::ProcessorState contents are different for each processor
|
||||||
|
struct ProcessorState {
|
||||||
|
std::array<u8, 0xE20> processor_raw_data{};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ProcessorState) == 0xE20, "ProcessorState is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::detail::DeviceFormat
|
||||||
|
struct DeviceFormat {
|
||||||
|
Core::IrSensor::IrCameraStatus camera_status{Core::IrSensor::IrCameraStatus::Unconnected};
|
||||||
|
Core::IrSensor::IrCameraInternalStatus camera_internal_status{
|
||||||
|
Core::IrSensor::IrCameraInternalStatus::Ready};
|
||||||
|
Core::IrSensor::IrSensorMode mode{Core::IrSensor::IrSensorMode::None};
|
||||||
|
ProcessorState state{};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(DeviceFormat) == 0xE30, "DeviceFormat is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::ImageTransferProcessorState
|
||||||
|
struct ImageTransferProcessorState {
|
||||||
|
u64 sampling_number;
|
||||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
|
||||||
|
INSERT_PADDING_BYTES(4);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ImageTransferProcessorState) == 0x10,
|
||||||
|
"ImageTransferProcessorState is an invalid size");
|
||||||
|
|
||||||
|
} // namespace Core::IrSensor
|
|
@ -19,3 +19,10 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
|
||||||
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
|
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
|
constexpr Result InvalidProcessorState{ErrorModule::Irsensor, 78};
|
||||||
|
constexpr Result InvalidIrCameraHandle{ErrorModule::Irsensor, 204};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
||||||
|
|
|
@ -2345,8 +2345,8 @@ void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system
|
||||||
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
|
std::make_shared<HidSys>(system)->InstallAsService(service_manager);
|
||||||
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
|
std::make_shared<HidTmp>(system)->InstallAsService(service_manager);
|
||||||
|
|
||||||
std::make_shared<IRS>(system)->InstallAsService(service_manager);
|
std::make_shared<Service::IRS::IRS>(system)->InstallAsService(service_manager);
|
||||||
std::make_shared<IRS_SYS>(system)->InstallAsService(service_manager);
|
std::make_shared<Service::IRS::IRS_SYS>(system)->InstallAsService(service_manager);
|
||||||
|
|
||||||
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
|
std::make_shared<XCD_SYS>(system)->InstallAsService(service_manager);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,28 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
|
#include "core/hid/emulated_controller.h"
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
#include "core/hle/ipc_helpers.h"
|
#include "core/hle/ipc_helpers.h"
|
||||||
#include "core/hle/kernel/k_shared_memory.h"
|
#include "core/hle/kernel/k_shared_memory.h"
|
||||||
#include "core/hle/kernel/k_transfer_memory.h"
|
#include "core/hle/kernel/k_transfer_memory.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/service/hid/errors.h"
|
#include "core/hle/service/hid/errors.h"
|
||||||
#include "core/hle/service/hid/irs.h"
|
#include "core/hle/service/hid/irs.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/moment_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/pointing_processor.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Service::IRS {
|
||||||
|
|
||||||
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -36,14 +48,19 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
u8* raw_shared_memory = system.Kernel().GetIrsSharedMem().GetPointer();
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
shared_memory = std::construct_at(reinterpret_cast<StatusManager*>(raw_shared_memory));
|
||||||
|
|
||||||
|
npad_device = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
|
||||||
}
|
}
|
||||||
|
IRS::~IRS() = default;
|
||||||
|
|
||||||
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
|
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
|
||||||
applet_resource_user_id);
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -54,7 +71,7 @@ void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
|
LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}",
|
||||||
applet_resource_user_id);
|
applet_resource_user_id);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
@ -75,7 +92,7 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
|
||||||
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
|
@ -88,17 +105,23 @@ void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Stop Image processor
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedMomentProcessorConfig processor_config;
|
Core::IrSensor::PackedMomentProcessorConfig processor_config;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
||||||
|
|
||||||
|
@ -109,19 +132,28 @@ void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessor<MomentProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedClusteringProcessorConfig processor_config;
|
Core::IrSensor::PackedClusteringProcessorConfig processor_config;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
|
@ -130,17 +162,27 @@ void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessor<ClusteringProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<ClusteringProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedImageTransferProcessorConfig processor_config;
|
Core::IrSensor::PackedImageTransferProcessorConfig processor_config;
|
||||||
u32 transfer_memory_size;
|
u32 transfer_memory_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size.");
|
||||||
|
@ -151,20 +193,42 @@ void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
auto t_mem =
|
auto t_mem =
|
||||||
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
if (t_mem.IsNull()) {
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle);
|
||||||
"applet_resource_user_id={}",
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
rb.Push(ResultUnknown);
|
||||||
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size");
|
||||||
|
|
||||||
|
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
|
||||||
|
|
||||||
|
LOG_INFO(Service_IRS,
|
||||||
|
"called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, "
|
||||||
|
"applet_resource_user_id={}",
|
||||||
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
|
parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
|
@ -172,32 +236,68 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.applet_resource_user_id);
|
||||||
parameters.applet_resource_user_id);
|
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 5};
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsError()) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) {
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(InvalidProcessorState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> data{};
|
||||||
|
const auto& image_transfer_processor =
|
||||||
|
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
|
||||||
|
const auto& state = image_transfer_processor.GetState(data);
|
||||||
|
|
||||||
|
ctx.WriteBuffer(data);
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
|
rb.PushRaw(state);
|
||||||
rb.PushRaw<u32>(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
struct Parameters {
|
||||||
const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()};
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
Core::IrSensor::PackedTeraPluginProcessorConfig processor_config;
|
||||||
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
|
u64 applet_resource_user_id;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
const auto parameters{rp.PopRaw<Parameters>()};
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
|
|
||||||
"applet_resource_user_id={}",
|
LOG_WARNING(
|
||||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.mode,
|
Service_IRS,
|
||||||
processor_config.required_mcu_version.major,
|
"(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, "
|
||||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
"applet_resource_user_id={}",
|
||||||
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
|
parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major,
|
||||||
|
parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
const auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<TeraPluginProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -207,17 +307,17 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
||||||
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
|
if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid &&
|
||||||
npad_id != Core::HID::NpadIdType::Handheld) {
|
npad_id != Core::HID::NpadIdType::Handheld) {
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(InvalidNpadId);
|
rb.Push(Service::HID::InvalidNpadId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrCameraHandle camera_handle{
|
Core::IrSensor::IrCameraHandle camera_handle{
|
||||||
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
|
.npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)),
|
||||||
.npad_type = Core::HID::NpadStyleIndex::None,
|
.npad_type = Core::HID::NpadStyleIndex::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}",
|
LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id,
|
||||||
npad_id, camera_handle.npad_id, camera_handle.npad_type);
|
camera_handle.npad_id, camera_handle.npad_type);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 3};
|
IPC::ResponseBuilder rb{ctx, 3};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
|
@ -226,8 +326,8 @@ void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()};
|
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(
|
LOG_WARNING(
|
||||||
|
@ -236,14 +336,23 @@ void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
|
camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major,
|
||||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
|
||||||
|
MakeProcessor<PointingProcessor>(camera_handle, device);
|
||||||
|
auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
|
@ -256,14 +365,20 @@ void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Suspend image processor
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
const auto mcu_version{rp.PopRaw<PackedMcuVersion>()};
|
const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(
|
LOG_WARNING(
|
||||||
|
@ -272,37 +387,45 @@ void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
|
||||||
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
|
camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major,
|
||||||
mcu_version.minor);
|
mcu_version.minor);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Check firmware version
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
|
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
IrCameraHandle camera_handle;
|
const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()};
|
||||||
PackedFunctionLevel function_level;
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
u64 applet_resource_user_id;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size.");
|
|
||||||
|
|
||||||
const auto parameters{rp.PopRaw<Parameters>()};
|
LOG_WARNING(
|
||||||
|
Service_IRS,
|
||||||
|
"(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}",
|
||||||
|
camera_handle.npad_type, camera_handle.npad_id, function_level.function_level,
|
||||||
|
applet_resource_user_id);
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}",
|
if (result.IsSuccess()) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
// TODO: Set Function level
|
||||||
parameters.applet_resource_user_id);
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
PackedImageTransferProcessorExConfig processor_config;
|
Core::IrSensor::PackedImageTransferProcessorExConfig processor_config;
|
||||||
u64 transfer_memory_size;
|
u64 transfer_memory_size;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size.");
|
||||||
|
@ -313,20 +436,33 @@ void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
auto t_mem =
|
auto t_mem =
|
||||||
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle);
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
u8* transfer_memory = system.Memory().GetPointer(t_mem->GetSourceAddress());
|
||||||
"(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
|
||||||
"applet_resource_user_id={}",
|
LOG_INFO(Service_IRS,
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
"called, npad_type={}, npad_id={}, transfer_memory_size={}, "
|
||||||
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
"applet_resource_user_id={}",
|
||||||
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
|
parameters.transfer_memory_size, parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle);
|
||||||
|
MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device);
|
||||||
|
auto& image_transfer_processor =
|
||||||
|
GetProcessor<ImageTransferProcessor>(parameters.camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(parameters.processor_config);
|
||||||
|
image_transfer_processor.SetTransferMemoryPointer(transfer_memory);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto camera_handle{rp.PopRaw<IrCameraHandle>()};
|
const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()};
|
||||||
const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()};
|
const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_WARNING(Service_IRS,
|
LOG_WARNING(Service_IRS,
|
||||||
|
@ -336,14 +472,23 @@ void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
|
||||||
processor_config.required_mcu_version.major,
|
processor_config.required_mcu_version.major,
|
||||||
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
processor_config.required_mcu_version.minor, applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(camera_handle);
|
||||||
|
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle);
|
||||||
|
MakeProcessor<IrLedProcessor>(camera_handle, device);
|
||||||
|
auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle);
|
||||||
|
image_transfer_processor.SetConfig(processor_config);
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
IrCameraHandle camera_handle;
|
Core::IrSensor::IrCameraHandle camera_handle;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
|
@ -356,14 +501,20 @@ void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
|
||||||
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
parameters.camera_handle.npad_type, parameters.camera_handle.npad_id,
|
||||||
parameters.applet_resource_user_id);
|
parameters.applet_resource_user_id);
|
||||||
|
|
||||||
|
auto result = IsIrCameraHandleValid(parameters.camera_handle);
|
||||||
|
if (result.IsSuccess()) {
|
||||||
|
// TODO: Stop image processor async
|
||||||
|
result = ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2};
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
struct Parameters {
|
struct Parameters {
|
||||||
PackedFunctionLevel function_level;
|
Core::IrSensor::PackedFunctionLevel function_level;
|
||||||
INSERT_PADDING_WORDS_NOINIT(1);
|
INSERT_PADDING_WORDS_NOINIT(1);
|
||||||
u64 applet_resource_user_id;
|
u64 applet_resource_user_id;
|
||||||
};
|
};
|
||||||
|
@ -378,7 +529,22 @@ void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
IRS::~IRS() = default;
|
Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const {
|
||||||
|
if (camera_handle.npad_id >
|
||||||
|
static_cast<u8>(NpadIdTypeToIndex(Core::HID::NpadIdType::Handheld))) {
|
||||||
|
return InvalidIrCameraHandle;
|
||||||
|
}
|
||||||
|
if (camera_handle.npad_type != Core::HID::NpadStyleIndex::None) {
|
||||||
|
return InvalidIrCameraHandle;
|
||||||
|
}
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::IrSensor::DeviceFormat& IRS::GetIrCameraSharedMemoryDeviceEntry(
|
||||||
|
const Core::IrSensor::IrCameraHandle& camera_handle) {
|
||||||
|
ASSERT_MSG(sizeof(StatusManager::device) > camera_handle.npad_id, "invalid npad_id");
|
||||||
|
return shared_memory->device[camera_handle.npad_id];
|
||||||
|
}
|
||||||
|
|
||||||
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
|
IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -395,4 +561,4 @@ IRS_SYS::IRS_SYS(Core::System& system_) : ServiceFramework{system_, "irs:sys"} {
|
||||||
|
|
||||||
IRS_SYS::~IRS_SYS() = default;
|
IRS_SYS::~IRS_SYS() = default;
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::IRS
|
||||||
|
|
|
@ -4,13 +4,19 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/hid/hid_types.h"
|
#include "core/hid/hid_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Service::HID {
|
namespace Core::HID {
|
||||||
|
class EmulatedController;
|
||||||
|
} // namespace Core::HID
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
class IRS final : public ServiceFramework<IRS> {
|
class IRS final : public ServiceFramework<IRS> {
|
||||||
public:
|
public:
|
||||||
|
@ -18,234 +24,19 @@ public:
|
||||||
~IRS() override;
|
~IRS() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This is nn::irsensor::IrCameraStatus
|
// This is nn::irsensor::detail::AruidFormat
|
||||||
enum IrCameraStatus : u32 {
|
struct AruidFormat {
|
||||||
Available,
|
u64 sensor_aruid;
|
||||||
Unsupported,
|
u64 sensor_aruid_status;
|
||||||
Unconnected,
|
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(AruidFormat) == 0x10, "AruidFormat is an invalid size");
|
||||||
|
|
||||||
// This is nn::irsensor::IrCameraInternalStatus
|
// This is nn::irsensor::detail::StatusManager
|
||||||
enum IrCameraInternalStatus : u32 {
|
struct StatusManager {
|
||||||
Stopped,
|
std::array<Core::IrSensor::DeviceFormat, 9> device;
|
||||||
FirmwareUpdateNeeded,
|
std::array<AruidFormat, 5> aruid;
|
||||||
Unkown2,
|
|
||||||
Unkown3,
|
|
||||||
Unkown4,
|
|
||||||
FirmwareVersionRequested,
|
|
||||||
FirmwareVersionIsInvalid,
|
|
||||||
Ready,
|
|
||||||
Setting,
|
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size");
|
||||||
// This is nn::irsensor::detail::StatusManager::IrSensorMode
|
|
||||||
enum IrSensorMode : u64 {
|
|
||||||
None,
|
|
||||||
MomentProcessor,
|
|
||||||
ClusteringProcessor,
|
|
||||||
ImageTransferProcessor,
|
|
||||||
PointingProcessorMarker,
|
|
||||||
TeraPluginProcessor,
|
|
||||||
IrLedProcessor,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::ImageProcessorStatus
|
|
||||||
enum ImageProcessorStatus : u8 {
|
|
||||||
stopped,
|
|
||||||
running,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::ImageTransferProcessorFormat
|
|
||||||
enum ImageTransferProcessorFormat : u8 {
|
|
||||||
Size320x240,
|
|
||||||
Size160x120,
|
|
||||||
Size80x60,
|
|
||||||
Size40x30,
|
|
||||||
Size20x15,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::AdaptiveClusteringMode
|
|
||||||
enum AdaptiveClusteringMode : u8 {
|
|
||||||
StaticFov,
|
|
||||||
DynamicFov,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::AdaptiveClusteringTargetDistance
|
|
||||||
enum AdaptiveClusteringTargetDistance : u8 {
|
|
||||||
Near,
|
|
||||||
Middle,
|
|
||||||
Far,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::IrsHandAnalysisMode
|
|
||||||
enum IrsHandAnalysisMode : u8 {
|
|
||||||
Silhouette,
|
|
||||||
Image,
|
|
||||||
SilhoueteAndImage,
|
|
||||||
SilhuetteOnly,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::IrSensorFunctionLevel
|
|
||||||
enum IrSensorFunctionLevel : u8 {
|
|
||||||
unknown0,
|
|
||||||
unknown1,
|
|
||||||
unknown2,
|
|
||||||
unknown3,
|
|
||||||
unknown4,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::IrCameraHandle
|
|
||||||
struct IrCameraHandle {
|
|
||||||
u8 npad_id{};
|
|
||||||
Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
|
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size");
|
|
||||||
|
|
||||||
struct IrsRect {
|
|
||||||
s16 x;
|
|
||||||
s16 y;
|
|
||||||
s16 width;
|
|
||||||
s16 height;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedMcuVersion
|
|
||||||
struct PackedMcuVersion {
|
|
||||||
u16 major;
|
|
||||||
u16 minor;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::MomentProcessorConfig
|
|
||||||
struct MomentProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(7);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
u8 preprocess;
|
|
||||||
u8 preprocess_intensity_threshold;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(MomentProcessorConfig) == 0x28,
|
|
||||||
"MomentProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedMomentProcessorConfig
|
|
||||||
struct PackedMomentProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 preprocess;
|
|
||||||
u8 preprocess_intensity_threshold;
|
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedMomentProcessorConfig) == 0x20,
|
|
||||||
"PackedMomentProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::ClusteringProcessorConfig
|
|
||||||
struct ClusteringProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u32 light_target;
|
|
||||||
u32 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(7);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
u32 pixel_count_min;
|
|
||||||
u32 pixel_count_max;
|
|
||||||
u32 object_intensity_min;
|
|
||||||
u8 is_external_light_filter_enabled;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
|
|
||||||
"ClusteringProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedClusteringProcessorConfig
|
|
||||||
struct PackedClusteringProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u32 pixel_count_min;
|
|
||||||
u32 pixel_count_max;
|
|
||||||
u32 object_intensity_min;
|
|
||||||
u8 is_external_light_filter_enabled;
|
|
||||||
INSERT_PADDING_BYTES(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30,
|
|
||||||
"PackedClusteringProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedImageTransferProcessorConfig
|
|
||||||
struct PackedImageTransferProcessorConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 format;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18,
|
|
||||||
"PackedImageTransferProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedTeraPluginProcessorConfig
|
|
||||||
struct PackedTeraPluginProcessorConfig {
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 mode;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8,
|
|
||||||
"PackedTeraPluginProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedPointingProcessorConfig
|
|
||||||
struct PackedPointingProcessorConfig {
|
|
||||||
IrsRect window_of_interest;
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedPointingProcessorConfig) == 0xC,
|
|
||||||
"PackedPointingProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedFunctionLevel
|
|
||||||
struct PackedFunctionLevel {
|
|
||||||
IrSensorFunctionLevel function_level;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedImageTransferProcessorExConfig
|
|
||||||
struct PackedImageTransferProcessorExConfig {
|
|
||||||
u64 exposire_time;
|
|
||||||
u8 light_target;
|
|
||||||
u8 gain;
|
|
||||||
u8 is_negative_used;
|
|
||||||
INSERT_PADDING_BYTES(5);
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
ImageTransferProcessorFormat origin_format;
|
|
||||||
ImageTransferProcessorFormat trimming_format;
|
|
||||||
u16 trimming_start_x;
|
|
||||||
u16 trimming_start_y;
|
|
||||||
u8 is_external_light_filter_enabled;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20,
|
|
||||||
"PackedImageTransferProcessorExConfig is an invalid size");
|
|
||||||
|
|
||||||
// This is nn::irsensor::PackedIrLedProcessorConfig
|
|
||||||
struct PackedIrLedProcessorConfig {
|
|
||||||
PackedMcuVersion required_mcu_version;
|
|
||||||
u8 light_target;
|
|
||||||
INSERT_PADDING_BYTES(3);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8,
|
|
||||||
"PackedIrLedProcessorConfig is an invalid size");
|
|
||||||
|
|
||||||
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
|
void ActivateIrsensor(Kernel::HLERequestContext& ctx);
|
||||||
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
|
void DeactivateIrsensor(Kernel::HLERequestContext& ctx);
|
||||||
|
@ -265,6 +56,56 @@ private:
|
||||||
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
|
void RunIrLedProcessor(Kernel::HLERequestContext& ctx);
|
||||||
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
|
void StopImageProcessorAsync(Kernel::HLERequestContext& ctx);
|
||||||
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
|
void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
|
Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const;
|
||||||
|
Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry(
|
||||||
|
const Core::IrSensor::IrCameraHandle& camera_handle);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void MakeProcessor(const Core::IrSensor::IrCameraHandle& handle,
|
||||||
|
Core::IrSensor::DeviceFormat& device_state) {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processors[index] = std::make_unique<T>(device_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void MakeProcessorWithCoreContext(const Core::IrSensor::IrCameraHandle& handle,
|
||||||
|
Core::IrSensor::DeviceFormat& device_state) {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processors[index] = std::make_unique<T>(system.HIDCore(), device_state, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return static_cast<T&>(*processors[0]);
|
||||||
|
}
|
||||||
|
return static_cast<T&>(*processors[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& GetProcessor(const Core::IrSensor::IrCameraHandle& handle) const {
|
||||||
|
const auto index = static_cast<std::size_t>(handle.npad_id);
|
||||||
|
if (index > sizeof(processors)) {
|
||||||
|
LOG_CRITICAL(Service_IRS, "Invalid index {}", index);
|
||||||
|
return static_cast<T&>(*processors[0]);
|
||||||
|
}
|
||||||
|
return static_cast<T&>(*processors[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::HID::EmulatedController* npad_device = nullptr;
|
||||||
|
StatusManager* shared_memory = nullptr;
|
||||||
|
std::array<std::unique_ptr<ProcessorBase>, 9> processors{};
|
||||||
};
|
};
|
||||||
|
|
||||||
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
|
class IRS_SYS final : public ServiceFramework<IRS_SYS> {
|
||||||
|
@ -273,4 +114,4 @@ public:
|
||||||
~IRS_SYS() override;
|
~IRS_SYS() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::IRS
|
||||||
|
|
34
src/core/hle/service/hid/irsensor/clustering_processor.cpp
Executable file
34
src/core/hle/service/hid/irsensor/clustering_processor.cpp
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/irsensor/clustering_processor.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
ClusteringProcessor::ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format)
|
||||||
|
: device(device_format) {
|
||||||
|
device.mode = Core::IrSensor::IrSensorMode::ClusteringProcessor;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusteringProcessor::~ClusteringProcessor() = default;
|
||||||
|
|
||||||
|
void ClusteringProcessor::StartProcessor() {}
|
||||||
|
|
||||||
|
void ClusteringProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
|
void ClusteringProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void ClusteringProcessor::SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config) {
|
||||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
||||||
|
current_config.camera_config.gain = config.camera_config.gain;
|
||||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
|
||||||
|
current_config.camera_config.light_target =
|
||||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
|
||||||
|
current_config.pixel_count_min = config.pixel_count_min;
|
||||||
|
current_config.pixel_count_max = config.pixel_count_max;
|
||||||
|
current_config.is_external_light_filter_enabled = config.is_external_light_filter_enabled;
|
||||||
|
current_config.object_intensity_min = config.object_intensity_min;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
74
src/core/hle/service/hid/irsensor/clustering_processor.h
Executable file
74
src/core/hle/service/hid/irsensor/clustering_processor.h
Executable file
|
@ -0,0 +1,74 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class ClusteringProcessor final : public ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit ClusteringProcessor(Core::IrSensor::DeviceFormat& device_format);
|
||||||
|
~ClusteringProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is initialized
|
||||||
|
void StartProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is suspended
|
||||||
|
void SuspendProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is stopped
|
||||||
|
void StopProcessor() override;
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetConfig(Core::IrSensor::PackedClusteringProcessorConfig config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is nn::irsensor::ClusteringProcessorConfig
|
||||||
|
struct ClusteringProcessorConfig {
|
||||||
|
Core::IrSensor::CameraConfig camera_config;
|
||||||
|
Core::IrSensor::IrsRect window_of_interest;
|
||||||
|
u32 pixel_count_min;
|
||||||
|
u32 pixel_count_max;
|
||||||
|
u32 object_intensity_min;
|
||||||
|
bool is_external_light_filter_enabled;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ClusteringProcessorConfig) == 0x30,
|
||||||
|
"ClusteringProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::AdaptiveClusteringProcessorConfig
|
||||||
|
struct AdaptiveClusteringProcessorConfig {
|
||||||
|
Core::IrSensor::AdaptiveClusteringMode mode;
|
||||||
|
Core::IrSensor::AdaptiveClusteringTargetDistance target_distance;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(AdaptiveClusteringProcessorConfig) == 0x8,
|
||||||
|
"AdaptiveClusteringProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::ClusteringData
|
||||||
|
struct ClusteringData {
|
||||||
|
f32 average_intensity;
|
||||||
|
Core::IrSensor::IrsCentroid centroid;
|
||||||
|
u32 pixel_count;
|
||||||
|
Core::IrSensor::IrsRect bound;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ClusteringData) == 0x18, "ClusteringData is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::ClusteringProcessorState
|
||||||
|
struct ClusteringProcessorState {
|
||||||
|
s64 sampling_number;
|
||||||
|
u64 timestamp;
|
||||||
|
u8 object_count;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
|
||||||
|
std::array<ClusteringData, 0x10> data;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ClusteringProcessorState) == 0x198,
|
||||||
|
"ClusteringProcessorState is an invalid size");
|
||||||
|
|
||||||
|
ClusteringProcessorConfig current_config{};
|
||||||
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
};
|
||||||
|
} // namespace Service::IRS
|
150
src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
Executable file
150
src/core/hle/service/hid/irsensor/image_transfer_processor.cpp
Executable file
|
@ -0,0 +1,150 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hid/emulated_controller.h"
|
||||||
|
#include "core/hid/hid_core.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/image_transfer_processor.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
ImageTransferProcessor::ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
|
||||||
|
Core::IrSensor::DeviceFormat& device_format,
|
||||||
|
std::size_t npad_index)
|
||||||
|
: device{device_format} {
|
||||||
|
npad_device = hid_core_.GetEmulatedControllerByIndex(npad_index);
|
||||||
|
|
||||||
|
Core::HID::ControllerUpdateCallback engine_callback{
|
||||||
|
.on_change = [this](Core::HID::ControllerTriggerType type) { OnControllerUpdate(type); },
|
||||||
|
.is_npad_service = true,
|
||||||
|
};
|
||||||
|
callback_key = npad_device->SetCallback(engine_callback);
|
||||||
|
|
||||||
|
device.mode = Core::IrSensor::IrSensorMode::ImageTransferProcessor;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageTransferProcessor::~ImageTransferProcessor() {
|
||||||
|
npad_device->DeleteCallback(callback_key);
|
||||||
|
};
|
||||||
|
|
||||||
|
void ImageTransferProcessor::StartProcessor() {
|
||||||
|
is_active = true;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Available;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Ready;
|
||||||
|
processor_state.sampling_number = 0;
|
||||||
|
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageTransferProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
|
void ImageTransferProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type) {
|
||||||
|
if (type != Core::HID::ControllerTriggerType::IrSensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!is_transfer_memory_set) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto camera_data = npad_device->GetCamera();
|
||||||
|
|
||||||
|
// This indicates how much ambient light is precent
|
||||||
|
processor_state.ambient_noise_level = Core::IrSensor::CameraAmbientNoiseLevel::Low;
|
||||||
|
processor_state.sampling_number = camera_data.sample;
|
||||||
|
|
||||||
|
if (camera_data.format != current_config.origin_format) {
|
||||||
|
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
|
||||||
|
current_config.origin_format);
|
||||||
|
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_config.origin_format > current_config.trimming_format) {
|
||||||
|
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
|
||||||
|
current_config.origin_format, current_config.trimming_format);
|
||||||
|
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> window_data{};
|
||||||
|
const auto origin_width = GetDataWidth(current_config.origin_format);
|
||||||
|
const auto origin_height = GetDataHeight(current_config.origin_format);
|
||||||
|
const auto trimming_width = GetDataWidth(current_config.trimming_format);
|
||||||
|
const auto trimming_height = GetDataHeight(current_config.trimming_format);
|
||||||
|
window_data.resize(GetDataSize(current_config.trimming_format));
|
||||||
|
|
||||||
|
if (trimming_width + current_config.trimming_start_x > origin_width ||
|
||||||
|
trimming_height + current_config.trimming_start_y > origin_height) {
|
||||||
|
LOG_WARNING(Service_IRS,
|
||||||
|
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
|
||||||
|
current_config.trimming_start_x, current_config.trimming_start_y,
|
||||||
|
trimming_width, trimming_height, origin_width, origin_height);
|
||||||
|
memset(transfer_memory, 0, GetDataSize(current_config.trimming_format));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t y = 0; y < trimming_height; y++) {
|
||||||
|
for (std::size_t x = 0; x < trimming_width; x++) {
|
||||||
|
const std::size_t window_index = (y * trimming_width) + x;
|
||||||
|
const std::size_t origin_index =
|
||||||
|
((y + current_config.trimming_start_y) * origin_width) + x +
|
||||||
|
current_config.trimming_start_x;
|
||||||
|
window_data[window_index] = camera_data.data[origin_index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(transfer_memory, window_data.data(), GetDataSize(current_config.trimming_format));
|
||||||
|
|
||||||
|
if (!IsProcessorActive()) {
|
||||||
|
StartProcessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageTransferProcessor::SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config) {
|
||||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
||||||
|
current_config.camera_config.gain = config.camera_config.gain;
|
||||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
|
||||||
|
current_config.camera_config.light_target =
|
||||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
|
||||||
|
current_config.origin_format =
|
||||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
|
||||||
|
current_config.trimming_format =
|
||||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.format);
|
||||||
|
current_config.trimming_start_x = 0;
|
||||||
|
current_config.trimming_start_y = 0;
|
||||||
|
|
||||||
|
npad_device->SetCameraFormat(current_config.origin_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageTransferProcessor::SetConfig(
|
||||||
|
Core::IrSensor::PackedImageTransferProcessorExConfig config) {
|
||||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
||||||
|
current_config.camera_config.gain = config.camera_config.gain;
|
||||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
|
||||||
|
current_config.camera_config.light_target =
|
||||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
|
||||||
|
current_config.origin_format =
|
||||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.origin_format);
|
||||||
|
current_config.trimming_format =
|
||||||
|
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(config.trimming_format);
|
||||||
|
current_config.trimming_start_x = config.trimming_start_x;
|
||||||
|
current_config.trimming_start_y = config.trimming_start_y;
|
||||||
|
|
||||||
|
npad_device->SetCameraFormat(current_config.origin_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImageTransferProcessor::SetTransferMemoryPointer(u8* t_mem) {
|
||||||
|
is_transfer_memory_set = true;
|
||||||
|
transfer_memory = t_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
|
||||||
|
std::vector<u8>& data) const {
|
||||||
|
const auto size = GetDataSize(current_config.trimming_format);
|
||||||
|
data.resize(size);
|
||||||
|
memcpy(data.data(), transfer_memory, size);
|
||||||
|
return processor_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
73
src/core/hle/service/hid/irsensor/image_transfer_processor.h
Executable file
73
src/core/hle/service/hid/irsensor/image_transfer_processor.h
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Core::HID {
|
||||||
|
class EmulatedController;
|
||||||
|
} // namespace Core::HID
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class ImageTransferProcessor final : public ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit ImageTransferProcessor(Core::HID::HIDCore& hid_core_,
|
||||||
|
Core::IrSensor::DeviceFormat& device_format,
|
||||||
|
std::size_t npad_index);
|
||||||
|
~ImageTransferProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is initialized
|
||||||
|
void StartProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is suspended
|
||||||
|
void SuspendProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is stopped
|
||||||
|
void StopProcessor() override;
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetConfig(Core::IrSensor::PackedImageTransferProcessorConfig config);
|
||||||
|
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
|
||||||
|
|
||||||
|
// Transfer memory where the image data will be stored
|
||||||
|
void SetTransferMemoryPointer(u8* t_mem);
|
||||||
|
|
||||||
|
Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is nn::irsensor::ImageTransferProcessorConfig
|
||||||
|
struct ImageTransferProcessorConfig {
|
||||||
|
Core::IrSensor::CameraConfig camera_config;
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat format;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ImageTransferProcessorConfig) == 0x20,
|
||||||
|
"ImageTransferProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::ImageTransferProcessorExConfig
|
||||||
|
struct ImageTransferProcessorExConfig {
|
||||||
|
Core::IrSensor::CameraConfig camera_config;
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat origin_format;
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat trimming_format;
|
||||||
|
u16 trimming_start_x;
|
||||||
|
u16 trimming_start_y;
|
||||||
|
bool is_external_light_filter_enabled;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ImageTransferProcessorExConfig) == 0x28,
|
||||||
|
"ImageTransferProcessorExConfig is an invalid size");
|
||||||
|
|
||||||
|
void OnControllerUpdate(Core::HID::ControllerTriggerType type);
|
||||||
|
|
||||||
|
ImageTransferProcessorExConfig current_config{};
|
||||||
|
Core::IrSensor::ImageTransferProcessorState processor_state{};
|
||||||
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
Core::HID::EmulatedController* npad_device;
|
||||||
|
int callback_key{};
|
||||||
|
|
||||||
|
u8* transfer_memory = nullptr;
|
||||||
|
bool is_transfer_memory_set = false;
|
||||||
|
};
|
||||||
|
} // namespace Service::IRS
|
27
src/core/hle/service/hid/irsensor/ir_led_processor.cpp
Executable file
27
src/core/hle/service/hid/irsensor/ir_led_processor.cpp
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/irsensor/ir_led_processor.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
IrLedProcessor::IrLedProcessor(Core::IrSensor::DeviceFormat& device_format)
|
||||||
|
: device(device_format) {
|
||||||
|
device.mode = Core::IrSensor::IrSensorMode::IrLedProcessor;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
IrLedProcessor::~IrLedProcessor() = default;
|
||||||
|
|
||||||
|
void IrLedProcessor::StartProcessor() {}
|
||||||
|
|
||||||
|
void IrLedProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
|
void IrLedProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void IrLedProcessor::SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config) {
|
||||||
|
current_config.light_target =
|
||||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.light_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
47
src/core/hle/service/hid/irsensor/ir_led_processor.h
Executable file
47
src/core/hle/service/hid/irsensor/ir_led_processor.h
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/bit_field.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class IrLedProcessor final : public ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit IrLedProcessor(Core::IrSensor::DeviceFormat& device_format);
|
||||||
|
~IrLedProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is initialized
|
||||||
|
void StartProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is suspended
|
||||||
|
void SuspendProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is stopped
|
||||||
|
void StopProcessor() override;
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetConfig(Core::IrSensor::PackedIrLedProcessorConfig config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is nn::irsensor::IrLedProcessorConfig
|
||||||
|
struct IrLedProcessorConfig {
|
||||||
|
Core::IrSensor::CameraLightTarget light_target;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IrLedProcessorConfig) == 0x4, "IrLedProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
struct IrLedProcessorState {
|
||||||
|
s64 sampling_number;
|
||||||
|
u64 timestamp;
|
||||||
|
std::array<u8, 0x8> data;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(IrLedProcessorState) == 0x18, "IrLedProcessorState is an invalid size");
|
||||||
|
|
||||||
|
IrLedProcessorConfig current_config{};
|
||||||
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
34
src/core/hle/service/hid/irsensor/moment_processor.cpp
Executable file
34
src/core/hle/service/hid/irsensor/moment_processor.cpp
Executable file
|
@ -0,0 +1,34 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/irsensor/moment_processor.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
MomentProcessor::MomentProcessor(Core::IrSensor::DeviceFormat& device_format)
|
||||||
|
: device(device_format) {
|
||||||
|
device.mode = Core::IrSensor::IrSensorMode::MomentProcessor;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
MomentProcessor::~MomentProcessor() = default;
|
||||||
|
|
||||||
|
void MomentProcessor::StartProcessor() {}
|
||||||
|
|
||||||
|
void MomentProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
|
void MomentProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig config) {
|
||||||
|
current_config.camera_config.exposure_time = config.camera_config.exposure_time;
|
||||||
|
current_config.camera_config.gain = config.camera_config.gain;
|
||||||
|
current_config.camera_config.is_negative_used = config.camera_config.is_negative_used;
|
||||||
|
current_config.camera_config.light_target =
|
||||||
|
static_cast<Core::IrSensor::CameraLightTarget>(config.camera_config.light_target);
|
||||||
|
current_config.window_of_interest = config.window_of_interest;
|
||||||
|
current_config.preprocess =
|
||||||
|
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
|
||||||
|
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
61
src/core/hle/service/hid/irsensor/moment_processor.h
Executable file
61
src/core/hle/service/hid/irsensor/moment_processor.h
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/bit_field.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class MomentProcessor final : public ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit MomentProcessor(Core::IrSensor::DeviceFormat& device_format);
|
||||||
|
~MomentProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is initialized
|
||||||
|
void StartProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is suspended
|
||||||
|
void SuspendProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is stopped
|
||||||
|
void StopProcessor() override;
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetConfig(Core::IrSensor::PackedMomentProcessorConfig config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is nn::irsensor::MomentProcessorConfig
|
||||||
|
struct MomentProcessorConfig {
|
||||||
|
Core::IrSensor::CameraConfig camera_config;
|
||||||
|
Core::IrSensor::IrsRect window_of_interest;
|
||||||
|
Core::IrSensor::MomentProcessorPreprocess preprocess;
|
||||||
|
u32 preprocess_intensity_threshold;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MomentProcessorConfig) == 0x28,
|
||||||
|
"MomentProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::MomentStatistic
|
||||||
|
struct MomentStatistic {
|
||||||
|
f32 average_intensity;
|
||||||
|
Core::IrSensor::IrsCentroid centroid;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MomentStatistic) == 0xC, "MomentStatistic is an invalid size");
|
||||||
|
|
||||||
|
// This is nn::irsensor::MomentProcessorState
|
||||||
|
struct MomentProcessorState {
|
||||||
|
s64 sampling_number;
|
||||||
|
u64 timestamp;
|
||||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
|
||||||
|
INSERT_PADDING_BYTES(4);
|
||||||
|
std::array<MomentStatistic, 0x30> stadistic;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MomentProcessorState) == 0x258, "MomentProcessorState is an invalid size");
|
||||||
|
|
||||||
|
MomentProcessorConfig current_config{};
|
||||||
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
26
src/core/hle/service/hid/irsensor/pointing_processor.cpp
Executable file
26
src/core/hle/service/hid/irsensor/pointing_processor.cpp
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/irsensor/pointing_processor.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
PointingProcessor::PointingProcessor(Core::IrSensor::DeviceFormat& device_format)
|
||||||
|
: device(device_format) {
|
||||||
|
device.mode = Core::IrSensor::IrSensorMode::PointingProcessorMarker;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
PointingProcessor::~PointingProcessor() = default;
|
||||||
|
|
||||||
|
void PointingProcessor::StartProcessor() {}
|
||||||
|
|
||||||
|
void PointingProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
|
void PointingProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void PointingProcessor::SetConfig(Core::IrSensor::PackedPointingProcessorConfig config) {
|
||||||
|
current_config.window_of_interest = config.window_of_interest;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
61
src/core/hle/service/hid/irsensor/pointing_processor.h
Executable file
61
src/core/hle/service/hid/irsensor/pointing_processor.h
Executable file
|
@ -0,0 +1,61 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class PointingProcessor final : public ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit PointingProcessor(Core::IrSensor::DeviceFormat& device_format);
|
||||||
|
~PointingProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is initialized
|
||||||
|
void StartProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is suspended
|
||||||
|
void SuspendProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is stopped
|
||||||
|
void StopProcessor() override;
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetConfig(Core::IrSensor::PackedPointingProcessorConfig config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is nn::irsensor::PointingProcessorConfig
|
||||||
|
struct PointingProcessorConfig {
|
||||||
|
Core::IrSensor::IrsRect window_of_interest;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PointingProcessorConfig) == 0x8,
|
||||||
|
"PointingProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
struct PointingProcessorMarkerData {
|
||||||
|
u8 pointing_status;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
u32 unknown;
|
||||||
|
float unkown_float1;
|
||||||
|
float position_x;
|
||||||
|
float position_y;
|
||||||
|
float unkown_float2;
|
||||||
|
Core::IrSensor::IrsRect window_of_interest;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PointingProcessorMarkerData) == 0x20,
|
||||||
|
"PointingProcessorMarkerData is an invalid size");
|
||||||
|
|
||||||
|
struct PointingProcessorMarkerState {
|
||||||
|
s64 sampling_number;
|
||||||
|
u64 timestamp;
|
||||||
|
std::array<PointingProcessorMarkerData, 0x3> data;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PointingProcessorMarkerState) == 0x70,
|
||||||
|
"PointingProcessorMarkerState is an invalid size");
|
||||||
|
|
||||||
|
PointingProcessorConfig current_config{};
|
||||||
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
67
src/core/hle/service/hid/irsensor/processor_base.cpp
Executable file
67
src/core/hle/service/hid/irsensor/processor_base.cpp
Executable file
|
@ -0,0 +1,67 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
|
||||||
|
ProcessorBase::ProcessorBase() {}
|
||||||
|
ProcessorBase::~ProcessorBase() = default;
|
||||||
|
|
||||||
|
bool ProcessorBase::IsProcessorActive() const {
|
||||||
|
return is_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ProcessorBase::GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const {
|
||||||
|
switch (format) {
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
|
||||||
|
return 320 * 240;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
|
||||||
|
return 160 * 120;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
|
||||||
|
return 80 * 60;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
|
||||||
|
return 40 * 30;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
|
||||||
|
return 20 * 15;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ProcessorBase::GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const {
|
||||||
|
switch (format) {
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
|
||||||
|
return 320;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
|
||||||
|
return 160;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
|
||||||
|
return 80;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
|
||||||
|
return 40;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
|
||||||
|
return 20;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ProcessorBase::GetDataHeight(
|
||||||
|
Core::IrSensor::ImageTransferProcessorFormat format) const {
|
||||||
|
switch (format) {
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size320x240:
|
||||||
|
return 240;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size160x120:
|
||||||
|
return 120;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size80x60:
|
||||||
|
return 60;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size40x30:
|
||||||
|
return 30;
|
||||||
|
case Core::IrSensor::ImageTransferProcessorFormat::Size20x15:
|
||||||
|
return 15;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
33
src/core/hle/service/hid/irsensor/processor_base.h
Executable file
33
src/core/hle/service/hid/irsensor/processor_base.h
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit ProcessorBase();
|
||||||
|
virtual ~ProcessorBase();
|
||||||
|
|
||||||
|
virtual void StartProcessor() = 0;
|
||||||
|
virtual void SuspendProcessor() = 0;
|
||||||
|
virtual void StopProcessor() = 0;
|
||||||
|
|
||||||
|
bool IsProcessorActive() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Returns the number of bytes the image uses
|
||||||
|
std::size_t GetDataSize(Core::IrSensor::ImageTransferProcessorFormat format) const;
|
||||||
|
|
||||||
|
/// Returns the width of the image
|
||||||
|
std::size_t GetDataWidth(Core::IrSensor::ImageTransferProcessorFormat format) const;
|
||||||
|
|
||||||
|
/// Returns the height of the image
|
||||||
|
std::size_t GetDataHeight(Core::IrSensor::ImageTransferProcessorFormat format) const;
|
||||||
|
|
||||||
|
bool is_active{false};
|
||||||
|
};
|
||||||
|
} // namespace Service::IRS
|
29
src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
Executable file
29
src/core/hle/service/hid/irsensor/tera_plugin_processor.cpp
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "core/hle/service/hid/irsensor/tera_plugin_processor.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
TeraPluginProcessor::TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format)
|
||||||
|
: device(device_format) {
|
||||||
|
device.mode = Core::IrSensor::IrSensorMode::TeraPluginProcessor;
|
||||||
|
device.camera_status = Core::IrSensor::IrCameraStatus::Unconnected;
|
||||||
|
device.camera_internal_status = Core::IrSensor::IrCameraInternalStatus::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeraPluginProcessor::~TeraPluginProcessor() = default;
|
||||||
|
|
||||||
|
void TeraPluginProcessor::StartProcessor() {}
|
||||||
|
|
||||||
|
void TeraPluginProcessor::SuspendProcessor() {}
|
||||||
|
|
||||||
|
void TeraPluginProcessor::StopProcessor() {}
|
||||||
|
|
||||||
|
void TeraPluginProcessor::SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config) {
|
||||||
|
current_config.mode = config.mode;
|
||||||
|
current_config.unknown_1 = config.unknown_1;
|
||||||
|
current_config.unknown_2 = config.unknown_2;
|
||||||
|
current_config.unknown_3 = config.unknown_3;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
53
src/core/hle/service/hid/irsensor/tera_plugin_processor.h
Executable file
53
src/core/hle/service/hid/irsensor/tera_plugin_processor.h
Executable file
|
@ -0,0 +1,53 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/bit_field.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hid/irs_types.h"
|
||||||
|
#include "core/hle/service/hid/irsensor/processor_base.h"
|
||||||
|
|
||||||
|
namespace Service::IRS {
|
||||||
|
class TeraPluginProcessor final : public ProcessorBase {
|
||||||
|
public:
|
||||||
|
explicit TeraPluginProcessor(Core::IrSensor::DeviceFormat& device_format);
|
||||||
|
~TeraPluginProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is initialized
|
||||||
|
void StartProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is suspended
|
||||||
|
void SuspendProcessor() override;
|
||||||
|
|
||||||
|
// Called when the processor is stopped
|
||||||
|
void StopProcessor() override;
|
||||||
|
|
||||||
|
// Sets config parameters of the camera
|
||||||
|
void SetConfig(Core::IrSensor::PackedTeraPluginProcessorConfig config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is nn::irsensor::TeraPluginProcessorConfig
|
||||||
|
struct TeraPluginProcessorConfig {
|
||||||
|
u8 mode;
|
||||||
|
u8 unknown_1;
|
||||||
|
u8 unknown_2;
|
||||||
|
u8 unknown_3;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TeraPluginProcessorConfig) == 0x4,
|
||||||
|
"TeraPluginProcessorConfig is an invalid size");
|
||||||
|
|
||||||
|
struct TeraPluginProcessorState {
|
||||||
|
s64 sampling_number;
|
||||||
|
u64 timestamp;
|
||||||
|
Core::IrSensor::CameraAmbientNoiseLevel ambient_noise_level;
|
||||||
|
std::array<u8, 0x12c> data;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(TeraPluginProcessorState) == 0x140,
|
||||||
|
"TeraPluginProcessorState is an invalid size");
|
||||||
|
|
||||||
|
TeraPluginProcessorConfig current_config{};
|
||||||
|
Core::IrSensor::DeviceFormat& device;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Service::IRS
|
|
@ -1,4 +1,6 @@
|
||||||
add_library(input_common STATIC
|
add_library(input_common STATIC
|
||||||
|
drivers/camera.cpp
|
||||||
|
drivers/camera.h
|
||||||
drivers/gc_adapter.cpp
|
drivers/gc_adapter.cpp
|
||||||
drivers/gc_adapter.h
|
drivers/gc_adapter.h
|
||||||
drivers/keyboard.cpp
|
drivers/keyboard.cpp
|
||||||
|
|
82
src/input_common/drivers/camera.cpp
Executable file
82
src/input_common/drivers/camera.cpp
Executable file
|
@ -0,0 +1,82 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "common/param_package.h"
|
||||||
|
#include "input_common/drivers/camera.h"
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
constexpr PadIdentifier identifier = {
|
||||||
|
.guid = Common::UUID{},
|
||||||
|
.port = 0,
|
||||||
|
.pad = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
|
||||||
|
PreSetController(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) {
|
||||||
|
const std::size_t desired_width = getImageWidth();
|
||||||
|
const std::size_t desired_height = getImageHeight();
|
||||||
|
status.data.resize(desired_width * desired_height);
|
||||||
|
|
||||||
|
// Resize image to desired format
|
||||||
|
for (std::size_t y = 0; y < desired_height; y++) {
|
||||||
|
for (std::size_t x = 0; x < desired_width; x++) {
|
||||||
|
const std::size_t pixel_index = y * desired_width + x;
|
||||||
|
const std::size_t old_x = width * x / desired_width;
|
||||||
|
const std::size_t old_y = height * y / desired_height;
|
||||||
|
const std::size_t data_pixel_index = old_y * width + old_x;
|
||||||
|
status.data[pixel_index] = static_cast<u8>(data[data_pixel_index] & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetCamera(identifier, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Camera::getImageWidth() const {
|
||||||
|
switch (status.format) {
|
||||||
|
case Common::Input::CameraFormat::Size320x240:
|
||||||
|
return 320;
|
||||||
|
case Common::Input::CameraFormat::Size160x120:
|
||||||
|
return 160;
|
||||||
|
case Common::Input::CameraFormat::Size80x60:
|
||||||
|
return 80;
|
||||||
|
case Common::Input::CameraFormat::Size40x30:
|
||||||
|
return 40;
|
||||||
|
case Common::Input::CameraFormat::Size20x15:
|
||||||
|
return 20;
|
||||||
|
case Common::Input::CameraFormat::None:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Camera::getImageHeight() const {
|
||||||
|
switch (status.format) {
|
||||||
|
case Common::Input::CameraFormat::Size320x240:
|
||||||
|
return 240;
|
||||||
|
case Common::Input::CameraFormat::Size160x120:
|
||||||
|
return 120;
|
||||||
|
case Common::Input::CameraFormat::Size80x60:
|
||||||
|
return 60;
|
||||||
|
case Common::Input::CameraFormat::Size40x30:
|
||||||
|
return 30;
|
||||||
|
case Common::Input::CameraFormat::Size20x15:
|
||||||
|
return 15;
|
||||||
|
case Common::Input::CameraFormat::None:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraError Camera::SetCameraFormat(
|
||||||
|
[[maybe_unused]] const PadIdentifier& identifier_,
|
||||||
|
const Common::Input::CameraFormat camera_format) {
|
||||||
|
status.format = camera_format;
|
||||||
|
return Common::Input::CameraError::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace InputCommon
|
29
src/input_common/drivers/camera.h
Executable file
29
src/input_common/drivers/camera.h
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "input_common/input_engine.h"
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A button device factory representing a keyboard. It receives keyboard events and forward them
|
||||||
|
* to all button devices it created.
|
||||||
|
*/
|
||||||
|
class Camera final : public InputEngine {
|
||||||
|
public:
|
||||||
|
explicit Camera(std::string input_engine_);
|
||||||
|
|
||||||
|
void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data);
|
||||||
|
|
||||||
|
std::size_t getImageWidth() const;
|
||||||
|
std::size_t getImageHeight() const;
|
||||||
|
|
||||||
|
Common::Input::CameraError SetCameraFormat(const PadIdentifier& identifier_,
|
||||||
|
Common::Input::CameraFormat camera_format) override;
|
||||||
|
|
||||||
|
Common::Input::CameraStatus status{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace InputCommon
|
|
@ -90,6 +90,18 @@ void InputEngine::SetMotion(const PadIdentifier& identifier, int motion, const B
|
||||||
TriggerOnMotionChange(identifier, motion, value);
|
TriggerOnMotionChange(identifier, motion, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEngine::SetCamera(const PadIdentifier& identifier,
|
||||||
|
const Common::Input::CameraStatus& value) {
|
||||||
|
{
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
ControllerData& controller = controller_list.at(identifier);
|
||||||
|
if (!configuring) {
|
||||||
|
controller.camera = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TriggerOnCameraChange(identifier, value);
|
||||||
|
}
|
||||||
|
|
||||||
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
bool InputEngine::GetButton(const PadIdentifier& identifier, int button) const {
|
||||||
std::scoped_lock lock{mutex};
|
std::scoped_lock lock{mutex};
|
||||||
const auto controller_iter = controller_list.find(identifier);
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
|
@ -165,6 +177,18 @@ BasicMotion InputEngine::GetMotion(const PadIdentifier& identifier, int motion)
|
||||||
return controller.motions.at(motion);
|
return controller.motions.at(motion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraStatus InputEngine::GetCamera(const PadIdentifier& identifier) const {
|
||||||
|
std::scoped_lock lock{mutex};
|
||||||
|
const auto controller_iter = controller_list.find(identifier);
|
||||||
|
if (controller_iter == controller_list.cend()) {
|
||||||
|
LOG_ERROR(Input, "Invalid identifier guid={}, pad={}, port={}", identifier.guid.RawString(),
|
||||||
|
identifier.pad, identifier.port);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const ControllerData& controller = controller_iter->second;
|
||||||
|
return controller.camera;
|
||||||
|
}
|
||||||
|
|
||||||
void InputEngine::ResetButtonState() {
|
void InputEngine::ResetButtonState() {
|
||||||
for (const auto& controller : controller_list) {
|
for (const auto& controller : controller_list) {
|
||||||
for (const auto& button : controller.second.buttons) {
|
for (const auto& button : controller.second.buttons) {
|
||||||
|
@ -317,6 +341,20 @@ void InputEngine::TriggerOnMotionChange(const PadIdentifier& identifier, int mot
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputEngine::TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||||
|
[[maybe_unused]] const Common::Input::CameraStatus& value) {
|
||||||
|
std::scoped_lock lock{mutex_callback};
|
||||||
|
for (const auto& poller_pair : callback_list) {
|
||||||
|
const InputIdentifier& poller = poller_pair.second;
|
||||||
|
if (!IsInputIdentifierEqual(poller, identifier, EngineInputType::Camera, 0)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (poller.callback.on_change) {
|
||||||
|
poller.callback.on_change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
bool InputEngine::IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||||
const PadIdentifier& identifier, EngineInputType type,
|
const PadIdentifier& identifier, EngineInputType type,
|
||||||
int index) const {
|
int index) const {
|
||||||
|
|
|
@ -36,11 +36,12 @@ struct BasicMotion {
|
||||||
// Types of input that are stored in the engine
|
// Types of input that are stored in the engine
|
||||||
enum class EngineInputType {
|
enum class EngineInputType {
|
||||||
None,
|
None,
|
||||||
Button,
|
|
||||||
HatButton,
|
|
||||||
Analog,
|
Analog,
|
||||||
Motion,
|
|
||||||
Battery,
|
Battery,
|
||||||
|
Button,
|
||||||
|
Camera,
|
||||||
|
HatButton,
|
||||||
|
Motion,
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
@ -115,10 +116,17 @@ public:
|
||||||
// Sets polling mode to a controller
|
// Sets polling mode to a controller
|
||||||
virtual Common::Input::PollingError SetPollingMode(
|
virtual Common::Input::PollingError SetPollingMode(
|
||||||
[[maybe_unused]] const PadIdentifier& identifier,
|
[[maybe_unused]] const PadIdentifier& identifier,
|
||||||
[[maybe_unused]] const Common::Input::PollingMode vibration) {
|
[[maybe_unused]] const Common::Input::PollingMode polling_mode) {
|
||||||
return Common::Input::PollingError::NotSupported;
|
return Common::Input::PollingError::NotSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets camera format to a controller
|
||||||
|
virtual Common::Input::CameraError SetCameraFormat(
|
||||||
|
[[maybe_unused]] const PadIdentifier& identifier,
|
||||||
|
[[maybe_unused]] Common::Input::CameraFormat camera_format) {
|
||||||
|
return Common::Input::CameraError::NotSupported;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the engine name
|
// Returns the engine name
|
||||||
[[nodiscard]] const std::string& GetEngineName() const;
|
[[nodiscard]] const std::string& GetEngineName() const;
|
||||||
|
|
||||||
|
@ -174,6 +182,7 @@ public:
|
||||||
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
f32 GetAxis(const PadIdentifier& identifier, int axis) const;
|
||||||
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
Common::Input::BatteryLevel GetBattery(const PadIdentifier& identifier) const;
|
||||||
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
BasicMotion GetMotion(const PadIdentifier& identifier, int motion) const;
|
||||||
|
Common::Input::CameraStatus GetCamera(const PadIdentifier& identifier) const;
|
||||||
|
|
||||||
int SetCallback(InputIdentifier input_identifier);
|
int SetCallback(InputIdentifier input_identifier);
|
||||||
void SetMappingCallback(MappingCallback callback);
|
void SetMappingCallback(MappingCallback callback);
|
||||||
|
@ -185,6 +194,7 @@ protected:
|
||||||
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
void SetAxis(const PadIdentifier& identifier, int axis, f32 value);
|
||||||
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
void SetBattery(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||||
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
void SetMotion(const PadIdentifier& identifier, int motion, const BasicMotion& value);
|
||||||
|
void SetCamera(const PadIdentifier& identifier, const Common::Input::CameraStatus& value);
|
||||||
|
|
||||||
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
virtual std::string GetHatButtonName([[maybe_unused]] u8 direction_value) const {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
|
@ -197,6 +207,7 @@ private:
|
||||||
std::unordered_map<int, float> axes;
|
std::unordered_map<int, float> axes;
|
||||||
std::unordered_map<int, BasicMotion> motions;
|
std::unordered_map<int, BasicMotion> motions;
|
||||||
Common::Input::BatteryLevel battery{};
|
Common::Input::BatteryLevel battery{};
|
||||||
|
Common::Input::CameraStatus camera{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
void TriggerOnButtonChange(const PadIdentifier& identifier, int button, bool value);
|
||||||
|
@ -205,6 +216,8 @@ private:
|
||||||
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
void TriggerOnBatteryChange(const PadIdentifier& identifier, Common::Input::BatteryLevel value);
|
||||||
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
void TriggerOnMotionChange(const PadIdentifier& identifier, int motion,
|
||||||
const BasicMotion& value);
|
const BasicMotion& value);
|
||||||
|
void TriggerOnCameraChange(const PadIdentifier& identifier,
|
||||||
|
const Common::Input::CameraStatus& value);
|
||||||
|
|
||||||
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
bool IsInputIdentifierEqual(const InputIdentifier& input_identifier,
|
||||||
const PadIdentifier& identifier, EngineInputType type,
|
const PadIdentifier& identifier, EngineInputType type,
|
||||||
|
|
|
@ -664,6 +664,47 @@ private:
|
||||||
InputEngine* input_engine;
|
InputEngine* input_engine;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InputFromCamera final : public Common::Input::InputDevice {
|
||||||
|
public:
|
||||||
|
explicit InputFromCamera(PadIdentifier identifier_, InputEngine* input_engine_)
|
||||||
|
: identifier(identifier_), input_engine(input_engine_) {
|
||||||
|
UpdateCallback engine_callback{[this]() { OnChange(); }};
|
||||||
|
const InputIdentifier input_identifier{
|
||||||
|
.identifier = identifier,
|
||||||
|
.type = EngineInputType::Camera,
|
||||||
|
.index = 0,
|
||||||
|
.callback = engine_callback,
|
||||||
|
};
|
||||||
|
callback_key = input_engine->SetCallback(input_identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
~InputFromCamera() override {
|
||||||
|
input_engine->DeleteCallback(callback_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraStatus GetStatus() const {
|
||||||
|
return input_engine->GetCamera(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForceUpdate() override {
|
||||||
|
OnChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnChange() {
|
||||||
|
const Common::Input::CallbackStatus status{
|
||||||
|
.type = Common::Input::InputType::IrSensor,
|
||||||
|
.camera_status = GetStatus(),
|
||||||
|
};
|
||||||
|
|
||||||
|
TriggerOnChange(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const PadIdentifier identifier;
|
||||||
|
int callback_key;
|
||||||
|
InputEngine* input_engine;
|
||||||
|
};
|
||||||
|
|
||||||
class OutputFromIdentifier final : public Common::Input::OutputDevice {
|
class OutputFromIdentifier final : public Common::Input::OutputDevice {
|
||||||
public:
|
public:
|
||||||
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
|
explicit OutputFromIdentifier(PadIdentifier identifier_, InputEngine* input_engine_)
|
||||||
|
@ -682,6 +723,10 @@ public:
|
||||||
return input_engine->SetPollingMode(identifier, polling_mode);
|
return input_engine->SetPollingMode(identifier, polling_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Common::Input::CameraError SetCameraFormat(Common::Input::CameraFormat camera_format) override {
|
||||||
|
return input_engine->SetCameraFormat(identifier, camera_format);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const PadIdentifier identifier;
|
const PadIdentifier identifier;
|
||||||
InputEngine* input_engine;
|
InputEngine* input_engine;
|
||||||
|
@ -920,6 +965,18 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateMotionDevice(
|
||||||
properties_y, properties_z, input_engine.get());
|
properties_y, properties_z, input_engine.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateCameraDevice(
|
||||||
|
const Common::ParamPackage& params) {
|
||||||
|
const PadIdentifier identifier = {
|
||||||
|
.guid = Common::UUID{params.Get("guid", "")},
|
||||||
|
.port = static_cast<std::size_t>(params.Get("port", 0)),
|
||||||
|
.pad = static_cast<std::size_t>(params.Get("pad", 0)),
|
||||||
|
};
|
||||||
|
|
||||||
|
input_engine->PreSetController(identifier);
|
||||||
|
return std::make_unique<InputFromCamera>(identifier, input_engine.get());
|
||||||
|
}
|
||||||
|
|
||||||
InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
|
InputFactory::InputFactory(std::shared_ptr<InputEngine> input_engine_)
|
||||||
: input_engine(std::move(input_engine_)) {}
|
: input_engine(std::move(input_engine_)) {}
|
||||||
|
|
||||||
|
@ -928,6 +985,9 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::Create(
|
||||||
if (params.Has("battery")) {
|
if (params.Has("battery")) {
|
||||||
return CreateBatteryDevice(params);
|
return CreateBatteryDevice(params);
|
||||||
}
|
}
|
||||||
|
if (params.Has("camera")) {
|
||||||
|
return CreateCameraDevice(params);
|
||||||
|
}
|
||||||
if (params.Has("button") && params.Has("axis")) {
|
if (params.Has("button") && params.Has("axis")) {
|
||||||
return CreateTriggerDevice(params);
|
return CreateTriggerDevice(params);
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,6 +211,17 @@ private:
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
std::unique_ptr<Common::Input::InputDevice> CreateMotionDevice(Common::ParamPackage params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a camera device from the parameters given.
|
||||||
|
* @param params contains parameters for creating the device:
|
||||||
|
* - "guid": text string for identifying controllers
|
||||||
|
* - "port": port of the connected device
|
||||||
|
* - "pad": slot of the connected controller
|
||||||
|
* @returns a unique input device with the parameters specified
|
||||||
|
*/
|
||||||
|
std::unique_ptr<Common::Input::InputDevice> CreateCameraDevice(
|
||||||
|
const Common::ParamPackage& params);
|
||||||
|
|
||||||
std::shared_ptr<InputEngine> input_engine;
|
std::shared_ptr<InputEngine> input_engine;
|
||||||
};
|
};
|
||||||
} // namespace InputCommon
|
} // namespace InputCommon
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "common/input.h"
|
#include "common/input.h"
|
||||||
#include "common/param_package.h"
|
#include "common/param_package.h"
|
||||||
|
#include "input_common/drivers/camera.h"
|
||||||
#include "input_common/drivers/gc_adapter.h"
|
#include "input_common/drivers/gc_adapter.h"
|
||||||
#include "input_common/drivers/keyboard.h"
|
#include "input_common/drivers/keyboard.h"
|
||||||
#include "input_common/drivers/mouse.h"
|
#include "input_common/drivers/mouse.h"
|
||||||
|
@ -78,6 +79,15 @@ struct InputSubsystem::Impl {
|
||||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
|
Common::Input::RegisterFactory<Common::Input::OutputDevice>(tas_input->GetEngineName(),
|
||||||
tas_output_factory);
|
tas_output_factory);
|
||||||
|
|
||||||
|
camera = std::make_shared<Camera>("camera");
|
||||||
|
camera->SetMappingCallback(mapping_callback);
|
||||||
|
camera_input_factory = std::make_shared<InputFactory>(camera);
|
||||||
|
camera_output_factory = std::make_shared<OutputFactory>(camera);
|
||||||
|
Common::Input::RegisterFactory<Common::Input::InputDevice>(camera->GetEngineName(),
|
||||||
|
camera_input_factory);
|
||||||
|
Common::Input::RegisterFactory<Common::Input::OutputDevice>(camera->GetEngineName(),
|
||||||
|
camera_output_factory);
|
||||||
|
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
sdl = std::make_shared<SDLDriver>("sdl");
|
sdl = std::make_shared<SDLDriver>("sdl");
|
||||||
sdl->SetMappingCallback(mapping_callback);
|
sdl->SetMappingCallback(mapping_callback);
|
||||||
|
@ -317,6 +327,7 @@ struct InputSubsystem::Impl {
|
||||||
std::shared_ptr<TouchScreen> touch_screen;
|
std::shared_ptr<TouchScreen> touch_screen;
|
||||||
std::shared_ptr<TasInput::Tas> tas_input;
|
std::shared_ptr<TasInput::Tas> tas_input;
|
||||||
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
std::shared_ptr<CemuhookUDP::UDPClient> udp_client;
|
||||||
|
std::shared_ptr<Camera> camera;
|
||||||
|
|
||||||
std::shared_ptr<InputFactory> keyboard_factory;
|
std::shared_ptr<InputFactory> keyboard_factory;
|
||||||
std::shared_ptr<InputFactory> mouse_factory;
|
std::shared_ptr<InputFactory> mouse_factory;
|
||||||
|
@ -324,12 +335,14 @@ struct InputSubsystem::Impl {
|
||||||
std::shared_ptr<InputFactory> touch_screen_factory;
|
std::shared_ptr<InputFactory> touch_screen_factory;
|
||||||
std::shared_ptr<InputFactory> udp_client_input_factory;
|
std::shared_ptr<InputFactory> udp_client_input_factory;
|
||||||
std::shared_ptr<InputFactory> tas_input_factory;
|
std::shared_ptr<InputFactory> tas_input_factory;
|
||||||
|
std::shared_ptr<InputFactory> camera_input_factory;
|
||||||
|
|
||||||
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
std::shared_ptr<OutputFactory> keyboard_output_factory;
|
||||||
std::shared_ptr<OutputFactory> mouse_output_factory;
|
std::shared_ptr<OutputFactory> mouse_output_factory;
|
||||||
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
std::shared_ptr<OutputFactory> gcadapter_output_factory;
|
||||||
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
std::shared_ptr<OutputFactory> udp_client_output_factory;
|
||||||
std::shared_ptr<OutputFactory> tas_output_factory;
|
std::shared_ptr<OutputFactory> tas_output_factory;
|
||||||
|
std::shared_ptr<OutputFactory> camera_output_factory;
|
||||||
|
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
std::shared_ptr<SDLDriver> sdl;
|
std::shared_ptr<SDLDriver> sdl;
|
||||||
|
@ -382,6 +395,14 @@ const TasInput::Tas* InputSubsystem::GetTas() const {
|
||||||
return impl->tas_input.get();
|
return impl->tas_input.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Camera* InputSubsystem::GetCamera() {
|
||||||
|
return impl->camera.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Camera* InputSubsystem::GetCamera() const {
|
||||||
|
return impl->camera.get();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
|
||||||
return impl->GetInputDevices();
|
return impl->GetInputDevices();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ enum Values : int;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace InputCommon {
|
namespace InputCommon {
|
||||||
|
class Camera;
|
||||||
class Keyboard;
|
class Keyboard;
|
||||||
class Mouse;
|
class Mouse;
|
||||||
class TouchScreen;
|
class TouchScreen;
|
||||||
|
@ -92,9 +93,15 @@ public:
|
||||||
/// Retrieves the underlying tas input device.
|
/// Retrieves the underlying tas input device.
|
||||||
[[nodiscard]] TasInput::Tas* GetTas();
|
[[nodiscard]] TasInput::Tas* GetTas();
|
||||||
|
|
||||||
/// Retrieves the underlying tas input device.
|
/// Retrieves the underlying tas input device.
|
||||||
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
[[nodiscard]] const TasInput::Tas* GetTas() const;
|
||||||
|
|
||||||
|
/// Retrieves the underlying camera input device.
|
||||||
|
[[nodiscard]] Camera* GetCamera();
|
||||||
|
|
||||||
|
/// Retrieves the underlying camera input device.
|
||||||
|
[[nodiscard]] const Camera* GetCamera() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all available input devices that this Factory can create a new device with.
|
* Returns all available input devices that this Factory can create a new device with.
|
||||||
* Each returned ParamPackage should have a `display` field used for display, a `engine` field
|
* Each returned ParamPackage should have a `display` field used for display, a `engine` field
|
||||||
|
|
|
@ -41,6 +41,9 @@ add_executable(yuzu
|
||||||
configuration/configure_audio.cpp
|
configuration/configure_audio.cpp
|
||||||
configuration/configure_audio.h
|
configuration/configure_audio.h
|
||||||
configuration/configure_audio.ui
|
configuration/configure_audio.ui
|
||||||
|
configuration/configure_camera.cpp
|
||||||
|
configuration/configure_camera.h
|
||||||
|
configuration/configure_camera.ui
|
||||||
configuration/configure_cpu.cpp
|
configuration/configure_cpu.cpp
|
||||||
configuration/configure_cpu.h
|
configuration/configure_cpu.h
|
||||||
configuration/configure_cpu.ui
|
configuration/configure_cpu.ui
|
||||||
|
@ -254,7 +257,7 @@ endif()
|
||||||
create_target_directory_groups(yuzu)
|
create_target_directory_groups(yuzu)
|
||||||
|
|
||||||
target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
target_link_libraries(yuzu PRIVATE common core input_common video_core)
|
||||||
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets)
|
target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets Qt::Multimedia)
|
||||||
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads)
|
||||||
|
|
||||||
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QCameraImageCapture>
|
||||||
|
#include <QCameraInfo>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
@ -31,6 +33,7 @@
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/cpu_manager.h"
|
#include "core/cpu_manager.h"
|
||||||
#include "core/frontend/framebuffer_layout.h"
|
#include "core/frontend/framebuffer_layout.h"
|
||||||
|
#include "input_common/drivers/camera.h"
|
||||||
#include "input_common/drivers/keyboard.h"
|
#include "input_common/drivers/keyboard.h"
|
||||||
#include "input_common/drivers/mouse.h"
|
#include "input_common/drivers/mouse.h"
|
||||||
#include "input_common/drivers/tas_input.h"
|
#include "input_common/drivers/tas_input.h"
|
||||||
|
@ -801,6 +804,74 @@ void GRenderWindow::TouchEndEvent() {
|
||||||
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
input_subsystem->GetTouchScreen()->ReleaseAllTouch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::InitializeCamera() {
|
||||||
|
if (!Settings::values.enable_ir_sensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool camera_found = false;
|
||||||
|
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
|
||||||
|
for (const QCameraInfo& cameraInfo : cameras) {
|
||||||
|
if (Settings::values.ir_sensor_device.GetValue() == cameraInfo.deviceName().toStdString() ||
|
||||||
|
Settings::values.ir_sensor_device.GetValue() == "Auto") {
|
||||||
|
camera = std::make_unique<QCamera>(cameraInfo);
|
||||||
|
camera_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!camera_found) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
|
||||||
|
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
|
||||||
|
&GRenderWindow::OnCameraCapture);
|
||||||
|
camera->unload();
|
||||||
|
camera->setCaptureMode(QCamera::CaptureViewfinder);
|
||||||
|
camera->load();
|
||||||
|
|
||||||
|
camera_timer = std::make_unique<QTimer>();
|
||||||
|
connect(camera_timer.get(), &QTimer::timeout, [this] { RequestCameraCapture(); });
|
||||||
|
// This timer should be dependent of camera resolution 5ms for every 100 pixels
|
||||||
|
camera_timer->start(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::FinalizeCamera() {
|
||||||
|
if (camera_timer) {
|
||||||
|
camera_timer->stop();
|
||||||
|
}
|
||||||
|
if (camera) {
|
||||||
|
camera->unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::RequestCameraCapture() {
|
||||||
|
if (!Settings::values.enable_ir_sensor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Idealy one should only call capture but Qt refuses to take a second capture without
|
||||||
|
// stopping the camera
|
||||||
|
camera->stop();
|
||||||
|
camera->start();
|
||||||
|
|
||||||
|
camera_capture->capture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) {
|
||||||
|
constexpr std::size_t camera_width = 320;
|
||||||
|
constexpr std::size_t camera_height = 240;
|
||||||
|
const auto converted =
|
||||||
|
img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||||
|
Qt::TransformationMode::SmoothTransformation)
|
||||||
|
.mirrored(false, true);
|
||||||
|
std::vector<u32> camera_data{};
|
||||||
|
camera_data.resize(camera_width * camera_height);
|
||||||
|
std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32));
|
||||||
|
input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data);
|
||||||
|
}
|
||||||
|
|
||||||
bool GRenderWindow::event(QEvent* event) {
|
bool GRenderWindow::event(QEvent* event) {
|
||||||
if (event->type() == QEvent::TouchBegin) {
|
if (event->type() == QEvent::TouchBegin) {
|
||||||
TouchBeginEvent(static_cast<QTouchEvent*>(event));
|
TouchBeginEvent(static_cast<QTouchEvent*>(event));
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
class GRenderWindow;
|
class GRenderWindow;
|
||||||
class GMainWindow;
|
class GMainWindow;
|
||||||
|
class QCamera;
|
||||||
|
class QCameraImageCapture;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -164,6 +166,9 @@ public:
|
||||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||||
void wheelEvent(QWheelEvent* event) override;
|
void wheelEvent(QWheelEvent* event) override;
|
||||||
|
|
||||||
|
void InitializeCamera();
|
||||||
|
void FinalizeCamera();
|
||||||
|
|
||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
|
|
||||||
void focusOutEvent(QFocusEvent* event) override;
|
void focusOutEvent(QFocusEvent* event) override;
|
||||||
|
@ -207,6 +212,9 @@ private:
|
||||||
void TouchUpdateEvent(const QTouchEvent* event);
|
void TouchUpdateEvent(const QTouchEvent* event);
|
||||||
void TouchEndEvent();
|
void TouchEndEvent();
|
||||||
|
|
||||||
|
void RequestCameraCapture();
|
||||||
|
void OnCameraCapture(int requestId, const QImage& img);
|
||||||
|
|
||||||
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
|
||||||
|
|
||||||
bool InitializeOpenGL();
|
bool InitializeOpenGL();
|
||||||
|
@ -232,6 +240,10 @@ private:
|
||||||
bool first_frame = false;
|
bool first_frame = false;
|
||||||
InputCommon::TasInput::TasState last_tas_state;
|
InputCommon::TasInput::TasState last_tas_state;
|
||||||
|
|
||||||
|
std::unique_ptr<QCamera> camera;
|
||||||
|
std::unique_ptr<QCameraImageCapture> camera_capture;
|
||||||
|
std::unique_ptr<QTimer> camera_timer;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -143,8 +143,8 @@ void Config::ReadBasicSetting(Settings::Setting<std::string>& setting) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void Config::ReadBasicSetting(Settings::Setting<Type>& setting) {
|
void Config::ReadBasicSetting(Settings::Setting<Type, ranged>& setting) {
|
||||||
const QString name = QString::fromStdString(setting.GetLabel());
|
const QString name = QString::fromStdString(setting.GetLabel());
|
||||||
const Type default_value = setting.GetDefault();
|
const Type default_value = setting.GetDefault();
|
||||||
if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
|
if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
|
||||||
|
@ -164,16 +164,16 @@ void Config::WriteBasicSetting(const Settings::Setting<std::string>& setting) {
|
||||||
qt_config->setValue(name, QString::fromStdString(value));
|
qt_config->setValue(name, QString::fromStdString(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void Config::WriteBasicSetting(const Settings::Setting<Type>& setting) {
|
void Config::WriteBasicSetting(const Settings::Setting<Type, ranged>& setting) {
|
||||||
const QString name = QString::fromStdString(setting.GetLabel());
|
const QString name = QString::fromStdString(setting.GetLabel());
|
||||||
const Type value = setting.GetValue();
|
const Type value = setting.GetValue();
|
||||||
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault());
|
||||||
qt_config->setValue(name, value);
|
qt_config->setValue(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting) {
|
void Config::WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting) {
|
||||||
const QString name = QString::fromStdString(setting.GetLabel());
|
const QString name = QString::fromStdString(setting.GetLabel());
|
||||||
const Type& value = setting.GetValue(global);
|
const Type& value = setting.GetValue(global);
|
||||||
if (!global) {
|
if (!global) {
|
||||||
|
@ -368,6 +368,11 @@ void Config::ReadHidbusValues() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::ReadIrCameraValues() {
|
||||||
|
ReadBasicSetting(Settings::values.enable_ir_sensor);
|
||||||
|
ReadBasicSetting(Settings::values.ir_sensor_device);
|
||||||
|
}
|
||||||
|
|
||||||
void Config::ReadAudioValues() {
|
void Config::ReadAudioValues() {
|
||||||
qt_config->beginGroup(QStringLiteral("Audio"));
|
qt_config->beginGroup(QStringLiteral("Audio"));
|
||||||
|
|
||||||
|
@ -393,6 +398,7 @@ void Config::ReadControlValues() {
|
||||||
ReadTouchscreenValues();
|
ReadTouchscreenValues();
|
||||||
ReadMotionTouchValues();
|
ReadMotionTouchValues();
|
||||||
ReadHidbusValues();
|
ReadHidbusValues();
|
||||||
|
ReadIrCameraValues();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
ReadBasicSetting(Settings::values.enable_raw_input);
|
ReadBasicSetting(Settings::values.enable_raw_input);
|
||||||
|
@ -998,6 +1004,11 @@ void Config::SaveHidbusValues() {
|
||||||
QString::fromStdString(default_param));
|
QString::fromStdString(default_param));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Config::SaveIrCameraValues() {
|
||||||
|
WriteBasicSetting(Settings::values.enable_ir_sensor);
|
||||||
|
WriteBasicSetting(Settings::values.ir_sensor_device);
|
||||||
|
}
|
||||||
|
|
||||||
void Config::SaveValues() {
|
void Config::SaveValues() {
|
||||||
if (global) {
|
if (global) {
|
||||||
SaveControlValues();
|
SaveControlValues();
|
||||||
|
@ -1040,6 +1051,7 @@ void Config::SaveControlValues() {
|
||||||
SaveTouchscreenValues();
|
SaveTouchscreenValues();
|
||||||
SaveMotionTouchValues();
|
SaveMotionTouchValues();
|
||||||
SaveHidbusValues();
|
SaveHidbusValues();
|
||||||
|
SaveIrCameraValues();
|
||||||
|
|
||||||
WriteGlobalSetting(Settings::values.use_docked_mode);
|
WriteGlobalSetting(Settings::values.use_docked_mode);
|
||||||
WriteGlobalSetting(Settings::values.vibration_enabled);
|
WriteGlobalSetting(Settings::values.vibration_enabled);
|
||||||
|
@ -1413,8 +1425,8 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting) {
|
void Config::ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting) {
|
||||||
QString name = QString::fromStdString(setting.GetLabel());
|
QString name = QString::fromStdString(setting.GetLabel());
|
||||||
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool();
|
||||||
setting.SetGlobal(use_global);
|
setting.SetGlobal(use_global);
|
||||||
|
|
|
@ -68,6 +68,7 @@ private:
|
||||||
void ReadTouchscreenValues();
|
void ReadTouchscreenValues();
|
||||||
void ReadMotionTouchValues();
|
void ReadMotionTouchValues();
|
||||||
void ReadHidbusValues();
|
void ReadHidbusValues();
|
||||||
|
void ReadIrCameraValues();
|
||||||
|
|
||||||
// Read functions bases off the respective config section names.
|
// Read functions bases off the respective config section names.
|
||||||
void ReadAudioValues();
|
void ReadAudioValues();
|
||||||
|
@ -96,6 +97,7 @@ private:
|
||||||
void SaveTouchscreenValues();
|
void SaveTouchscreenValues();
|
||||||
void SaveMotionTouchValues();
|
void SaveMotionTouchValues();
|
||||||
void SaveHidbusValues();
|
void SaveHidbusValues();
|
||||||
|
void SaveIrCameraValues();
|
||||||
|
|
||||||
// Save functions based off the respective config section names.
|
// Save functions based off the respective config section names.
|
||||||
void SaveAudioValues();
|
void SaveAudioValues();
|
||||||
|
@ -159,8 +161,8 @@ private:
|
||||||
*
|
*
|
||||||
* @param The setting
|
* @param The setting
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void ReadGlobalSetting(Settings::SwitchableSetting<Type>& setting);
|
void ReadGlobalSetting(Settings::SwitchableSetting<Type, ranged>& setting);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a value to the qt_config using the setting's label and default value. If the config is a
|
* Sets a value to the qt_config using the setting's label and default value. If the config is a
|
||||||
|
@ -168,8 +170,8 @@ private:
|
||||||
*
|
*
|
||||||
* @param The setting
|
* @param The setting
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void WriteGlobalSetting(const Settings::SwitchableSetting<Type>& setting);
|
void WriteGlobalSetting(const Settings::SwitchableSetting<Type, ranged>& setting);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a value from the qt_config using the setting's label and default value and applies the
|
* Reads a value from the qt_config using the setting's label and default value and applies the
|
||||||
|
@ -177,15 +179,15 @@ private:
|
||||||
*
|
*
|
||||||
* @param The setting
|
* @param The setting
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void ReadBasicSetting(Settings::Setting<Type>& setting);
|
void ReadBasicSetting(Settings::Setting<Type, ranged>& setting);
|
||||||
|
|
||||||
/** Sets a value from the setting in the qt_config using the setting's label and default value.
|
/** Sets a value from the setting in the qt_config using the setting's label and default value.
|
||||||
*
|
*
|
||||||
* @param The setting
|
* @param The setting
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void WriteBasicSetting(const Settings::Setting<Type>& setting);
|
void WriteBasicSetting(const Settings::Setting<Type, ranged>& setting);
|
||||||
|
|
||||||
ConfigType type;
|
ConfigType type;
|
||||||
std::unique_ptr<QSettings> qt_config;
|
std::unique_ptr<QSettings> qt_config;
|
||||||
|
|
|
@ -27,8 +27,9 @@ enum class CheckState {
|
||||||
// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
|
// ApplyPerGameSetting, given a Settings::Setting and a Qt UI element, properly applies a Setting
|
||||||
void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
|
void ApplyPerGameSetting(Settings::SwitchableSetting<bool>* setting, const QCheckBox* checkbox,
|
||||||
const CheckState& tracker);
|
const CheckState& tracker);
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void ApplyPerGameSetting(Settings::SwitchableSetting<Type>* setting, const QComboBox* combobox) {
|
void ApplyPerGameSetting(Settings::SwitchableSetting<Type, ranged>* setting,
|
||||||
|
const QComboBox* combobox) {
|
||||||
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) {
|
||||||
setting->SetValue(static_cast<Type>(combobox->currentIndex()));
|
setting->SetValue(static_cast<Type>(combobox->currentIndex()));
|
||||||
} else if (!Settings::IsConfiguringGlobal()) {
|
} else if (!Settings::IsConfiguringGlobal()) {
|
||||||
|
@ -45,8 +46,9 @@ void ApplyPerGameSetting(Settings::SwitchableSetting<Type>* setting, const QComb
|
||||||
// Sets a Qt UI element given a Settings::Setting
|
// Sets a Qt UI element given a Settings::Setting
|
||||||
void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
|
void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting<bool>* setting);
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void SetPerGameSetting(QComboBox* combobox, const Settings::SwitchableSetting<Type>* setting) {
|
void SetPerGameSetting(QComboBox* combobox,
|
||||||
|
const Settings::SwitchableSetting<Type, ranged>* setting) {
|
||||||
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
|
combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX
|
||||||
: static_cast<int>(setting->GetValue()) +
|
: static_cast<int>(setting->GetValue()) +
|
||||||
ConfigurationShared::USE_GLOBAL_OFFSET);
|
ConfigurationShared::USE_GLOBAL_OFFSET);
|
||||||
|
|
126
src/yuzu/configuration/configure_camera.cpp
Executable file
126
src/yuzu/configuration/configure_camera.cpp
Executable file
|
@ -0,0 +1,126 @@
|
||||||
|
// Text : Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QCameraImageCapture>
|
||||||
|
#include <QCameraInfo>
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "input_common/drivers/camera.h"
|
||||||
|
#include "input_common/main.h"
|
||||||
|
#include "ui_configure_camera.h"
|
||||||
|
#include "yuzu/configuration/config.h"
|
||||||
|
#include "yuzu/configuration/configure_camera.h"
|
||||||
|
|
||||||
|
ConfigureCamera::ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_)
|
||||||
|
: QDialog(parent), input_subsystem{input_subsystem_},
|
||||||
|
ui(std::make_unique<Ui::ConfigureCamera>()) {
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
connect(ui->restore_defaults_button, &QPushButton::clicked, this,
|
||||||
|
&ConfigureCamera::RestoreDefaults);
|
||||||
|
connect(ui->preview_button, &QPushButton::clicked, this, &ConfigureCamera::PreviewCamera);
|
||||||
|
|
||||||
|
auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32);
|
||||||
|
blank_image.fill(Qt::black);
|
||||||
|
DisplayCapturedFrame(0, blank_image);
|
||||||
|
|
||||||
|
LoadConfiguration();
|
||||||
|
resize(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureCamera::~ConfigureCamera() = default;
|
||||||
|
|
||||||
|
void ConfigureCamera::PreviewCamera() {
|
||||||
|
const auto index = ui->ir_sensor_combo_box->currentIndex();
|
||||||
|
bool camera_found = false;
|
||||||
|
const QList<QCameraInfo> cameras = QCameraInfo::availableCameras();
|
||||||
|
for (const QCameraInfo& cameraInfo : cameras) {
|
||||||
|
if (input_devices[index] == cameraInfo.deviceName().toStdString() ||
|
||||||
|
input_devices[index] == "Auto") {
|
||||||
|
LOG_INFO(Frontend, "Selected Camera {} {}", cameraInfo.description().toStdString(),
|
||||||
|
cameraInfo.deviceName().toStdString());
|
||||||
|
camera = std::make_unique<QCamera>(cameraInfo);
|
||||||
|
camera_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear previous frame
|
||||||
|
auto blank_image = QImage(320, 240, QImage::Format::Format_RGB32);
|
||||||
|
blank_image.fill(Qt::black);
|
||||||
|
DisplayCapturedFrame(0, blank_image);
|
||||||
|
|
||||||
|
if (!camera_found) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
camera_capture = std::make_unique<QCameraImageCapture>(camera.get());
|
||||||
|
connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this,
|
||||||
|
&ConfigureCamera::DisplayCapturedFrame);
|
||||||
|
camera->unload();
|
||||||
|
camera->setCaptureMode(QCamera::CaptureViewfinder);
|
||||||
|
camera->load();
|
||||||
|
|
||||||
|
camera_timer = std::make_unique<QTimer>();
|
||||||
|
connect(camera_timer.get(), &QTimer::timeout, [this] {
|
||||||
|
camera->stop();
|
||||||
|
camera->start();
|
||||||
|
|
||||||
|
camera_capture->capture();
|
||||||
|
});
|
||||||
|
|
||||||
|
camera_timer->start(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCamera::DisplayCapturedFrame(int requestId, const QImage& img) {
|
||||||
|
LOG_INFO(Frontend, "ImageCaptured {} {}", img.width(), img.height());
|
||||||
|
const auto converted = img.scaled(320, 240, Qt::AspectRatioMode::IgnoreAspectRatio,
|
||||||
|
Qt::TransformationMode::SmoothTransformation);
|
||||||
|
ui->preview_box->setPixmap(QPixmap::fromImage(converted));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCamera::changeEvent(QEvent* event) {
|
||||||
|
if (event->type() == QEvent::LanguageChange) {
|
||||||
|
RetranslateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
QDialog::changeEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCamera::RetranslateUI() {
|
||||||
|
ui->retranslateUi(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCamera::ApplyConfiguration() {
|
||||||
|
const auto index = ui->ir_sensor_combo_box->currentIndex();
|
||||||
|
Settings::values.ir_sensor_device.SetValue(input_devices[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCamera::LoadConfiguration() {
|
||||||
|
input_devices.clear();
|
||||||
|
ui->ir_sensor_combo_box->clear();
|
||||||
|
input_devices.push_back("Auto");
|
||||||
|
ui->ir_sensor_combo_box->addItem(tr("Auto"));
|
||||||
|
const auto cameras = QCameraInfo::availableCameras();
|
||||||
|
for (const QCameraInfo& cameraInfo : cameras) {
|
||||||
|
input_devices.push_back(cameraInfo.deviceName().toStdString());
|
||||||
|
ui->ir_sensor_combo_box->addItem(cameraInfo.description());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto current_device = Settings::values.ir_sensor_device.GetValue();
|
||||||
|
|
||||||
|
const auto devices_it = std::find_if(
|
||||||
|
input_devices.begin(), input_devices.end(),
|
||||||
|
[current_device](const std::string& device) { return device == current_device; });
|
||||||
|
const int device_index =
|
||||||
|
devices_it != input_devices.end()
|
||||||
|
? static_cast<int>(std::distance(input_devices.begin(), devices_it))
|
||||||
|
: 0;
|
||||||
|
ui->ir_sensor_combo_box->setCurrentIndex(device_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigureCamera::RestoreDefaults() {
|
||||||
|
ui->ir_sensor_combo_box->setCurrentIndex(0);
|
||||||
|
}
|
52
src/yuzu/configuration/configure_camera.h
Executable file
52
src/yuzu/configuration/configure_camera.h
Executable file
|
@ -0,0 +1,52 @@
|
||||||
|
// Text : Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QTimer;
|
||||||
|
class QCamera;
|
||||||
|
class QCameraImageCapture;
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
class InputSubsystem;
|
||||||
|
} // namespace InputCommon
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ConfigureCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConfigureCamera : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ConfigureCamera(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
|
||||||
|
~ConfigureCamera() override;
|
||||||
|
|
||||||
|
void ApplyConfiguration();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void changeEvent(QEvent* event) override;
|
||||||
|
void RetranslateUI();
|
||||||
|
|
||||||
|
/// Load configuration settings.
|
||||||
|
void LoadConfiguration();
|
||||||
|
|
||||||
|
/// Restore all buttons to their default values.
|
||||||
|
void RestoreDefaults();
|
||||||
|
|
||||||
|
void DisplayCapturedFrame(int requestId, const QImage& img);
|
||||||
|
|
||||||
|
/// Loads and signals the current selected camera to display a frame
|
||||||
|
void PreviewCamera();
|
||||||
|
|
||||||
|
InputCommon::InputSubsystem* input_subsystem;
|
||||||
|
|
||||||
|
std::unique_ptr<QCamera> camera;
|
||||||
|
std::unique_ptr<QCameraImageCapture> camera_capture;
|
||||||
|
std::unique_ptr<QTimer> camera_timer;
|
||||||
|
std::vector<std::string> input_devices;
|
||||||
|
std::unique_ptr<Ui::ConfigureCamera> ui;
|
||||||
|
};
|
170
src/yuzu/configuration/configure_camera.ui
Executable file
170
src/yuzu/configuration/configure_camera.ui
Executable file
|
@ -0,0 +1,170 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ConfigureCamera</class>
|
||||||
|
<widget class="QDialog" name="ConfigureCamera">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>298</width>
|
||||||
|
<height>339</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Configure Infrared Camera</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>280</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Select where the image of the emulated camera comes from. It may be a virtual camera or a real camera.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="gridGroupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Camera Image Source:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
|
<item row="0" column="0">
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Input device:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QComboBox" name="ir_sensor_combo_box"/>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3">
|
||||||
|
<spacer name="horizontalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item><item>
|
||||||
|
<widget class="QGroupBox" name="previewBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Preview</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="preview_box">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>320</width>
|
||||||
|
<height>240</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Resolution: 320*240</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="preview_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Click to preview</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="restore_defaults_button">
|
||||||
|
<property name="text">
|
||||||
|
<string>Restore Defaults</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>ConfigureCamera</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>ConfigureCamera</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
|
@ -15,6 +15,7 @@
|
||||||
#include "ui_configure_input.h"
|
#include "ui_configure_input.h"
|
||||||
#include "ui_configure_input_advanced.h"
|
#include "ui_configure_input_advanced.h"
|
||||||
#include "ui_configure_input_player.h"
|
#include "ui_configure_input_player.h"
|
||||||
|
#include "yuzu/configuration/configure_camera.h"
|
||||||
#include "yuzu/configuration/configure_debug_controller.h"
|
#include "yuzu/configuration/configure_debug_controller.h"
|
||||||
#include "yuzu/configuration/configure_input.h"
|
#include "yuzu/configuration/configure_input.h"
|
||||||
#include "yuzu/configuration/configure_input_advanced.h"
|
#include "yuzu/configuration/configure_input_advanced.h"
|
||||||
|
@ -163,6 +164,10 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
||||||
[this, input_subsystem, &hid_core] {
|
[this, input_subsystem, &hid_core] {
|
||||||
CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
|
CallConfigureDialog<ConfigureRingController>(*this, input_subsystem, hid_core);
|
||||||
});
|
});
|
||||||
|
connect(advanced, &ConfigureInputAdvanced::CallCameraDialog,
|
||||||
|
[this, input_subsystem, &hid_core] {
|
||||||
|
CallConfigureDialog<ConfigureCamera>(*this, input_subsystem);
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->vibrationButton, &QPushButton::clicked,
|
connect(ui->vibrationButton, &QPushButton::clicked,
|
||||||
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
|
[this, &hid_core] { CallConfigureDialog<ConfigureVibration>(*this, hid_core); });
|
||||||
|
|
|
@ -89,6 +89,7 @@ ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
|
||||||
[this] { CallMotionTouchConfigDialog(); });
|
[this] { CallMotionTouchConfigDialog(); });
|
||||||
connect(ui->ring_controller_configure, &QPushButton::clicked, this,
|
connect(ui->ring_controller_configure, &QPushButton::clicked, this,
|
||||||
[this] { CallRingControllerDialog(); });
|
[this] { CallRingControllerDialog(); });
|
||||||
|
connect(ui->camera_configure, &QPushButton::clicked, this, [this] { CallCameraDialog(); });
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
ui->enable_raw_input->setVisible(false);
|
ui->enable_raw_input->setVisible(false);
|
||||||
|
@ -136,6 +137,7 @@ void ConfigureInputAdvanced::ApplyConfiguration() {
|
||||||
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
|
Settings::values.enable_udp_controller = ui->enable_udp_controller->isChecked();
|
||||||
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
|
Settings::values.controller_navigation = ui->controller_navigation->isChecked();
|
||||||
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
|
Settings::values.enable_ring_controller = ui->enable_ring_controller->isChecked();
|
||||||
|
Settings::values.enable_ir_sensor = ui->enable_ir_sensor->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureInputAdvanced::LoadConfiguration() {
|
void ConfigureInputAdvanced::LoadConfiguration() {
|
||||||
|
@ -169,6 +171,7 @@ void ConfigureInputAdvanced::LoadConfiguration() {
|
||||||
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
|
ui->enable_udp_controller->setChecked(Settings::values.enable_udp_controller.GetValue());
|
||||||
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
|
ui->controller_navigation->setChecked(Settings::values.controller_navigation.GetValue());
|
||||||
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
|
ui->enable_ring_controller->setChecked(Settings::values.enable_ring_controller.GetValue());
|
||||||
|
ui->enable_ir_sensor->setChecked(Settings::values.enable_ir_sensor.GetValue());
|
||||||
|
|
||||||
UpdateUIEnabled();
|
UpdateUIEnabled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ signals:
|
||||||
void CallTouchscreenConfigDialog();
|
void CallTouchscreenConfigDialog();
|
||||||
void CallMotionTouchConfigDialog();
|
void CallMotionTouchConfigDialog();
|
||||||
void CallRingControllerDialog();
|
void CallRingControllerDialog();
|
||||||
|
void CallCameraDialog();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void changeEvent(QEvent* event) override;
|
void changeEvent(QEvent* event) override;
|
||||||
|
|
|
@ -2617,6 +2617,20 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QCheckBox" name="enable_ir_sensor">
|
||||||
|
<property name="text">
|
||||||
|
<string>Infrared Camera</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="2">
|
||||||
|
<widget class="QPushButton" name="camera_configure">
|
||||||
|
<property name="text">
|
||||||
|
<string>Configure</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -1540,6 +1540,8 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t
|
||||||
mouse_hide_timer.start();
|
mouse_hide_timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_window->InitializeCamera();
|
||||||
|
|
||||||
std::string title_name;
|
std::string title_name;
|
||||||
std::string title_version;
|
std::string title_version;
|
||||||
const auto res = system->GetGameName(title_name);
|
const auto res = system->GetGameName(title_name);
|
||||||
|
@ -1621,6 +1623,7 @@ void GMainWindow::ShutdownGame() {
|
||||||
tas_label->clear();
|
tas_label->clear();
|
||||||
input_subsystem->GetTas()->Stop();
|
input_subsystem->GetTas()->Stop();
|
||||||
OnTasStateChanged();
|
OnTasStateChanged();
|
||||||
|
render_window->FinalizeCamera();
|
||||||
|
|
||||||
// Enable all controllers
|
// Enable all controllers
|
||||||
system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
system->HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
|
||||||
|
@ -2860,6 +2863,12 @@ void GMainWindow::OnConfigure() {
|
||||||
mouse_hide_timer.start();
|
mouse_hide_timer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restart camera config
|
||||||
|
if (emulation_running) {
|
||||||
|
render_window->FinalizeCamera();
|
||||||
|
render_window->InitializeCamera();
|
||||||
|
}
|
||||||
|
|
||||||
if (!UISettings::values.has_broken_vulkan) {
|
if (!UISettings::values.has_broken_vulkan) {
|
||||||
renderer_status_button->setEnabled(!emulation_running);
|
renderer_status_button->setEnabled(!emulation_running);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,8 +99,8 @@ void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& sett
|
||||||
setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
|
setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void Config::ReadSetting(const std::string& group, Settings::Setting<Type>& setting) {
|
void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
|
||||||
setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
|
setting = static_cast<Type>(sdl2_config->GetInteger(group, setting.GetLabel(),
|
||||||
static_cast<long>(setting.GetDefault())));
|
static_cast<long>(setting.GetDefault())));
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,6 @@ private:
|
||||||
* @param group The name of the INI group
|
* @param group The name of the INI group
|
||||||
* @param setting The yuzu setting to modify
|
* @param setting The yuzu setting to modify
|
||||||
*/
|
*/
|
||||||
template <typename Type>
|
template <typename Type, bool ranged>
|
||||||
void ReadSetting(const std::string& group, Settings::Setting<Type>& setting);
|
void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue