mirror of
https://git.suyu.dev/suyu/suyu.git
synced 2024-11-23 15:22:45 +01:00
Merge remote-tracking branch 'upstream/master' into int-flags
This commit is contained in:
commit
7a3c884e39
912 changed files with 91129 additions and 25508 deletions
|
@ -15,5 +15,5 @@ mv "${REV_NAME}-source.tar.xz" $RELEASE_NAME
|
||||||
7z a "$REV_NAME.7z" $RELEASE_NAME
|
7z a "$REV_NAME.7z" $RELEASE_NAME
|
||||||
|
|
||||||
# move the compiled archive into the artifacts directory to be uploaded by travis releases
|
# move the compiled archive into the artifacts directory to be uploaded by travis releases
|
||||||
mv "$ARCHIVE_NAME" artifacts/
|
mv "$ARCHIVE_NAME" "${ARTIFACTS_DIR}/"
|
||||||
mv "$REV_NAME.7z" artifacts/
|
mv "$REV_NAME.7z" "${ARTIFACTS_DIR}/"
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
|
|
||||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||||
GITREV="`git show -s --format='%h'`"
|
GITREV="`git show -s --format='%h'`"
|
||||||
|
ARTIFACTS_DIR="artifacts"
|
||||||
|
|
||||||
mkdir -p artifacts
|
mkdir -p "${ARTIFACTS_DIR}/"
|
||||||
|
|
|
@ -1,14 +1,54 @@
|
||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
# Exit on error, rather than continuing with the rest of the script.
|
||||||
|
set -e
|
||||||
|
|
||||||
cd /yuzu
|
cd /yuzu
|
||||||
|
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
mkdir build || true && cd build
|
mkdir build || true && cd build
|
||||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON
|
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||||
|
|
||||||
ninja
|
make -j$(nproc)
|
||||||
|
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
ctest -VV -C Release
|
ctest -VV -C Release
|
||||||
|
|
||||||
|
make install DESTDIR=AppDir
|
||||||
|
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||||
|
|
||||||
|
# Download tools needed to build an AppImage
|
||||||
|
wget -nc https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
|
||||||
|
wget -nc https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage
|
||||||
|
wget -nc https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||||
|
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/AppRun-patched-x86_64
|
||||||
|
wget -nc https://github.com/darealshinji/AppImageKit-checkrt/releases/download/continuous/exec-x86_64.so
|
||||||
|
# Set executable bit
|
||||||
|
chmod 755 \
|
||||||
|
appimagetool-x86_64.AppImage \
|
||||||
|
AppRun-patched-x86_64 \
|
||||||
|
exec-x86_64.so \
|
||||||
|
linuxdeploy-x86_64.AppImage \
|
||||||
|
linuxdeploy-plugin-qt-x86_64.AppImage
|
||||||
|
|
||||||
|
# Workaround for https://github.com/AppImage/AppImageKit/issues/828
|
||||||
|
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||||
|
|
||||||
|
mkdir -p AppDir/usr/optional
|
||||||
|
mkdir -p AppDir/usr/optional/libstdc++
|
||||||
|
mkdir -p AppDir/usr/optional/libgcc_s
|
||||||
|
|
||||||
|
# Deploy yuzu's needed dependencies
|
||||||
|
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt
|
||||||
|
|
||||||
|
# Workaround for building yuzu with GCC 10 but also trying to distribute it to Ubuntu 18.04 et al.
|
||||||
|
# See https://github.com/darealshinji/AppImageKit-checkrt
|
||||||
|
cp exec-x86_64.so AppDir/usr/optional/exec.so
|
||||||
|
cp AppRun-patched-x86_64 AppDir/AppRun
|
||||||
|
cp --dereference /usr/lib/x86_64-linux-gnu/libstdc++.so.6 AppDir/usr/optional/libstdc++/libstdc++.so.6
|
||||||
|
cp --dereference /lib/x86_64-linux-gnu/libgcc_s.so.1 AppDir/usr/optional/libgcc_s/libgcc_s.so.1
|
||||||
|
|
||||||
|
# Build the AppImage
|
||||||
|
./appimagetool-x86_64.AppImage AppDir
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
. .ci/scripts/common/pre-upload.sh
|
. .ci/scripts/common/pre-upload.sh
|
||||||
|
|
||||||
|
APPIMAGE_NAME="yuzu-x86_64.AppImage"
|
||||||
|
NEW_APPIMAGE_NAME="yuzu-${GITDATE}-${GITREV}-x86_64.AppImage"
|
||||||
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
REV_NAME="yuzu-linux-${GITDATE}-${GITREV}"
|
||||||
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
ARCHIVE_NAME="${REV_NAME}.tar.xz"
|
||||||
COMPRESSION_FLAGS="-cJvf"
|
COMPRESSION_FLAGS="-cJvf"
|
||||||
|
@ -17,4 +19,7 @@ mkdir "$DIR_NAME"
|
||||||
cp build/bin/yuzu-cmd "$DIR_NAME"
|
cp build/bin/yuzu-cmd "$DIR_NAME"
|
||||||
cp build/bin/yuzu "$DIR_NAME"
|
cp build/bin/yuzu "$DIR_NAME"
|
||||||
|
|
||||||
|
# Copy the AppImage to the artifacts directory and avoid compressing it
|
||||||
|
cp "build/${APPIMAGE_NAME}" "${ARTIFACTS_DIR}/${NEW_APPIMAGE_NAME}"
|
||||||
|
|
||||||
. .ci/scripts/common/post-upload.sh
|
. .ci/scripts/common/post-upload.sh
|
||||||
|
|
|
@ -5,7 +5,7 @@ cd /yuzu
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
||||||
mkdir build || true && cd build
|
mkdir build || true && cd build
|
||||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
|
@ -4,9 +4,11 @@ parameters:
|
||||||
version: ''
|
version: ''
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- script: choco install vulkan-sdk
|
||||||
|
displayName: 'Install vulkan-sdk'
|
||||||
- script: python -m pip install --upgrade pip conan
|
- script: python -m pip install --upgrade pip conan
|
||||||
displayName: 'Install conan'
|
displayName: 'Install conan'
|
||||||
- script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
- script: refreshenv && mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
||||||
displayName: 'Configure CMake'
|
displayName: 'Configure CMake'
|
||||||
- task: MSBuild@1
|
- task: MSBuild@1
|
||||||
displayName: 'Build'
|
displayName: 'Build'
|
||||||
|
|
|
@ -9,6 +9,7 @@ stages:
|
||||||
displayName: 'build'
|
displayName: 'build'
|
||||||
jobs:
|
jobs:
|
||||||
- job: build
|
- job: build
|
||||||
|
timeoutInMinutes: 120
|
||||||
displayName: 'windows-msvc'
|
displayName: 'windows-msvc'
|
||||||
pool:
|
pool:
|
||||||
vmImage: windows-2019
|
vmImage: windows-2019
|
||||||
|
|
5
.gitmodules
vendored
5
.gitmodules
vendored
|
@ -1,15 +1,12 @@
|
||||||
[submodule "inih"]
|
[submodule "inih"]
|
||||||
path = externals/inih/inih
|
path = externals/inih/inih
|
||||||
url = https://github.com/svn2github/inih
|
url = https://github.com/benhoyt/inih.git
|
||||||
[submodule "cubeb"]
|
[submodule "cubeb"]
|
||||||
path = externals/cubeb
|
path = externals/cubeb
|
||||||
url = https://github.com/kinetiknz/cubeb.git
|
url = https://github.com/kinetiknz/cubeb.git
|
||||||
[submodule "dynarmic"]
|
[submodule "dynarmic"]
|
||||||
path = externals/dynarmic
|
path = externals/dynarmic
|
||||||
url = https://github.com/MerryMage/dynarmic.git
|
url = https://github.com/MerryMage/dynarmic.git
|
||||||
[submodule "unicorn"]
|
|
||||||
path = externals/unicorn
|
|
||||||
url = https://github.com/yuzu-emu/unicorn
|
|
||||||
[submodule "soundtouch"]
|
[submodule "soundtouch"]
|
||||||
path = externals/soundtouch
|
path = externals/soundtouch
|
||||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||||
|
|
|
@ -4,16 +4,8 @@ cd /yuzu
|
||||||
# override Travis CI unreasonable ccache size
|
# override Travis CI unreasonable ccache size
|
||||||
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
|
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
|
||||||
|
|
||||||
# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
|
|
||||||
mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
|
|
||||||
chmod +x /bin/uname
|
|
||||||
|
|
||||||
# Dirty hack to trick unicorn makefile into believing we have cmd
|
|
||||||
echo '' >> /bin/cmd
|
|
||||||
chmod +x /bin/cmd
|
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
# Clean up the dirty hacks
|
# Clean up the dirty hacks
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash -ex
|
#!/bin/bash -ex
|
||||||
|
|
||||||
mkdir -p "$HOME/.ccache"
|
mkdir -p "$HOME/.ccache"
|
||||||
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.travis/linux/docker.sh
|
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/home/yuzu/.ccache yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.travis/linux/docker.sh
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
cd /yuzu
|
cd /yuzu
|
||||||
|
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
cmake .. -G Ninja -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
|
||||||
ninja
|
ninja
|
||||||
|
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
|
@ -4,13 +4,12 @@ set -o pipefail
|
||||||
|
|
||||||
export MACOSX_DEPLOYMENT_TARGET=10.14
|
export MACOSX_DEPLOYMENT_TARGET=10.14
|
||||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
|
||||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||||
|
|
||||||
# TODO: Build using ninja instead of make
|
# TODO: Build using ninja instead of make
|
||||||
mkdir build && cd build
|
mkdir build && cd build
|
||||||
cmake --version
|
cmake --version
|
||||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
cmake .. -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||||
make -j4
|
make -j4
|
||||||
|
|
||||||
ccache -s
|
ccache -s
|
||||||
|
|
134
CMakeLists.txt
134
CMakeLists.txt
|
@ -18,18 +18,18 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "EN
|
||||||
|
|
||||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||||
|
|
||||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
|
||||||
|
|
||||||
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
|
||||||
|
|
||||||
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
|
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
|
||||||
|
|
||||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||||
|
|
||||||
option(ENABLE_VULKAN "Enables Vulkan backend" ON)
|
|
||||||
|
|
||||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||||
|
|
||||||
|
if (NOT ENABLE_WEB_SERVICE)
|
||||||
|
set(YUZU_ENABLE_BOXCAT OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Default to a Release build
|
# Default to a Release build
|
||||||
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
if (NOT IS_MULTI_CONFIG AND NOT CMAKE_BUILD_TYPE)
|
||||||
|
@ -115,6 +115,9 @@ if (NOT DEFINED ARCHITECTURE)
|
||||||
endif()
|
endif()
|
||||||
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
message(STATUS "Target architecture: ${ARCHITECTURE}")
|
||||||
|
|
||||||
|
if (UNIX)
|
||||||
|
add_definitions(-DYUZU_UNIX=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Configure C++ standard
|
# Configure C++ standard
|
||||||
# ===========================
|
# ===========================
|
||||||
|
@ -159,15 +162,14 @@ macro(yuzu_find_packages)
|
||||||
# Capitalization matters here. We need the naming to match the generated paths from Conan
|
# Capitalization matters here. We need the naming to match the generated paths from Conan
|
||||||
set(REQUIRED_LIBS
|
set(REQUIRED_LIBS
|
||||||
# Cmake Pkg Prefix Version Conan Pkg
|
# Cmake Pkg Prefix Version Conan Pkg
|
||||||
"Boost 1.73 boost/1.73.0"
|
|
||||||
"Catch2 2.13 catch2/2.13.0"
|
"Catch2 2.13 catch2/2.13.0"
|
||||||
"fmt 7.0 fmt/7.0.3"
|
"fmt 7.1 fmt/7.1.2"
|
||||||
# can't use until https://github.com/bincrafters/community/issues/1173
|
# can't use until https://github.com/bincrafters/community/issues/1173
|
||||||
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
|
||||||
"lz4 1.8 lz4/1.9.2"
|
"lz4 1.8 lz4/1.9.2"
|
||||||
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
"nlohmann_json 3.8 nlohmann_json/3.8.0"
|
||||||
"ZLIB 1.2 zlib/1.2.11"
|
"ZLIB 1.2 zlib/1.2.11"
|
||||||
"zstd 1.4 zstd/1.4.5"
|
"zstd 1.4 zstd/1.4.8"
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(PACKAGE ${REQUIRED_LIBS})
|
foreach(PACKAGE ${REQUIRED_LIBS})
|
||||||
|
@ -194,6 +196,22 @@ macro(yuzu_find_packages)
|
||||||
unset(FN_FORCE_REQUIRED)
|
unset(FN_FORCE_REQUIRED)
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
find_package(Boost 1.73.0 COMPONENTS context headers QUIET)
|
||||||
|
if (Boost_FOUND)
|
||||||
|
set(Boost_LIBRARIES Boost::boost)
|
||||||
|
# Conditionally add Boost::context only if the active version of the Conan or system Boost package provides it
|
||||||
|
# The old version is missing Boost::context, so we want to avoid adding in that case
|
||||||
|
# The new version requires adding Boost::context to prevent linking issues
|
||||||
|
#
|
||||||
|
# This one is used by Conan on subsequent CMake configures, not the first configure.
|
||||||
|
if (TARGET Boost::context)
|
||||||
|
list(APPEND Boost_LIBRARIES Boost::context)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||||
|
list(APPEND CONAN_REQUIRED_LIBS "boost/1.73.0")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||||
yuzu_find_packages()
|
yuzu_find_packages()
|
||||||
|
|
||||||
|
@ -225,7 +243,7 @@ if(ENABLE_QT)
|
||||||
if (YUZU_USE_QT_WEB_ENGINE)
|
if (YUZU_USE_QT_WEB_ENGINE)
|
||||||
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
find_package(Qt5 COMPONENTS WebEngineCore WebEngineWidgets)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_QT_TRANSLATION)
|
if (ENABLE_QT_TRANSLATION)
|
||||||
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
find_package(Qt5 REQUIRED COMPONENTS LinguistTools ${QT_PREFIX_HINT})
|
||||||
endif()
|
endif()
|
||||||
|
@ -263,6 +281,7 @@ if (CONAN_REQUIRED_LIBS)
|
||||||
libzip:with_openssl=False
|
libzip:with_openssl=False
|
||||||
libzip:enable_windows_crypto=False
|
libzip:enable_windows_crypto=False
|
||||||
)
|
)
|
||||||
|
|
||||||
conan_check(VERSION 1.24.0 REQUIRED)
|
conan_check(VERSION 1.24.0 REQUIRED)
|
||||||
# Add the bincrafters remote
|
# Add the bincrafters remote
|
||||||
conan_add_remote(NAME bincrafters
|
conan_add_remote(NAME bincrafters
|
||||||
|
@ -297,6 +316,17 @@ if (CONAN_REQUIRED_LIBS)
|
||||||
# this time with required, so we bail if its not found.
|
# this time with required, so we bail if its not found.
|
||||||
yuzu_find_packages(FORCE_REQUIRED)
|
yuzu_find_packages(FORCE_REQUIRED)
|
||||||
|
|
||||||
|
if (NOT Boost_FOUND)
|
||||||
|
find_package(Boost 1.73.0 REQUIRED COMPONENTS context headers)
|
||||||
|
set(Boost_LIBRARIES Boost::boost)
|
||||||
|
# Conditionally add Boost::context only if the active version of the Conan Boost package provides it
|
||||||
|
# The old version is missing Boost::context, so we want to avoid adding in that case
|
||||||
|
# The new version requires adding Boost::context to prevent linking issues
|
||||||
|
if (TARGET Boost::context)
|
||||||
|
list(APPEND Boost_LIBRARIES Boost::context)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function
|
# Due to issues with variable scopes in functions, we need to also find_package(qt5) outside of the function
|
||||||
if(ENABLE_QT)
|
if(ENABLE_QT)
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
list(APPEND CMAKE_MODULE_PATH "${CONAN_QT_ROOT_RELEASE}")
|
||||||
|
@ -354,85 +384,23 @@ if (NOT LIBUSB_FOUND)
|
||||||
set(LIBUSB_LIBRARIES usb)
|
set(LIBUSB_LIBRARIES usb)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Use system installed ffmpeg.
|
||||||
|
if (NOT MSVC)
|
||||||
|
find_package(FFmpeg REQUIRED)
|
||||||
|
else()
|
||||||
|
set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
|
||||||
|
set(FFMPEG_PATH "${CMAKE_BINARY_DIR}/externals/${FFMPEG_EXT_NAME}")
|
||||||
|
download_bundled_external("ffmpeg/" ${FFMPEG_EXT_NAME} "")
|
||||||
|
set(FFMPEG_FOUND YES)
|
||||||
|
set(FFMPEG_INCLUDE_DIR "${FFMPEG_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
|
||||||
|
set(FFMPEG_LIBRARY_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg library" FORCE)
|
||||||
|
set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Prefer the -pthread flag on Linux.
|
# Prefer the -pthread flag on Linux.
|
||||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
# If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external
|
|
||||||
if (YUZU_USE_BUNDLED_UNICORN)
|
|
||||||
if (MSVC)
|
|
||||||
message(STATUS "unicorn not found, falling back to bundled")
|
|
||||||
# Detect toolchain and platform
|
|
||||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
|
|
||||||
set(UNICORN_VER "unicorn-yuzu")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (DEFINED UNICORN_VER)
|
|
||||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (DEFINED UNICORN_VER)
|
|
||||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(UNICORN_FOUND YES)
|
|
||||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
|
||||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/lib/x64/unicorn_dynload.lib" CACHE PATH "Path to Unicorn library" FORCE)
|
|
||||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/lib/x64/" CACHE PATH "Path to unicorn.dll" FORCE)
|
|
||||||
else()
|
|
||||||
message(STATUS "unicorn not found, falling back to externals")
|
|
||||||
if (MINGW)
|
|
||||||
set(UNICORN_LIB_NAME "unicorn.a")
|
|
||||||
else()
|
|
||||||
set(UNICORN_LIB_NAME "libunicorn.a")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(UNICORN_FOUND YES)
|
|
||||||
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
|
|
||||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
|
|
||||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
|
||||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
|
|
||||||
|
|
||||||
find_package(PythonInterp 2.7 REQUIRED)
|
|
||||||
|
|
||||||
if (MINGW)
|
|
||||||
# Intentionally call the unicorn makefile directly instead of using make.sh so that we can override the
|
|
||||||
# UNAME_S makefile variable to MINGW. This way we don't have to hack at the uname binary to build
|
|
||||||
# Additionally, overriding DO_WINDOWS_EXPORT prevents unicorn from patching the static unicorn.a by using msvc and cmd,
|
|
||||||
# which are both things we don't have in a mingw cross compiling environment.
|
|
||||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc-ar RANLIB=x86_64-w64-mingw32-gcc-ranlib make UNAME_S=MINGW DO_WINDOWS_EXPORT=0
|
|
||||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
|
|
||||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ALL makes this custom target build every time
|
|
||||||
# but it won't actually build if LIBUNICORN_LIBRARY is up to date
|
|
||||||
add_custom_target(unicorn-build ALL
|
|
||||||
DEPENDS ${LIBUNICORN_LIBRARY}
|
|
||||||
)
|
|
||||||
unset(UNICORN_LIB_NAME)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
find_package(Unicorn REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (UNICORN_FOUND)
|
|
||||||
add_library(unicorn INTERFACE)
|
|
||||||
add_dependencies(unicorn unicorn-build)
|
|
||||||
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
|
|
||||||
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Could not find or build unicorn which is required.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Platform-specific library requirements
|
# Platform-specific library requirements
|
||||||
# ======================================
|
# ======================================
|
||||||
|
|
||||||
|
|
10
CMakeModules/CopyYuzuFFmpegDeps.cmake
Normal file
10
CMakeModules/CopyYuzuFFmpegDeps.cmake
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
function(copy_yuzu_FFmpeg_deps target_dir)
|
||||||
|
include(WindowsCopyFiles)
|
||||||
|
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||||
|
windows_copy_files(${target_dir} ${FFMPEG_DLL_DIR} ${DLL_DEST}
|
||||||
|
avcodec-58.dll
|
||||||
|
avutil-56.dll
|
||||||
|
swresample-3.dll
|
||||||
|
swscale-5.dll
|
||||||
|
)
|
||||||
|
endfunction(copy_yuzu_FFmpeg_deps)
|
|
@ -1,9 +0,0 @@
|
||||||
function(copy_yuzu_unicorn_deps target_dir)
|
|
||||||
include(WindowsCopyFiles)
|
|
||||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
|
||||||
windows_copy_files(${target_dir} ${UNICORN_DLL_DIR} ${DLL_DEST}
|
|
||||||
libgcc_s_seh-1.dll
|
|
||||||
libwinpthread-1.dll
|
|
||||||
unicorn.dll
|
|
||||||
)
|
|
||||||
endfunction(copy_yuzu_unicorn_deps)
|
|
|
@ -30,7 +30,6 @@ If you want to contribute to the user interface translation, please check out th
|
||||||
|
|
||||||
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
|
* __Windows__: [Windows Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Windows)
|
||||||
* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux)
|
* __Linux__: [Linux Build](https://github.com/yuzu-emu/yuzu/wiki/Building-For-Linux)
|
||||||
* __macOS__: [macOS Build](https://github.com/yuzu-emu/yuzu/wiki/Building-for-macOS)
|
|
||||||
|
|
||||||
|
|
||||||
### Support
|
### Support
|
||||||
|
|
4749
dist/languages/de.ts
vendored
Normal file
4749
dist/languages/de.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4757
dist/languages/es.ts
vendored
Normal file
4757
dist/languages/es.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4732
dist/languages/fr.ts
vendored
Normal file
4732
dist/languages/fr.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4724
dist/languages/it.ts
vendored
Normal file
4724
dist/languages/it.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4752
dist/languages/ja_JP.ts
vendored
Normal file
4752
dist/languages/ja_JP.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4719
dist/languages/nl.ts
vendored
Normal file
4719
dist/languages/nl.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4713
dist/languages/pl.ts
vendored
Normal file
4713
dist/languages/pl.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4757
dist/languages/pt_BR.ts
vendored
Normal file
4757
dist/languages/pt_BR.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4725
dist/languages/pt_PT.ts
vendored
Normal file
4725
dist/languages/pt_PT.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4720
dist/languages/ru_RU.ts
vendored
Normal file
4720
dist/languages/ru_RU.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
4747
dist/languages/zh_CN.ts
vendored
Normal file
4747
dist/languages/zh_CN.ts
vendored
Normal file
File diff suppressed because it is too large
Load diff
14
dist/qt_themes/default/style.qss
vendored
14
dist/qt_themes/default/style.qss
vendored
|
@ -1,3 +1,7 @@
|
||||||
|
QAbstractSpinBox {
|
||||||
|
min-height: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
QPushButton#TogglableStatusBarButton {
|
QPushButton#TogglableStatusBarButton {
|
||||||
color: #959595;
|
color: #959595;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
@ -35,10 +39,10 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#buttonRefreshDevices {
|
QPushButton#buttonRefreshDevices {
|
||||||
min-width: 20px;
|
min-width: 21px;
|
||||||
min-height: 20px;
|
min-height: 21px;
|
||||||
max-width: 20px;
|
max-width: 21px;
|
||||||
max-height: 20px;
|
max-height: 21px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#bottomPerGameInput,
|
QWidget#bottomPerGameInput,
|
||||||
|
@ -71,7 +75,7 @@ QWidget#middleControllerApplet {
|
||||||
|
|
||||||
QWidget#topPerGameInput QComboBox,
|
QWidget#topPerGameInput QComboBox,
|
||||||
QWidget#middleControllerApplet QComboBox {
|
QWidget#middleControllerApplet QComboBox {
|
||||||
width: 123px;
|
width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#connectedControllers {
|
QWidget#connectedControllers {
|
||||||
|
|
82
dist/qt_themes/qdarkstyle/style.qss
vendored
82
dist/qt_themes/qdarkstyle/style.qss
vendored
|
@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled {
|
||||||
}
|
}
|
||||||
|
|
||||||
QRadioButton {
|
QRadioButton {
|
||||||
spacing: 5px;
|
|
||||||
outline: none;
|
|
||||||
color: #eff0f1;
|
color: #eff0f1;
|
||||||
|
spacing: 3px;
|
||||||
|
padding: 0px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QGroupBox QRadioButton {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
QRadioButton:disabled {
|
QRadioButton:disabled {
|
||||||
color: #76797C;
|
color: #76797C;
|
||||||
}
|
}
|
||||||
|
@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button {
|
||||||
|
|
||||||
QPushButton {
|
QPushButton {
|
||||||
color: #eff0f1;
|
color: #eff0f1;
|
||||||
border-width: 1px;
|
border: 1px solid #54575B;
|
||||||
border-color: #54575B;
|
|
||||||
border-style: solid;
|
|
||||||
padding: 6px 4px;
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
padding: 5px 0px 5px 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
|
min-height: 13px;
|
||||||
background-color: #232629;
|
background-color: #232629;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -553,8 +559,9 @@ QComboBox {
|
||||||
selection-background-color: #3daee9;
|
selection-background-color: #3daee9;
|
||||||
border: 1px solid #54575B;
|
border: 1px solid #54575B;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
padding: 4px 6px;
|
padding: 0px 4px 0px 4px;
|
||||||
min-width: 75px;
|
min-width: 60px;
|
||||||
|
min-height: 23px;
|
||||||
background-color: #232629;
|
background-color: #232629;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,26 +615,26 @@ QComboBox::down-arrow:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractSpinBox {
|
QAbstractSpinBox {
|
||||||
padding: 4px 6px;
|
|
||||||
border: 1px solid #54575B;
|
border: 1px solid #54575B;
|
||||||
background-color: #232629;
|
background-color: #232629;
|
||||||
color: #eff0f1;
|
color: #eff0f1;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
min-width: 75px;
|
min-width: 52px;
|
||||||
|
min-height: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractSpinBox:up-button {
|
QAbstractSpinBox:up-button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
subcontrol-origin: border;
|
subcontrol-origin: border;
|
||||||
subcontrol-position: center right;
|
subcontrol-position: center right;
|
||||||
left: -6px;
|
left: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractSpinBox:down-button {
|
QAbstractSpinBox:down-button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
subcontrol-origin: border;
|
subcontrol-origin: border;
|
||||||
subcontrol-position: center left;
|
subcontrol-position: center left;
|
||||||
right: -6px;
|
right: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractSpinBox::up-arrow,
|
QAbstractSpinBox::up-arrow,
|
||||||
|
@ -1277,41 +1284,33 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#buttonRefreshDevices {
|
QPushButton#buttonRefreshDevices {
|
||||||
min-width: 24px;
|
min-width: 23px;
|
||||||
min-height: 24px;
|
min-height: 23px;
|
||||||
max-width: 24px;
|
max-width: 23px;
|
||||||
max-height: 24px;
|
max-height: 23px;
|
||||||
padding: 0px 0px;
|
padding: 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSpinBox#spinboxLStickRange,
|
QSpinBox#spinboxLStickRange,
|
||||||
QSpinBox#spinboxRStickRange {
|
QSpinBox#spinboxRStickRange,
|
||||||
padding: 4px 0px 5px 0px;
|
QSpinBox#vibrationSpinPlayer1,
|
||||||
min-width: 63px;
|
QSpinBox#vibrationSpinPlayer2,
|
||||||
}
|
QSpinBox#vibrationSpinPlayer3,
|
||||||
|
QSpinBox#vibrationSpinPlayer4,
|
||||||
QSpinBox#vibrationSpin {
|
QSpinBox#vibrationSpinPlayer5,
|
||||||
padding: 4px 0px 5px 0px;
|
QSpinBox#vibrationSpinPlayer6,
|
||||||
min-width: 63px;
|
QSpinBox#vibrationSpinPlayer7,
|
||||||
}
|
QSpinBox#vibrationSpinPlayer8 {
|
||||||
|
min-width: 68px;
|
||||||
QSpinBox#spinboxLStickRange:up-button,
|
|
||||||
QSpinBox#spinboxRStickRange:up-button,
|
|
||||||
QSpinBox#vibrationSpin:up-button {
|
|
||||||
left: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSpinBox#spinboxLStickRange:down-button,
|
|
||||||
QSpinBox#spinboxRStickRange:down-button,
|
|
||||||
QSpinBox#vibrationSpin:down-button {
|
|
||||||
right: -1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDialog#ConfigureVibration QGroupBox::indicator,
|
||||||
QGroupBox#motionGroup::indicator,
|
QGroupBox#motionGroup::indicator,
|
||||||
QGroupBox#vibrationGroup::indicator {
|
QGroupBox#vibrationGroup::indicator {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDialog#ConfigureVibration QGroupBox::title,
|
||||||
QGroupBox#motionGroup::title,
|
QGroupBox#motionGroup::title,
|
||||||
QGroupBox#vibrationGroup::title {
|
QGroupBox#vibrationGroup::title {
|
||||||
spacing: 2px;
|
spacing: 2px;
|
||||||
|
@ -1340,16 +1339,7 @@ QWidget#middleControllerApplet {
|
||||||
|
|
||||||
QWidget#topPerGameInput QComboBox,
|
QWidget#topPerGameInput QComboBox,
|
||||||
QWidget#middleControllerApplet QComboBox {
|
QWidget#middleControllerApplet QComboBox {
|
||||||
width: 119px;
|
width: 120px;
|
||||||
}
|
|
||||||
|
|
||||||
QRadioButton#radioDocked {
|
|
||||||
margin-left: -3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QRadioButton#radioUndocked {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#connectedControllers {
|
QWidget#connectedControllers {
|
||||||
|
|
|
@ -172,8 +172,8 @@ QCheckBox {
|
||||||
color: #F0F0F0;
|
color: #F0F0F0;
|
||||||
spacing: 4px;
|
spacing: 4px;
|
||||||
outline: none;
|
outline: none;
|
||||||
padding-top: 4px;
|
padding-top: 2px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCheckBox:focus {
|
QCheckBox:focus {
|
||||||
|
@ -239,7 +239,7 @@ QGroupBox {
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
padding: 4px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QGroupBox::title {
|
QGroupBox::title {
|
||||||
|
@ -247,7 +247,7 @@ QGroupBox::title {
|
||||||
subcontrol-position: top left;
|
subcontrol-position: top left;
|
||||||
padding-left: 3px;
|
padding-left: 3px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
padding-top: 4px;
|
padding-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QGroupBox::indicator {
|
QGroupBox::indicator {
|
||||||
|
@ -298,6 +298,11 @@ QRadioButton {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QGroupBox QRadioButton {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
QRadioButton:focus {
|
QRadioButton:focus {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
@ -321,7 +326,6 @@ QRadioButton QWidget {
|
||||||
QRadioButton::indicator {
|
QRadioButton::indicator {
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
margin-left: 4px;
|
|
||||||
height: 16px;
|
height: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
@ -785,14 +789,8 @@ QAbstractSpinBox {
|
||||||
background-color: #19232D;
|
background-color: #19232D;
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
color: #F0F0F0;
|
color: #F0F0F0;
|
||||||
/* This fixes 103, 111 */
|
|
||||||
padding-top: 2px;
|
|
||||||
/* This fixes 103, 111 */
|
|
||||||
padding-bottom: 2px;
|
|
||||||
padding-left: 4px;
|
|
||||||
padding-right: 4px;
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
/* min-width: 5px; removed to fix 109 */
|
min-height: 19px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QAbstractSpinBox:up-button {
|
QAbstractSpinBox:up-button {
|
||||||
|
@ -997,10 +995,11 @@ QPushButton {
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
color: #F0F0F0;
|
color: #F0F0F0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 3px;
|
padding: 3px 0px 3px 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
/* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
|
/* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
|
min-height: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton:disabled {
|
QPushButton:disabled {
|
||||||
|
@ -1008,14 +1007,14 @@ QPushButton:disabled {
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
color: #787878;
|
color: #787878;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 3px;
|
padding: 3px 0px 3px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton:checked {
|
QPushButton:checked {
|
||||||
background-color: #32414B;
|
background-color: #32414B;
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 3px;
|
padding: 3px 0px 3px 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1024,7 +1023,7 @@ QPushButton:checked:disabled {
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
color: #787878;
|
color: #787878;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 3px;
|
padding: 3px 0px 3px 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1197,15 +1196,9 @@ QComboBox {
|
||||||
border: 1px solid #32414B;
|
border: 1px solid #32414B;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
selection-background-color: #1464A0;
|
selection-background-color: #1464A0;
|
||||||
padding-left: 4px;
|
padding: 0px 4px 0px 4px;
|
||||||
padding-right: 36px;
|
min-width: 60px;
|
||||||
/* 4 + 16*2 See scrollbar size */
|
min-height: 19px;
|
||||||
/* Fixes #103, #111 */
|
|
||||||
min-height: 1.5em;
|
|
||||||
/* padding-top: 2px; removed to fix #132 */
|
|
||||||
/* padding-bottom: 2px; removed to fix #132 */
|
|
||||||
/* min-width: 75px; removed to fix #109 */
|
|
||||||
/* Needed to remove indicator - fix #132 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox QAbstractItemView {
|
QComboBox QAbstractItemView {
|
||||||
|
@ -2198,29 +2191,40 @@ QPushButton#RendererStatusBarButton:!checked {
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton#buttonRefreshDevices {
|
QPushButton#buttonRefreshDevices {
|
||||||
min-width: 20px;
|
min-width: 19px;
|
||||||
min-height: 20px;
|
min-height: 19px;
|
||||||
max-width: 20px;
|
max-width: 19px;
|
||||||
max-height: 20px;
|
max-height: 19px;
|
||||||
padding: 0px 0px;
|
padding: 0px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSpinBox#spinboxLStickRange,
|
QSpinBox#spinboxLStickRange,
|
||||||
QSpinBox#spinboxRStickRange {
|
QSpinBox#spinboxRStickRange,
|
||||||
min-width: 38px;
|
QSpinBox#vibrationSpinPlayer1,
|
||||||
|
QSpinBox#vibrationSpinPlayer2,
|
||||||
|
QSpinBox#vibrationSpinPlayer3,
|
||||||
|
QSpinBox#vibrationSpinPlayer4,
|
||||||
|
QSpinBox#vibrationSpinPlayer5,
|
||||||
|
QSpinBox#vibrationSpinPlayer6,
|
||||||
|
QSpinBox#vibrationSpinPlayer7,
|
||||||
|
QSpinBox#vibrationSpinPlayer8 {
|
||||||
|
min-width: 68px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDialog#ConfigureVibration QGroupBox::indicator,
|
||||||
QGroupBox#motionGroup::indicator,
|
QGroupBox#motionGroup::indicator,
|
||||||
QGroupBox#vibrationGroup::indicator {
|
QGroupBox#vibrationGroup::indicator {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDialog#ConfigureVibration QGroupBox,
|
||||||
QWidget#bottomPerGameInput QGroupBox#motionGroup,
|
QWidget#bottomPerGameInput QGroupBox#motionGroup,
|
||||||
QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
|
QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
|
||||||
QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
|
QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDialog#ConfigureVibration QGroupBox::title,
|
||||||
QGroupBox#motionGroup::title,
|
QGroupBox#motionGroup::title,
|
||||||
QGroupBox#vibrationGroup::title {
|
QGroupBox#vibrationGroup::title {
|
||||||
spacing: 2px;
|
spacing: 2px;
|
||||||
|
@ -2260,26 +2264,7 @@ QWidget#middleControllerApplet {
|
||||||
|
|
||||||
QWidget#topPerGameInput QComboBox,
|
QWidget#topPerGameInput QComboBox,
|
||||||
QWidget#middleControllerApplet QComboBox {
|
QWidget#middleControllerApplet QComboBox {
|
||||||
padding-right: 2px;
|
width: 120px;
|
||||||
width: 127px;
|
|
||||||
}
|
|
||||||
|
|
||||||
QGroupBox#handheldGroup {
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRadioButton#radioDocked {
|
|
||||||
margin-left: -1px;
|
|
||||||
padding-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
QRadioButton#radioDocked::indicator {
|
|
||||||
margin-left: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QRadioButton#radioUndocked {
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#connectedControllers {
|
QWidget#connectedControllers {
|
||||||
|
@ -2352,7 +2337,7 @@ QCheckBox#checkboxPlayer5Connected,
|
||||||
QCheckBox#checkboxPlayer6Connected,
|
QCheckBox#checkboxPlayer6Connected,
|
||||||
QCheckBox#checkboxPlayer7Connected,
|
QCheckBox#checkboxPlayer7Connected,
|
||||||
QCheckBox#checkboxPlayer8Connected {
|
QCheckBox#checkboxPlayer8Connected {
|
||||||
spacing: 0px;
|
spacing: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#connectedControllers QLabel {
|
QWidget#connectedControllers QLabel {
|
||||||
|
@ -2427,7 +2412,7 @@ QCheckBox#checkboxPlayer7Connected::indicator,
|
||||||
QCheckBox#checkboxPlayer8Connected::indicator {
|
QCheckBox#checkboxPlayer8Connected::indicator {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
margin-left: 2px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QWidget#Player1LEDs QCheckBox::indicator:checked,
|
QWidget#Player1LEDs QCheckBox::indicator:checked,
|
||||||
|
|
32
externals/CMakeLists.txt
vendored
32
externals/CMakeLists.txt
vendored
|
@ -61,9 +61,7 @@ if (USE_DISCORD_PRESENCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Sirit
|
# Sirit
|
||||||
if (ENABLE_VULKAN)
|
add_subdirectory(sirit)
|
||||||
add_subdirectory(sirit)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# libzip
|
# libzip
|
||||||
find_package(Libzip 1.5)
|
find_package(Libzip 1.5)
|
||||||
|
@ -73,23 +71,29 @@ if (NOT LIBZIP_FOUND)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ENABLE_WEB_SERVICE)
|
if (ENABLE_WEB_SERVICE)
|
||||||
# LibreSSL
|
find_package(OpenSSL 1.1)
|
||||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
if (OPENSSL_FOUND)
|
||||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
else()
|
||||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
# LibreSSL
|
||||||
get_directory_property(OPENSSL_LIBRARIES
|
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||||
DIRECTORY libressl
|
set(OPENSSLDIR "/etc/ssl/")
|
||||||
DEFINITION OPENSSL_LIBS)
|
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||||
|
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||||
# lurlparser
|
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||||
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
|
get_directory_property(OPENSSL_LIBRARIES
|
||||||
|
DIRECTORY libressl
|
||||||
|
DEFINITION OPENSSL_LIBS)
|
||||||
|
endif()
|
||||||
|
|
||||||
# httplib
|
# httplib
|
||||||
add_library(httplib INTERFACE)
|
add_library(httplib INTERFACE)
|
||||||
target_include_directories(httplib INTERFACE ./httplib)
|
target_include_directories(httplib INTERFACE ./httplib)
|
||||||
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
|
||||||
|
if (WIN32)
|
||||||
|
target_link_libraries(httplib INTERFACE crypt32 cryptui ws2_32)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Opus
|
# Opus
|
||||||
|
|
2
externals/cubeb
vendored
2
externals/cubeb
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 616d773441b5355800ce64197a699e6cd6b36172
|
Subproject commit 1d66483ad2b93f0e00e175f9480c771af90003a7
|
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 0e1112b7df77ae55a62a51622940d5c8f9e8c84c
|
Subproject commit 3806284cbefc4115436dcdc687776a45ec313093
|
100
externals/find-modules/FindFFmpeg.cmake
vendored
Normal file
100
externals/find-modules/FindFFmpeg.cmake
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
|
||||||
|
# Once done this will define
|
||||||
|
#
|
||||||
|
# FFMPEG_FOUND - system has ffmpeg or libav
|
||||||
|
# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
|
||||||
|
# FFMPEG_LIBRARIES - Link these to use ffmpeg
|
||||||
|
# FFMPEG_LIBAVCODEC
|
||||||
|
# FFMPEG_LIBAVFORMAT
|
||||||
|
# FFMPEG_LIBAVUTIL
|
||||||
|
#
|
||||||
|
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
|
||||||
|
# Modified for other libraries by Lasse Kärkkäinen <tronic>
|
||||||
|
# Modified for Hedgewars by Stepik777
|
||||||
|
# Modified for FFmpeg-example Tuukka Pasanen 2018
|
||||||
|
# Modified for yuzu toastUnlimted 2020
|
||||||
|
#
|
||||||
|
# Redistribution and use is allowed according to the terms of the New
|
||||||
|
# BSD license.
|
||||||
|
#
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
find_package_handle_standard_args(FFMPEG
|
||||||
|
FOUND_VAR FFMPEG_FOUND
|
||||||
|
REQUIRED_VARS
|
||||||
|
FFMPEG_LIBRARY
|
||||||
|
FFMPEG_INCLUDE_DIR
|
||||||
|
VERSION_VAR FFMPEG_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
|
if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
|
||||||
|
# in cache already
|
||||||
|
set(FFMPEG_FOUND TRUE)
|
||||||
|
else()
|
||||||
|
# use pkg-config to get the directories and then use these values
|
||||||
|
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
|
||||||
|
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
|
||||||
|
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
|
||||||
|
NAMES libavcodec/avcodec.h
|
||||||
|
PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
|
||||||
|
/usr/include
|
||||||
|
/usr/local/include
|
||||||
|
/opt/local/include
|
||||||
|
/sw/include
|
||||||
|
PATH_SUFFIXES ffmpeg libav)
|
||||||
|
|
||||||
|
find_library(FFMPEG_LIBAVCODEC
|
||||||
|
NAMES avcodec
|
||||||
|
PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
|
||||||
|
/usr/lib
|
||||||
|
/usr/local/lib
|
||||||
|
/opt/local/lib
|
||||||
|
/sw/lib)
|
||||||
|
|
||||||
|
find_library(FFMPEG_LIBAVUTIL
|
||||||
|
NAMES avutil
|
||||||
|
PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
|
||||||
|
/usr/lib
|
||||||
|
/usr/local/lib
|
||||||
|
/opt/local/lib
|
||||||
|
/sw/lib)
|
||||||
|
|
||||||
|
find_library(FFMPEG_LIBSWSCALE
|
||||||
|
NAMES swscale
|
||||||
|
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
|
||||||
|
/usr/lib
|
||||||
|
/usr/local/lib
|
||||||
|
/opt/local/lib
|
||||||
|
/sw/lib)
|
||||||
|
|
||||||
|
if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
|
||||||
|
set(FFMPEG_FOUND TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(FFMPEG_FOUND)
|
||||||
|
set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
|
||||||
|
set(FFMPEG_LIBRARIES
|
||||||
|
${FFMPEG_LIBAVCODEC}
|
||||||
|
${FFMPEG_LIBAVUTIL}
|
||||||
|
${FFMPEG_LIBSWSCALE})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(FFMPEG_FOUND)
|
||||||
|
if(NOT FFMPEG_FIND_QUIETLY)
|
||||||
|
message(STATUS
|
||||||
|
"Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if(FFMPEG_FIND_REQUIRED)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Could not find libavcodec or libavutil or libswscale")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
2
externals/httplib/README.md
vendored
2
externals/httplib/README.md
vendored
|
@ -1,4 +1,4 @@
|
||||||
From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
|
From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
|
4792
externals/httplib/httplib.h
vendored
4792
externals/httplib/httplib.h
vendored
File diff suppressed because it is too large
Load diff
2
externals/inih/inih
vendored
2
externals/inih/inih
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 603729dec89aaca42d7bd08f08bc333165b7d5d1
|
Subproject commit 1e80a47dffbda813604f0913e2ad68c7054c14e4
|
2
externals/libressl
vendored
2
externals/libressl
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 7d01cb01cb1a926ecb4c9c98b107ef3c26f59dfb
|
Subproject commit 8289d0d07de6553bf4b900bf60e808ea3f7f59da
|
8
externals/lurlparser/CMakeLists.txt
vendored
8
externals/lurlparser/CMakeLists.txt
vendored
|
@ -1,8 +0,0 @@
|
||||||
add_library(lurlparser
|
|
||||||
LUrlParser.cpp
|
|
||||||
LUrlParser.h
|
|
||||||
)
|
|
||||||
|
|
||||||
create_target_directory_groups(lurlparser)
|
|
||||||
|
|
||||||
target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
265
externals/lurlparser/LUrlParser.cpp
vendored
265
externals/lurlparser/LUrlParser.cpp
vendored
|
@ -1,265 +0,0 @@
|
||||||
/*
|
|
||||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
|
||||||
* https://github.com/corporateshark/LUrlParser
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "LUrlParser.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
// check if the scheme name is valid
|
|
||||||
static bool IsSchemeValid( const std::string& SchemeName )
|
|
||||||
{
|
|
||||||
for ( auto c : SchemeName )
|
|
||||||
{
|
|
||||||
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
|
||||||
{
|
|
||||||
if ( !IsValid() ) { return false; }
|
|
||||||
|
|
||||||
int Port = atoi( m_Port.c_str() );
|
|
||||||
|
|
||||||
if ( Port <= 0 || Port > 65535 ) { return false; }
|
|
||||||
|
|
||||||
if ( OutPort ) { *OutPort = Port; }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// based on RFC 1738 and RFC 3986
|
|
||||||
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
|
||||||
{
|
|
||||||
LUrlParser::clParseURL Result;
|
|
||||||
|
|
||||||
const char* CurrentString = URL.c_str();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* <scheme>:<scheme-specific-part>
|
|
||||||
* <scheme> := [a-z\+\-\.]+
|
|
||||||
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
|
||||||
*/
|
|
||||||
|
|
||||||
// try to read scheme
|
|
||||||
{
|
|
||||||
const char* LocalString = strchr( CurrentString, ':' );
|
|
||||||
|
|
||||||
if ( !LocalString )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_NoUrlCharacter );
|
|
||||||
}
|
|
||||||
|
|
||||||
// save the scheme name
|
|
||||||
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
if ( !IsSchemeValid( Result.m_Scheme ) )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_InvalidSchemeName );
|
|
||||||
}
|
|
||||||
|
|
||||||
// scheme should be lowercase
|
|
||||||
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
|
||||||
|
|
||||||
// skip ':'
|
|
||||||
CurrentString = LocalString+1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* //<user>:<password>@<host>:<port>/<url-path>
|
|
||||||
* any ":", "@" and "/" must be normalized
|
|
||||||
*/
|
|
||||||
|
|
||||||
// skip "//"
|
|
||||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
|
||||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
|
||||||
|
|
||||||
// check if the user name and password are specified
|
|
||||||
bool bHasUserName = false;
|
|
||||||
|
|
||||||
const char* LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString )
|
|
||||||
{
|
|
||||||
if ( *LocalString == '@' )
|
|
||||||
{
|
|
||||||
// user name and password are specified
|
|
||||||
bHasUserName = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if ( *LocalString == '/' )
|
|
||||||
{
|
|
||||||
// end of <host>:<port> specification
|
|
||||||
bHasUserName = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalString++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// user name and password
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
if ( bHasUserName )
|
|
||||||
{
|
|
||||||
// read user name
|
|
||||||
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
// proceed with the current pointer
|
|
||||||
CurrentString = LocalString;
|
|
||||||
|
|
||||||
if ( *CurrentString == ':' )
|
|
||||||
{
|
|
||||||
// skip ':'
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read password
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip '@'
|
|
||||||
if ( *CurrentString != '@' )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_NoAtSign );
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentString++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bHasBracket = ( *CurrentString == '[' );
|
|
||||||
|
|
||||||
// go ahead, read the host name
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString )
|
|
||||||
{
|
|
||||||
if ( bHasBracket && *LocalString == ']' )
|
|
||||||
{
|
|
||||||
// end of IPv6 address
|
|
||||||
LocalString++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
|
||||||
{
|
|
||||||
// port number is specified
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalString++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
|
|
||||||
// is port number specified?
|
|
||||||
if ( *CurrentString == ':' )
|
|
||||||
{
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read port number
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// end of string
|
|
||||||
if ( !*CurrentString )
|
|
||||||
{
|
|
||||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip '/'
|
|
||||||
if ( *CurrentString != '/' )
|
|
||||||
{
|
|
||||||
return clParseURL( LUrlParserError_NoSlash );
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// parse the path
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
|
|
||||||
// check for query
|
|
||||||
if ( *CurrentString == '?' )
|
|
||||||
{
|
|
||||||
// skip '?'
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read query
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for fragment
|
|
||||||
if ( *CurrentString == '#' )
|
|
||||||
{
|
|
||||||
// skip '#'
|
|
||||||
CurrentString++;
|
|
||||||
|
|
||||||
// read fragment
|
|
||||||
LocalString = CurrentString;
|
|
||||||
|
|
||||||
while ( *LocalString ) LocalString++;
|
|
||||||
|
|
||||||
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
|
||||||
|
|
||||||
CurrentString = LocalString;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
78
externals/lurlparser/LUrlParser.h
vendored
78
externals/lurlparser/LUrlParser.h
vendored
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
|
||||||
* https://github.com/corporateshark/LUrlParser
|
|
||||||
*
|
|
||||||
* The MIT License (MIT)
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in all
|
|
||||||
* copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
* SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace LUrlParser
|
|
||||||
{
|
|
||||||
enum LUrlParserError
|
|
||||||
{
|
|
||||||
LUrlParserError_Ok = 0,
|
|
||||||
LUrlParserError_Uninitialized = 1,
|
|
||||||
LUrlParserError_NoUrlCharacter = 2,
|
|
||||||
LUrlParserError_InvalidSchemeName = 3,
|
|
||||||
LUrlParserError_NoDoubleSlash = 4,
|
|
||||||
LUrlParserError_NoAtSign = 5,
|
|
||||||
LUrlParserError_UnexpectedEndOfLine = 6,
|
|
||||||
LUrlParserError_NoSlash = 7,
|
|
||||||
};
|
|
||||||
|
|
||||||
class clParseURL
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LUrlParserError m_ErrorCode;
|
|
||||||
std::string m_Scheme;
|
|
||||||
std::string m_Host;
|
|
||||||
std::string m_Port;
|
|
||||||
std::string m_Path;
|
|
||||||
std::string m_Query;
|
|
||||||
std::string m_Fragment;
|
|
||||||
std::string m_UserName;
|
|
||||||
std::string m_Password;
|
|
||||||
|
|
||||||
clParseURL()
|
|
||||||
: m_ErrorCode( LUrlParserError_Uninitialized )
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// return 'true' if the parsing was successful
|
|
||||||
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
|
||||||
|
|
||||||
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
|
||||||
bool GetPort( int* OutPort ) const;
|
|
||||||
|
|
||||||
/// parse the URL
|
|
||||||
static clParseURL ParseURL( const std::string& URL );
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit clParseURL( LUrlParserError ErrorCode )
|
|
||||||
: m_ErrorCode( ErrorCode )
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace LUrlParser
|
|
19
externals/lurlparser/README.md
vendored
19
externals/lurlparser/README.md
vendored
|
@ -1,19 +0,0 @@
|
||||||
From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
===
|
|
||||||
|
|
||||||
Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
|
||||||
|
|
||||||
(C) Sergey Kosarevsky, 2015
|
|
||||||
|
|
||||||
@corporateshark sk@linderdaum.com
|
|
||||||
|
|
||||||
http://www.linderdaum.com
|
|
||||||
|
|
||||||
http://blog.linderdaum.com
|
|
||||||
|
|
||||||
=============================
|
|
||||||
|
|
||||||
A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.
|
|
18
externals/microprofile/microprofile.h
vendored
18
externals/microprofile/microprofile.h
vendored
|
@ -902,8 +902,10 @@ inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4244)
|
#pragma warning(disable: 4244)
|
||||||
|
#endif
|
||||||
int64_t MicroProfileTicksPerSecondCpu()
|
int64_t MicroProfileTicksPerSecondCpu()
|
||||||
{
|
{
|
||||||
static int64_t nTicksPerSecond = 0;
|
static int64_t nTicksPerSecond = 0;
|
||||||
|
@ -946,7 +948,11 @@ typedef HANDLE MicroProfileThread;
|
||||||
DWORD _stdcall ThreadTrampoline(void* pFunc)
|
DWORD _stdcall ThreadTrampoline(void* pFunc)
|
||||||
{
|
{
|
||||||
MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc;
|
MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc;
|
||||||
return (uint32_t)F(0);
|
|
||||||
|
// The return value of F will always return a void*, however, this is for
|
||||||
|
// compatibility with pthreads. The underlying "address" of the pointer
|
||||||
|
// is always a 32-bit value, so this cast is safe to perform.
|
||||||
|
return static_cast<DWORD>(reinterpret_cast<uint64_t>(F(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
|
||||||
|
@ -1742,10 +1748,10 @@ void MicroProfileFlip()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
|
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
|
||||||
{
|
{
|
||||||
pLog->nGroupTicks[i] += nGroupTicks[i];
|
pLog->nGroupTicks[j] += nGroupTicks[j];
|
||||||
pFrameGroup[i] += nGroupTicks[i];
|
pFrameGroup[j] += nGroupTicks[j];
|
||||||
}
|
}
|
||||||
pLog->nStackPos = nStackPos;
|
pLog->nStackPos = nStackPos;
|
||||||
}
|
}
|
||||||
|
@ -3328,7 +3334,7 @@ bool MicroProfileIsLocalThread(uint32_t nThreadId)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
|
|
||||||
bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;}
|
bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; }
|
||||||
void MicroProfileStopContextSwitchTrace(){}
|
void MicroProfileStopContextSwitchTrace(){}
|
||||||
void MicroProfileStartContextSwitchTrace(){}
|
void MicroProfileStartContextSwitchTrace(){}
|
||||||
|
|
||||||
|
@ -3576,7 +3582,7 @@ int MicroProfileGetGpuTickReference(int64_t* pOutCpu, int64_t* pOutGpu)
|
||||||
|
|
||||||
#undef S
|
#undef S
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _MSC_VER
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
1
externals/unicorn
vendored
1
externals/unicorn
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit 73f45735354396766a4bfb26d0b96b06e5cf31b2
|
|
|
@ -32,7 +32,6 @@ if (MSVC)
|
||||||
# /Zc:inline - Let codegen omit inline functions in object files
|
# /Zc:inline - Let codegen omit inline functions in object files
|
||||||
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
|
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
/W3
|
|
||||||
/MP
|
/MP
|
||||||
/Zi
|
/Zi
|
||||||
/Zo
|
/Zo
|
||||||
|
@ -43,6 +42,18 @@ if (MSVC)
|
||||||
/Zc:externConstexpr
|
/Zc:externConstexpr
|
||||||
/Zc:inline
|
/Zc:inline
|
||||||
/Zc:throwingNew
|
/Zc:throwingNew
|
||||||
|
|
||||||
|
# Warnings
|
||||||
|
/W3
|
||||||
|
/we4062 # enumerator 'identifier' in a switch of enum 'enumeration' is not handled
|
||||||
|
/we4101 # 'identifier': unreferenced local variable
|
||||||
|
/we4265 # 'class': class has virtual functions, but destructor is not virtual
|
||||||
|
/we4388 # signed/unsigned mismatch
|
||||||
|
/we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
|
||||||
|
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||||
|
/we4555 # Expression has no effect; expected expression with side-effect
|
||||||
|
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||||
|
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||||
)
|
)
|
||||||
|
|
||||||
# /GS- - No stack buffer overflow checks
|
# /GS- - No stack buffer overflow checks
|
||||||
|
@ -56,9 +67,12 @@ else()
|
||||||
-Werror=implicit-fallthrough
|
-Werror=implicit-fallthrough
|
||||||
-Werror=missing-declarations
|
-Werror=missing-declarations
|
||||||
-Werror=reorder
|
-Werror=reorder
|
||||||
|
-Werror=uninitialized
|
||||||
|
-Werror=unused-result
|
||||||
-Wextra
|
-Wextra
|
||||||
-Wmissing-declarations
|
-Wmissing-declarations
|
||||||
-Wno-attributes
|
-Wno-attributes
|
||||||
|
-Wno-invalid-offsetof
|
||||||
-Wno-unused-parameter
|
-Wno-unused-parameter
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,24 @@ add_library(audio_core STATIC
|
||||||
|
|
||||||
create_target_directory_groups(audio_core)
|
create_target_directory_groups(audio_core)
|
||||||
|
|
||||||
|
if (NOT MSVC)
|
||||||
|
target_compile_options(audio_core PRIVATE
|
||||||
|
-Werror=conversion
|
||||||
|
-Werror=ignored-qualifiers
|
||||||
|
-Werror=implicit-fallthrough
|
||||||
|
-Werror=reorder
|
||||||
|
-Werror=sign-compare
|
||||||
|
-Werror=shadow
|
||||||
|
-Werror=unused-parameter
|
||||||
|
-Werror=unused-variable
|
||||||
|
|
||||||
|
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||||
|
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
|
||||||
|
|
||||||
|
-Wno-sign-conversion
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(audio_core PUBLIC common core)
|
target_link_libraries(audio_core PUBLIC common core)
|
||||||
target_link_libraries(audio_core PRIVATE SoundTouch)
|
target_link_libraries(audio_core PRIVATE SoundTouch)
|
||||||
|
|
||||||
|
|
|
@ -31,8 +31,8 @@ Filter Filter::LowPass(double cutoff, double Q) {
|
||||||
|
|
||||||
Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
|
Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
|
||||||
|
|
||||||
Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2)
|
Filter::Filter(double a0_, double a1_, double a2_, double b0_, double b1_, double b2_)
|
||||||
: a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
|
: a1(a1_ / a0_), a2(a2_ / a0_), b0(b0_ / a0_), b1(b1_ / a0_), b2(b2_ / a0_) {}
|
||||||
|
|
||||||
void Filter::Process(std::vector<s16>& signal) {
|
void Filter::Process(std::vector<s16>& signal) {
|
||||||
const std::size_t num_frames = signal.size() / 2;
|
const std::size_t num_frames = signal.size() / 2;
|
||||||
|
@ -55,7 +55,8 @@ void Filter::Process(std::vector<s16>& signal) {
|
||||||
/// @param total_count The total number of biquads to be cascaded.
|
/// @param total_count The total number of biquads to be cascaded.
|
||||||
/// @param index 0-index of the biquad to calculate the Q value for.
|
/// @param index 0-index of the biquad to calculate the Q value for.
|
||||||
static double CascadingBiquadQ(std::size_t total_count, std::size_t index) {
|
static double CascadingBiquadQ(std::size_t total_count, std::size_t index) {
|
||||||
const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
|
const auto pole =
|
||||||
|
M_PI * static_cast<double>(2 * index + 1) / (4.0 * static_cast<double>(total_count));
|
||||||
return 1.0 / (2.0 * std::cos(pole));
|
return 1.0 / (2.0 * std::cos(pole));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size
|
||||||
}
|
}
|
||||||
|
|
||||||
CascadingFilter::CascadingFilter() = default;
|
CascadingFilter::CascadingFilter() = default;
|
||||||
CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
|
CascadingFilter::CascadingFilter(std::vector<Filter> filters_) : filters(std::move(filters_)) {}
|
||||||
|
|
||||||
void CascadingFilter::Process(std::vector<s16>& signal) {
|
void CascadingFilter::Process(std::vector<s16>& signal) {
|
||||||
for (auto& filter : filters) {
|
for (auto& filter : filters) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ public:
|
||||||
/// Passthrough filter.
|
/// Passthrough filter.
|
||||||
Filter();
|
Filter();
|
||||||
|
|
||||||
Filter(double a0, double a1, double a2, double b0, double b1, double b2);
|
Filter(double a0_, double a1_, double a2_, double b0_, double b1_, double b2_);
|
||||||
|
|
||||||
void Process(std::vector<s16>& signal);
|
void Process(std::vector<s16>& signal);
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ public:
|
||||||
/// Passthrough.
|
/// Passthrough.
|
||||||
CascadingFilter();
|
CascadingFilter();
|
||||||
|
|
||||||
explicit CascadingFilter(std::vector<Filter> filters);
|
explicit CascadingFilter(std::vector<Filter> filters_);
|
||||||
|
|
||||||
void Process(std::vector<s16>& signal);
|
void Process(std::vector<s16>& signal);
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (ratio <= 0) {
|
if (ratio <= 0) {
|
||||||
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
|
LOG_ERROR(Audio, "Nonsensical interpolation ratio {}", ratio);
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +164,8 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
|
||||||
const std::size_t num_frames{input.size() / 2};
|
const std::size_t num_frames{input.size() / 2};
|
||||||
|
|
||||||
std::vector<s16> output;
|
std::vector<s16> output;
|
||||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + InterpolationState::taps));
|
output.reserve(static_cast<std::size_t>(static_cast<double>(input.size()) / ratio +
|
||||||
|
InterpolationState::taps));
|
||||||
|
|
||||||
for (std::size_t frame{}; frame < num_frames; ++frame) {
|
for (std::size_t frame{}; frame < num_frames; ++frame) {
|
||||||
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
|
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
|
||||||
|
@ -217,7 +218,7 @@ void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size
|
||||||
const auto l2 = lut[lut_index + 2];
|
const auto l2 = lut[lut_index + 2];
|
||||||
const auto l3 = lut[lut_index + 3];
|
const auto l3 = lut[lut_index + 3];
|
||||||
|
|
||||||
const auto s0 = static_cast<s32>(input[index]);
|
const auto s0 = static_cast<s32>(input[index + 0]);
|
||||||
const auto s1 = static_cast<s32>(input[index + 1]);
|
const auto s1 = static_cast<s32>(input[index + 1]);
|
||||||
const auto s2 = static_cast<s32>(input[index + 2]);
|
const auto s2 = static_cast<s32>(input[index + 2]);
|
||||||
const auto s3 = static_cast<s32>(input[index + 3]);
|
const auto s3 = static_cast<s32>(input[index + 3]);
|
||||||
|
|
|
@ -43,6 +43,10 @@ std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
|
||||||
|
return stream->GetTagsAndReleaseBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
void AudioOut::StartStream(StreamPtr stream) {
|
void AudioOut::StartStream(StreamPtr stream) {
|
||||||
stream->Play();
|
stream->Play();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,9 @@ public:
|
||||||
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
||||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
||||||
|
|
||||||
|
/// Returns a vector of all recently released buffers specified by tag for the specified stream
|
||||||
|
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
|
||||||
|
|
||||||
/// Starts an audio stream for playback
|
/// Starts an audio stream for playback
|
||||||
void StartStream(StreamPtr stream);
|
void StartStream(StreamPtr stream);
|
||||||
|
|
||||||
|
|
|
@ -2,43 +2,90 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "audio_core/algorithm/interpolate.h"
|
|
||||||
#include "audio_core/audio_out.h"
|
#include "audio_core/audio_out.h"
|
||||||
#include "audio_core/audio_renderer.h"
|
#include "audio_core/audio_renderer.h"
|
||||||
#include "audio_core/codec.h"
|
|
||||||
#include "audio_core/common.h"
|
#include "audio_core/common.h"
|
||||||
#include "audio_core/info_updater.h"
|
#include "audio_core/info_updater.h"
|
||||||
#include "audio_core/voice_context.h"
|
#include "audio_core/voice_context.h"
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/hle/kernel/writable_event.h"
|
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
|
||||||
|
return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()},
|
||||||
|
s32{std::numeric_limits<s16>::max()}));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
|
||||||
|
// Mix 50% from left and 50% from right channel
|
||||||
|
constexpr float l_mix_amount = 50.0f / 100.0f;
|
||||||
|
constexpr float r_mix_amount = 50.0f / 100.0f;
|
||||||
|
return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
|
||||||
|
(static_cast<float>(r_channel) * r_mix_amount)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
|
||||||
|
s16 fc_channel,
|
||||||
|
[[maybe_unused]] s16 lf_channel,
|
||||||
|
s16 bl_channel, s16 br_channel) {
|
||||||
|
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
||||||
|
// are mixed to be 36.94%
|
||||||
|
|
||||||
|
constexpr float front_mix_amount = 36.94f / 100.0f;
|
||||||
|
constexpr float center_mix_amount = 26.12f / 100.0f;
|
||||||
|
constexpr float back_mix_amount = 36.94f / 100.0f;
|
||||||
|
|
||||||
|
// Mix 50% from left and 50% from right channel
|
||||||
|
const auto left = front_mix_amount * static_cast<float>(fl_channel) +
|
||||||
|
center_mix_amount * static_cast<float>(fc_channel) +
|
||||||
|
back_mix_amount * static_cast<float>(bl_channel);
|
||||||
|
|
||||||
|
const auto right = front_mix_amount * static_cast<float>(fr_channel) +
|
||||||
|
center_mix_amount * static_cast<float>(fc_channel) +
|
||||||
|
back_mix_amount * static_cast<float>(br_channel);
|
||||||
|
|
||||||
|
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
|
||||||
|
s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
|
||||||
|
const std::array<float_le, 4>& coeff) {
|
||||||
|
const auto left =
|
||||||
|
static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||||
|
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0];
|
||||||
|
|
||||||
|
const auto right =
|
||||||
|
static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||||
|
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0];
|
||||||
|
|
||||||
|
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||||
AudioCommon::AudioRendererParameter params,
|
AudioCommon::AudioRendererParameter params,
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event,
|
Stream::ReleaseCallback&& release_callback,
|
||||||
std::size_t instance_number)
|
std::size_t instance_number)
|
||||||
: worker_params{params}, buffer_event{buffer_event},
|
: worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),
|
||||||
memory_pool_info(params.effect_count + params.voice_count * 4),
|
|
||||||
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
||||||
sink_context(params.sink_count), splitter_context(),
|
sink_context(params.sink_count), splitter_context(),
|
||||||
voices(params.voice_count), memory{memory_},
|
voices(params.voice_count), memory{memory_},
|
||||||
command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
|
command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
|
||||||
memory),
|
memory) {
|
||||||
temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
|
|
||||||
behavior_info.SetUserRevision(params.revision);
|
behavior_info.SetUserRevision(params.revision);
|
||||||
splitter_context.Initialize(behavior_info, params.splitter_count,
|
splitter_context.Initialize(behavior_info, params.splitter_count,
|
||||||
params.num_splitter_send_channels);
|
params.num_splitter_send_channels);
|
||||||
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
||||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||||
stream =
|
stream = audio_out->OpenStream(
|
||||||
audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
||||||
fmt::format("AudioRenderer-Instance{}", instance_number),
|
fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
|
||||||
[=]() { buffer_event->Signal(); });
|
|
||||||
audio_out->StartStream(stream);
|
audio_out->StartStream(stream);
|
||||||
|
|
||||||
QueueMixedBuffer(0);
|
QueueMixedBuffer(0);
|
||||||
|
@ -65,10 +112,6 @@ Stream::State AudioRenderer::GetStreamState() const {
|
||||||
return stream->GetState();
|
return stream->GetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr s16 ClampToS16(s32 value) {
|
|
||||||
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||||
std::vector<u8>& output_params) {
|
std::vector<u8>& output_params) {
|
||||||
|
|
||||||
|
@ -107,8 +150,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
||||||
splitter_context, effect_context);
|
splitter_context, effect_context);
|
||||||
|
|
||||||
if (mix_result.IsError()) {
|
if (mix_result.IsError()) {
|
||||||
LOG_ERROR(Audio, "Failed to update mix parameters");
|
LOG_ERROR(Audio, "Failed to update mix parameters");
|
||||||
|
@ -197,20 +240,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
|
for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||||
if (channel_count == 1) {
|
if (channel_count == 1) {
|
||||||
const auto sample = ClampToS16(mix_buffers[0][i]);
|
const auto sample = ClampToS16(mix_buffers[0][i]);
|
||||||
buffer[i * stream_channel_count + 0] = sample;
|
|
||||||
if (stream_channel_count > 1) {
|
// Place sample in all channels
|
||||||
buffer[i * stream_channel_count + 1] = sample;
|
for (u32 channel = 0; channel < stream_channel_count; channel++) {
|
||||||
|
buffer[i * stream_channel_count + channel] = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream_channel_count == 6) {
|
if (stream_channel_count == 6) {
|
||||||
buffer[i * stream_channel_count + 2] = sample;
|
// Output stream has a LF channel, mute it!
|
||||||
buffer[i * stream_channel_count + 4] = sample;
|
buffer[i * stream_channel_count + 3] = 0;
|
||||||
buffer[i * stream_channel_count + 5] = sample;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (channel_count == 2) {
|
} else if (channel_count == 2) {
|
||||||
const auto l_sample = ClampToS16(mix_buffers[0][i]);
|
const auto l_sample = ClampToS16(mix_buffers[0][i]);
|
||||||
const auto r_sample = ClampToS16(mix_buffers[1][i]);
|
const auto r_sample = ClampToS16(mix_buffers[1][i]);
|
||||||
if (stream_channel_count == 1) {
|
if (stream_channel_count == 1) {
|
||||||
buffer[i * stream_channel_count + 0] = l_sample;
|
buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
|
||||||
} else if (stream_channel_count == 2) {
|
} else if (stream_channel_count == 2) {
|
||||||
buffer[i * stream_channel_count + 0] = l_sample;
|
buffer[i * stream_channel_count + 0] = l_sample;
|
||||||
buffer[i * stream_channel_count + 1] = r_sample;
|
buffer[i * stream_channel_count + 1] = r_sample;
|
||||||
|
@ -218,8 +263,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
buffer[i * stream_channel_count + 0] = l_sample;
|
buffer[i * stream_channel_count + 0] = l_sample;
|
||||||
buffer[i * stream_channel_count + 1] = r_sample;
|
buffer[i * stream_channel_count + 1] = r_sample;
|
||||||
|
|
||||||
buffer[i * stream_channel_count + 2] =
|
// Combine both left and right channels to the center channel
|
||||||
ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2);
|
buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
|
||||||
|
|
||||||
buffer[i * stream_channel_count + 4] = l_sample;
|
buffer[i * stream_channel_count + 4] = l_sample;
|
||||||
buffer[i * stream_channel_count + 5] = r_sample;
|
buffer[i * stream_channel_count + 5] = r_sample;
|
||||||
|
@ -234,17 +279,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
const auto br_sample = ClampToS16(mix_buffers[5][i]);
|
const auto br_sample = ClampToS16(mix_buffers[5][i]);
|
||||||
|
|
||||||
if (stream_channel_count == 1) {
|
if (stream_channel_count == 1) {
|
||||||
buffer[i * stream_channel_count + 0] = fc_sample;
|
// Games seem to ignore the center channel half the time, we use the front left
|
||||||
|
// and right channel for mixing as that's where majority of the audio goes
|
||||||
|
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
||||||
} else if (stream_channel_count == 2) {
|
} else if (stream_channel_count == 2) {
|
||||||
buffer[i * stream_channel_count + 0] =
|
// Mix all channels into 2 channels
|
||||||
static_cast<s16>(0.3694f * static_cast<float>(fl_sample) +
|
if (sink_context.HasDownMixingCoefficients()) {
|
||||||
0.2612f * static_cast<float>(fc_sample) +
|
const auto [left, right] = Mix6To2WithCoefficients(
|
||||||
0.3694f * static_cast<float>(bl_sample));
|
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
||||||
buffer[i * stream_channel_count + 1] =
|
sink_context.GetDownmixCoefficients());
|
||||||
static_cast<s16>(0.3694f * static_cast<float>(fr_sample) +
|
buffer[i * stream_channel_count + 0] = left;
|
||||||
0.2612f * static_cast<float>(fc_sample) +
|
buffer[i * stream_channel_count + 1] = right;
|
||||||
0.3694f * static_cast<float>(br_sample));
|
} else {
|
||||||
|
const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample,
|
||||||
|
lf_sample, bl_sample, br_sample);
|
||||||
|
buffer[i * stream_channel_count + 0] = left;
|
||||||
|
buffer[i * stream_channel_count + 1] = right;
|
||||||
|
}
|
||||||
} else if (stream_channel_count == 6) {
|
} else if (stream_channel_count == 6) {
|
||||||
|
// Pass through
|
||||||
buffer[i * stream_channel_count + 0] = fl_sample;
|
buffer[i * stream_channel_count + 0] = fl_sample;
|
||||||
buffer[i * stream_channel_count + 1] = fr_sample;
|
buffer[i * stream_channel_count + 1] = fr_sample;
|
||||||
buffer[i * stream_channel_count + 2] = fc_sample;
|
buffer[i * stream_channel_count + 2] = fc_sample;
|
||||||
|
@ -262,7 +315,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioRenderer::ReleaseAndQueueBuffers() {
|
void AudioRenderer::ReleaseAndQueueBuffers() {
|
||||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
|
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
|
||||||
for (const auto& tag : released_buffers) {
|
for (const auto& tag : released_buffers) {
|
||||||
QueueMixedBuffer(tag);
|
QueueMixedBuffer(tag);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,53 +21,41 @@
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/hle/kernel/object.h"
|
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
namespace Core::Timing {
|
namespace Core::Timing {
|
||||||
class CoreTiming;
|
class CoreTiming;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Kernel {
|
|
||||||
class WritableEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Core::Memory {
|
namespace Core::Memory {
|
||||||
class Memory;
|
class Memory;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
using DSPStateHolder = std::array<VoiceState*, 6>;
|
using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
|
||||||
|
|
||||||
class AudioOut;
|
class AudioOut;
|
||||||
|
|
||||||
struct RendererInfo {
|
|
||||||
u64_le elasped_frame_count{};
|
|
||||||
INSERT_PADDING_WORDS(2);
|
|
||||||
};
|
|
||||||
static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
|
|
||||||
|
|
||||||
class AudioRenderer {
|
class AudioRenderer {
|
||||||
public:
|
public:
|
||||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||||
AudioCommon::AudioRendererParameter params,
|
AudioCommon::AudioRendererParameter params,
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
|
Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
|
||||||
~AudioRenderer();
|
~AudioRenderer();
|
||||||
|
|
||||||
ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||||
std::vector<u8>& output_params);
|
std::vector<u8>& output_params);
|
||||||
void QueueMixedBuffer(Buffer::Tag tag);
|
void QueueMixedBuffer(Buffer::Tag tag);
|
||||||
void ReleaseAndQueueBuffers();
|
void ReleaseAndQueueBuffers();
|
||||||
u32 GetSampleRate() const;
|
[[nodiscard]] u32 GetSampleRate() const;
|
||||||
u32 GetSampleCount() const;
|
[[nodiscard]] u32 GetSampleCount() const;
|
||||||
u32 GetMixBufferCount() const;
|
[[nodiscard]] u32 GetMixBufferCount() const;
|
||||||
Stream::State GetStreamState() const;
|
[[nodiscard]] Stream::State GetStreamState() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BehaviorInfo behavior_info{};
|
BehaviorInfo behavior_info{};
|
||||||
|
|
||||||
AudioCommon::AudioRendererParameter worker_params;
|
AudioCommon::AudioRendererParameter worker_params;
|
||||||
std::shared_ptr<Kernel::WritableEvent> buffer_event;
|
|
||||||
std::vector<ServerMemoryPoolInfo> memory_pool_info;
|
std::vector<ServerMemoryPoolInfo> memory_pool_info;
|
||||||
VoiceContext voice_context;
|
VoiceContext voice_context;
|
||||||
EffectContext effect_context;
|
EffectContext effect_context;
|
||||||
|
@ -80,7 +68,6 @@ private:
|
||||||
Core::Memory::Memory& memory;
|
Core::Memory::Memory& memory;
|
||||||
CommandGenerator command_generator;
|
CommandGenerator command_generator;
|
||||||
std::size_t elapsed_frame_count{};
|
std::size_t elapsed_frame_count{};
|
||||||
std::vector<s32> temp_mix_buffer{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -57,15 +57,15 @@ bool BehaviorInfo::IsLongSizePreDelaySupported() const {
|
||||||
return AudioCommon::IsRevisionSupported(3, user_revision);
|
return AudioCommon::IsRevisionSupported(3, user_revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
|
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const {
|
||||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
|
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const {
|
||||||
return AudioCommon::IsRevisionSupported(4, user_revision);
|
return AudioCommon::IsRevisionSupported(4, user_revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
|
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const {
|
||||||
return AudioCommon::IsRevisionSupported(1, user_revision);
|
return AudioCommon::IsRevisionSupported(1, user_revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,22 +43,22 @@ public:
|
||||||
void ClearError();
|
void ClearError();
|
||||||
void UpdateFlags(u64_le dest_flags);
|
void UpdateFlags(u64_le dest_flags);
|
||||||
void SetUserRevision(u32_le revision);
|
void SetUserRevision(u32_le revision);
|
||||||
u32_le GetUserRevision() const;
|
[[nodiscard]] u32_le GetUserRevision() const;
|
||||||
u32_le GetProcessRevision() const;
|
[[nodiscard]] u32_le GetProcessRevision() const;
|
||||||
|
|
||||||
bool IsAdpcmLoopContextBugFixed() const;
|
[[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
|
||||||
bool IsSplitterSupported() const;
|
[[nodiscard]] bool IsSplitterSupported() const;
|
||||||
bool IsLongSizePreDelaySupported() const;
|
[[nodiscard]] bool IsLongSizePreDelaySupported() const;
|
||||||
bool IsAudioRenererProcessingTimeLimit80PercentSupported() const;
|
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
||||||
bool IsAudioRenererProcessingTimeLimit75PercentSupported() const;
|
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
||||||
bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
|
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
||||||
bool IsElapsedFrameCountSupported() const;
|
[[nodiscard]] bool IsElapsedFrameCountSupported() const;
|
||||||
bool IsMemoryPoolForceMappingEnabled() const;
|
[[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
|
||||||
bool IsFlushVoiceWaveBuffersSupported() const;
|
[[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
|
||||||
bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
[[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
||||||
bool IsVoicePitchAndSrcSkippedSupported() const;
|
[[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
|
||||||
bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
[[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
||||||
bool IsSplitterBugFixed() const;
|
[[nodiscard]] bool IsSplitterBugFixed() const;
|
||||||
void CopyErrorInfo(OutParams& dst);
|
void CopyErrorInfo(OutParams& dst);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Buffer {
|
||||||
public:
|
public:
|
||||||
using Tag = u64;
|
using Tag = u64;
|
||||||
|
|
||||||
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
|
Buffer(Tag tag_, std::vector<s16>&& samples_) : tag{tag_}, samples{std::move(samples_)} {}
|
||||||
|
|
||||||
/// Returns the raw audio data for the buffer
|
/// Returns the raw audio data for the buffer
|
||||||
std::vector<s16>& GetSamples() {
|
std::vector<s16>& GetSamples() {
|
||||||
|
|
|
@ -16,8 +16,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM
|
||||||
|
|
||||||
constexpr std::size_t FRAME_LEN = 8;
|
constexpr std::size_t FRAME_LEN = 8;
|
||||||
constexpr std::size_t SAMPLES_PER_FRAME = 14;
|
constexpr std::size_t SAMPLES_PER_FRAME = 14;
|
||||||
constexpr std::array<int, 16> SIGNED_NIBBLES = {
|
static constexpr std::array<int, 16> SIGNED_NIBBLES{
|
||||||
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
|
0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
|
||||||
|
};
|
||||||
|
|
||||||
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
|
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
|
||||||
const std::size_t ret_size =
|
const std::size_t ret_size =
|
||||||
|
|
|
@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>;
|
||||||
* @param state ADPCM state, this is updated with new state
|
* @param state ADPCM state, this is updated with new state
|
||||||
* @return Decoded stereo signed PCM16 data, sample_count in length
|
* @return Decoded stereo signed PCM16 data, sample_count in length
|
||||||
*/
|
*/
|
||||||
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
|
std::vector<s16> DecodeADPCM(const u8* data, std::size_t size, const ADPCM_Coeff& coeff,
|
||||||
ADPCMState& state);
|
ADPCMState& state);
|
||||||
|
|
||||||
}; // namespace AudioCore::Codec
|
}; // namespace AudioCore::Codec
|
||||||
|
|
|
@ -67,12 +67,12 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||||
VoiceContext& voice_context, MixContext& mix_context,
|
VoiceContext& voice_context_, MixContext& mix_context_,
|
||||||
SplitterContext& splitter_context, EffectContext& effect_context,
|
SplitterContext& splitter_context_,
|
||||||
Core::Memory::Memory& memory)
|
EffectContext& effect_context_, Core::Memory::Memory& memory_)
|
||||||
: worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
|
: worker_params(worker_params_), voice_context(voice_context_), mix_context(mix_context_),
|
||||||
splitter_context(splitter_context), effect_context(effect_context), memory(memory),
|
splitter_context(splitter_context_), effect_context(effect_context_), memory(memory_),
|
||||||
mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
|
mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
|
||||||
worker_params.sample_count),
|
worker_params.sample_count),
|
||||||
sample_buffer(MIX_BUFFER_SIZE),
|
sample_buffer(MIX_BUFFER_SIZE),
|
||||||
|
@ -152,7 +152,7 @@ void CommandGenerator::GenerateVoiceCommand(ServerVoiceInfo& voice_info) {
|
||||||
if (!destination_data->IsConfigured()) {
|
if (!destination_data->IsConfigured()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (destination_data->GetMixId() >= mix_context.GetCount()) {
|
if (destination_data->GetMixId() >= static_cast<int>(mix_context.GetCount())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,7 +255,8 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo
|
||||||
|
|
||||||
void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
|
void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
|
||||||
VoiceState& dsp_state,
|
VoiceState& dsp_state,
|
||||||
s32 mix_buffer_count, s32 channel) {
|
[[maybe_unused]] s32 mix_buffer_count,
|
||||||
|
[[maybe_unused]] s32 channel) {
|
||||||
for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
|
for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
|
||||||
const auto& in_params = voice_info.GetInParams();
|
const auto& in_params = voice_info.GetInParams();
|
||||||
auto& biquad_filter = in_params.biquad_filter[i];
|
auto& biquad_filter = in_params.biquad_filter[i];
|
||||||
|
@ -278,9 +279,12 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioCore::CommandGenerator::GenerateBiquadFilterCommand(
|
void CommandGenerator::GenerateBiquadFilterCommand([[maybe_unused]] s32 mix_buffer_id,
|
||||||
s32 mix_buffer, const BiquadFilterParameter& params, std::array<s64, 2>& state,
|
const BiquadFilterParameter& params,
|
||||||
std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id) {
|
std::array<s64, 2>& state,
|
||||||
|
std::size_t input_offset,
|
||||||
|
std::size_t output_offset, s32 sample_count,
|
||||||
|
s32 node_id) {
|
||||||
if (dumping_frame) {
|
if (dumping_frame) {
|
||||||
LOG_DEBUG(Audio,
|
LOG_DEBUG(Audio,
|
||||||
"(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
|
"(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
|
||||||
|
@ -435,7 +439,7 @@ void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* inf
|
||||||
GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
|
GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
|
||||||
memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
|
memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
|
||||||
|
|
||||||
if (samples_read != worker_params.sample_count &&
|
if (samples_read != static_cast<int>(worker_params.sample_count) &&
|
||||||
samples_read <= params.sample_count) {
|
samples_read <= params.sample_count) {
|
||||||
std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
|
std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
|
||||||
}
|
}
|
||||||
|
@ -611,7 +615,8 @@ void CommandGenerator::GenerateMixCommands(ServerMixInfo& mix_info) {
|
||||||
const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
|
const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
|
||||||
const auto& dest_in_params = dest_mix.GetInParams();
|
const auto& dest_in_params = dest_mix.GetInParams();
|
||||||
const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
|
const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
|
||||||
for (std::size_t i = 0; i < dest_in_params.buffer_count; i++) {
|
for (std::size_t i = 0; i < static_cast<std::size_t>(dest_in_params.buffer_count);
|
||||||
|
i++) {
|
||||||
const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
|
const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
|
||||||
if (mixed_volume != 0.0f) {
|
if (mixed_volume != 0.0f) {
|
||||||
GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
|
GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
|
||||||
|
@ -704,7 +709,7 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
std::vector<s16> buffer(samples_processed * channel_count);
|
std::vector<s16> buffer(samples_processed * channel_count);
|
||||||
memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
|
memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
|
||||||
|
|
||||||
for (std::size_t i = 0; i < samples_processed; i++) {
|
for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) {
|
||||||
sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
|
sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,7 +718,8 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
|
s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
|
||||||
s32 sample_count, s32 channel, std::size_t mix_offset) {
|
s32 sample_count, [[maybe_unused]] s32 channel,
|
||||||
|
std::size_t mix_offset) {
|
||||||
const auto& in_params = voice_info.GetInParams();
|
const auto& in_params = voice_info.GetInParams();
|
||||||
const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
|
const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
|
||||||
if (wave_buffer.buffer_address == 0) {
|
if (wave_buffer.buffer_address == 0) {
|
||||||
|
@ -726,8 +732,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<int, 16> SIGNED_NIBBLES = {
|
static constexpr std::array<int, 16> SIGNED_NIBBLES{
|
||||||
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
|
0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
|
||||||
|
};
|
||||||
|
|
||||||
constexpr std::size_t FRAME_LEN = 8;
|
constexpr std::size_t FRAME_LEN = 8;
|
||||||
constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
|
constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
|
||||||
|
@ -789,9 +796,8 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
position_in_frame += 2;
|
position_in_frame += 2;
|
||||||
|
|
||||||
// Decode entire frame
|
// Decode entire frame
|
||||||
if (remaining_samples >= SAMPLES_PER_FRAME) {
|
if (remaining_samples >= static_cast<int>(SAMPLES_PER_FRAME)) {
|
||||||
for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
|
for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
|
||||||
|
|
||||||
// Sample 1
|
// Sample 1
|
||||||
const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4];
|
const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4];
|
||||||
const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf];
|
const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf];
|
||||||
|
@ -800,7 +806,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
sample_buffer[cur_mix_offset++] = sample_1;
|
sample_buffer[cur_mix_offset++] = sample_1;
|
||||||
sample_buffer[cur_mix_offset++] = sample_2;
|
sample_buffer[cur_mix_offset++] = sample_2;
|
||||||
}
|
}
|
||||||
remaining_samples -= SAMPLES_PER_FRAME;
|
remaining_samples -= static_cast<int>(SAMPLES_PER_FRAME);
|
||||||
position_in_frame += SAMPLES_PER_FRAME;
|
position_in_frame += SAMPLES_PER_FRAME;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -866,7 +872,6 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
const auto resample_rate = static_cast<s32>(
|
const auto resample_rate = static_cast<s32>(
|
||||||
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
|
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
|
||||||
static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
|
static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
|
||||||
auto* output_base = output;
|
|
||||||
if (dsp_state.fraction + sample_count * resample_rate >
|
if (dsp_state.fraction + sample_count * resample_rate >
|
||||||
static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
|
static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include "audio_core/common.h"
|
#include "audio_core/common.h"
|
||||||
#include "audio_core/voice_context.h"
|
#include "audio_core/voice_context.h"
|
||||||
#include "common/common_funcs.h"
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
|
||||||
namespace Core::Memory {
|
namespace Core::Memory {
|
||||||
|
@ -26,10 +25,10 @@ using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||||
|
|
||||||
class CommandGenerator {
|
class CommandGenerator {
|
||||||
public:
|
public:
|
||||||
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
|
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||||
VoiceContext& voice_context, MixContext& mix_context,
|
VoiceContext& voice_context_, MixContext& mix_context_,
|
||||||
SplitterContext& splitter_context, EffectContext& effect_context,
|
SplitterContext& splitter_context_, EffectContext& effect_context_,
|
||||||
Core::Memory::Memory& memory);
|
Core::Memory::Memory& memory_);
|
||||||
~CommandGenerator();
|
~CommandGenerator();
|
||||||
|
|
||||||
void ClearMixBuffers();
|
void ClearMixBuffers();
|
||||||
|
@ -40,13 +39,13 @@ public:
|
||||||
void PreCommand();
|
void PreCommand();
|
||||||
void PostCommand();
|
void PostCommand();
|
||||||
|
|
||||||
s32* GetChannelMixBuffer(s32 channel);
|
[[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
|
||||||
const s32* GetChannelMixBuffer(s32 channel) const;
|
[[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
|
||||||
s32* GetMixBuffer(std::size_t index);
|
[[nodiscard]] s32* GetMixBuffer(std::size_t index);
|
||||||
const s32* GetMixBuffer(std::size_t index) const;
|
[[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
|
||||||
std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||||
|
|
||||||
std::size_t GetTotalMixBufferCount() const;
|
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
|
void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
|
||||||
|
@ -74,7 +73,7 @@ private:
|
||||||
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||||
|
|
||||||
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
||||||
u32 sample_count, u32 write_offset, u32 write_count);
|
u32 sample_count, u32 write_offset, u32 write_count);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
|
@ -21,7 +22,7 @@ constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||||
constexpr std::size_t MAX_WAVE_BUFFERS = 4;
|
constexpr std::size_t MAX_WAVE_BUFFERS = 4;
|
||||||
constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
|
constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
|
||||||
constexpr u32 STREAM_SAMPLE_RATE = 48000;
|
constexpr u32 STREAM_SAMPLE_RATE = 48000;
|
||||||
constexpr u32 STREAM_NUM_CHANNELS = 6;
|
constexpr u32 STREAM_NUM_CHANNELS = 2;
|
||||||
constexpr s32 NO_SPLITTER = -1;
|
constexpr s32 NO_SPLITTER = -1;
|
||||||
constexpr s32 NO_MIX = 0x7fffffff;
|
constexpr s32 NO_MIX = 0x7fffffff;
|
||||||
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
||||||
|
|
|
@ -21,15 +21,16 @@ namespace AudioCore {
|
||||||
|
|
||||||
class CubebSinkStream final : public SinkStream {
|
class CubebSinkStream final : public SinkStream {
|
||||||
public:
|
public:
|
||||||
CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
CubebSinkStream(cubeb* ctx_, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||||
const std::string& name)
|
const std::string& name)
|
||||||
: ctx{ctx}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
|
: ctx{ctx_}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
|
||||||
num_channels} {
|
num_channels} {
|
||||||
|
|
||||||
cubeb_stream_params params{};
|
cubeb_stream_params params{};
|
||||||
params.rate = sample_rate;
|
params.rate = sample_rate;
|
||||||
params.channels = num_channels;
|
params.channels = num_channels;
|
||||||
params.format = CUBEB_SAMPLE_S16NE;
|
params.format = CUBEB_SAMPLE_S16NE;
|
||||||
|
params.prefs = CUBEB_STREAM_PREF_PERSIST;
|
||||||
switch (num_channels) {
|
switch (num_channels) {
|
||||||
case 1:
|
case 1:
|
||||||
params.layout = CUBEB_LAYOUT_MONO;
|
params.layout = CUBEB_LAYOUT_MONO;
|
||||||
|
@ -93,8 +94,10 @@ public:
|
||||||
constexpr s32 clev{707}; // center mixing level coefficient
|
constexpr s32 clev{707}; // center mixing level coefficient
|
||||||
constexpr s32 slev{707}; // surround mixing level coefficient
|
constexpr s32 slev{707}; // surround mixing level coefficient
|
||||||
|
|
||||||
buf.push_back(left + (clev * center / 1000) + (slev * surround_left / 1000));
|
buf.push_back(static_cast<s16>(left + (clev * center / 1000) +
|
||||||
buf.push_back(right + (clev * center / 1000) + (slev * surround_right / 1000));
|
(slev * surround_left / 1000)));
|
||||||
|
buf.push_back(static_cast<s16>(right + (clev * center / 1000) +
|
||||||
|
(slev * surround_right / 1000)));
|
||||||
}
|
}
|
||||||
queue.Push(buf);
|
queue.Push(buf);
|
||||||
return;
|
return;
|
||||||
|
@ -190,10 +193,11 @@ SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||||
return *sink_streams.back();
|
return *sink_streams.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void* user_data,
|
||||||
void* output_buffer, long num_frames) {
|
[[maybe_unused]] const void* input_buffer, void* output_buffer,
|
||||||
CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
|
long num_frames) {
|
||||||
u8* buffer = reinterpret_cast<u8*>(output_buffer);
|
auto* impl = static_cast<CubebSinkStream*>(user_data);
|
||||||
|
auto* buffer = static_cast<u8*>(output_buffer);
|
||||||
|
|
||||||
if (!impl) {
|
if (!impl) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -234,7 +238,9 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
|
||||||
return num_frames;
|
return num_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
void CubebSinkStream::StateCallback([[maybe_unused]] cubeb_stream* stream,
|
||||||
|
[[maybe_unused]] void* user_data,
|
||||||
|
[[maybe_unused]] cubeb_state state) {}
|
||||||
|
|
||||||
std::vector<std::string> ListCubebSinkDevices() {
|
std::vector<std::string> ListCubebSinkDevices() {
|
||||||
std::vector<std::string> device_list;
|
std::vector<std::string> device_list;
|
||||||
|
|
|
@ -12,7 +12,7 @@ bool ValidChannelCountForEffect(s32 channel_count) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
|
EffectContext::EffectContext(std::size_t effect_count_) : effect_count(effect_count_) {
|
||||||
effects.reserve(effect_count);
|
effects.reserve(effect_count);
|
||||||
std::generate_n(std::back_inserter(effects), effect_count,
|
std::generate_n(std::back_inserter(effects), effect_count,
|
||||||
[] { return std::make_unique<EffectStubbed>(); });
|
[] { return std::make_unique<EffectStubbed>(); });
|
||||||
|
@ -61,13 +61,13 @@ const EffectBase* EffectContext::GetInfo(std::size_t i) const {
|
||||||
return effects.at(i).get();
|
return effects.at(i).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
|
EffectStubbed::EffectStubbed() : EffectBase(EffectType::Invalid) {}
|
||||||
EffectStubbed::~EffectStubbed() = default;
|
EffectStubbed::~EffectStubbed() = default;
|
||||||
|
|
||||||
void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
|
void EffectStubbed::Update([[maybe_unused]] EffectInfo::InParams& in_params) {}
|
||||||
void EffectStubbed::UpdateForCommandGeneration() {}
|
void EffectStubbed::UpdateForCommandGeneration() {}
|
||||||
|
|
||||||
EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
|
EffectBase::EffectBase(EffectType effect_type_) : effect_type(effect_type_) {}
|
||||||
EffectBase::~EffectBase() = default;
|
EffectBase::~EffectBase() = default;
|
||||||
|
|
||||||
UsageState EffectBase::GetUsage() const {
|
UsageState EffectBase::GetUsage() const {
|
||||||
|
@ -90,32 +90,32 @@ s32 EffectBase::GetProcessingOrder() const {
|
||||||
return processing_order;
|
return processing_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
|
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
|
||||||
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
|
||||||
|
|
||||||
void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
|
||||||
auto& internal_params = GetParams();
|
auto& params = GetParams();
|
||||||
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
|
const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
|
||||||
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||||
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
|
UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto last_status = internal_params.status;
|
const auto last_status = params.status;
|
||||||
mix_id = in_params.mix_id;
|
mix_id = in_params.mix_id;
|
||||||
processing_order = in_params.processing_order;
|
processing_order = in_params.processing_order;
|
||||||
internal_params = *reverb_params;
|
params = *reverb_params;
|
||||||
if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
|
if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
|
||||||
internal_params.channel_count = internal_params.max_channels;
|
params.channel_count = params.max_channels;
|
||||||
}
|
}
|
||||||
enabled = in_params.is_enabled;
|
enabled = in_params.is_enabled;
|
||||||
if (last_status != ParameterStatus::Updated) {
|
if (last_status != ParameterStatus::Updated) {
|
||||||
internal_params.status = last_status;
|
params.status = last_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_params.is_new || skipped) {
|
if (in_params.is_new || skipped) {
|
||||||
usage = UsageState::Initialized;
|
usage = UsageState::Initialized;
|
||||||
internal_params.status = ParameterStatus::Initialized;
|
params.status = ParameterStatus::Initialized;
|
||||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,15 +129,15 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
|
||||||
GetParams().status = ParameterStatus::Updated;
|
GetParams().status = ParameterStatus::Updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
|
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
|
||||||
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
EffectBiquadFilter::~EffectBiquadFilter() = default;
|
||||||
|
|
||||||
void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
|
void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
|
||||||
auto& internal_params = GetParams();
|
auto& params = GetParams();
|
||||||
const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
|
const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
|
||||||
mix_id = in_params.mix_id;
|
mix_id = in_params.mix_id;
|
||||||
processing_order = in_params.processing_order;
|
processing_order = in_params.processing_order;
|
||||||
internal_params = *biquad_params;
|
params = *biquad_params;
|
||||||
enabled = in_params.is_enabled;
|
enabled = in_params.is_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ void EffectBiquadFilter::UpdateForCommandGeneration() {
|
||||||
GetParams().status = ParameterStatus::Updated;
|
GetParams().status = ParameterStatus::Updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {}
|
EffectAuxInfo::EffectAuxInfo() : EffectGeneric(EffectType::Aux) {}
|
||||||
EffectAuxInfo::~EffectAuxInfo() = default;
|
EffectAuxInfo::~EffectAuxInfo() = default;
|
||||||
|
|
||||||
void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
|
void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
|
||||||
|
@ -184,48 +184,48 @@ void EffectAuxInfo::UpdateForCommandGeneration() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VAddr EffectAuxInfo::GetSendInfo() const {
|
VAddr EffectAuxInfo::GetSendInfo() const {
|
||||||
return send_info;
|
return send_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VAddr EffectAuxInfo::GetSendBuffer() const {
|
VAddr EffectAuxInfo::GetSendBuffer() const {
|
||||||
return send_buffer;
|
return send_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VAddr EffectAuxInfo::GetRecvInfo() const {
|
VAddr EffectAuxInfo::GetRecvInfo() const {
|
||||||
return recv_info;
|
return recv_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VAddr EffectAuxInfo::GetRecvBuffer() const {
|
VAddr EffectAuxInfo::GetRecvBuffer() const {
|
||||||
return recv_buffer;
|
return recv_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {}
|
EffectDelay::EffectDelay() : EffectGeneric(EffectType::Delay) {}
|
||||||
EffectDelay::~EffectDelay() = default;
|
EffectDelay::~EffectDelay() = default;
|
||||||
|
|
||||||
void EffectDelay::Update(EffectInfo::InParams& in_params) {
|
void EffectDelay::Update(EffectInfo::InParams& in_params) {
|
||||||
const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
|
const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
|
||||||
auto& internal_params = GetParams();
|
auto& params = GetParams();
|
||||||
if (!ValidChannelCountForEffect(delay_params->max_channels)) {
|
if (!ValidChannelCountForEffect(delay_params->max_channels)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto last_status = internal_params.status;
|
const auto last_status = params.status;
|
||||||
mix_id = in_params.mix_id;
|
mix_id = in_params.mix_id;
|
||||||
processing_order = in_params.processing_order;
|
processing_order = in_params.processing_order;
|
||||||
internal_params = *delay_params;
|
params = *delay_params;
|
||||||
if (!ValidChannelCountForEffect(delay_params->channels)) {
|
if (!ValidChannelCountForEffect(delay_params->channels)) {
|
||||||
internal_params.channels = internal_params.max_channels;
|
params.channels = params.max_channels;
|
||||||
}
|
}
|
||||||
enabled = in_params.is_enabled;
|
enabled = in_params.is_enabled;
|
||||||
|
|
||||||
if (last_status != ParameterStatus::Updated) {
|
if (last_status != ParameterStatus::Updated) {
|
||||||
internal_params.status = last_status;
|
params.status = last_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_params.is_new || skipped) {
|
if (in_params.is_new || skipped) {
|
||||||
usage = UsageState::Initialized;
|
usage = UsageState::Initialized;
|
||||||
internal_params.status = ParameterStatus::Initialized;
|
params.status = ParameterStatus::Initialized;
|
||||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,7 @@ void EffectDelay::UpdateForCommandGeneration() {
|
||||||
GetParams().status = ParameterStatus::Updated;
|
GetParams().status = ParameterStatus::Updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {}
|
EffectBufferMixer::EffectBufferMixer() : EffectGeneric(EffectType::BufferMixer) {}
|
||||||
EffectBufferMixer::~EffectBufferMixer() = default;
|
EffectBufferMixer::~EffectBufferMixer() = default;
|
||||||
|
|
||||||
void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
|
void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
|
||||||
|
@ -257,32 +257,32 @@ void EffectBufferMixer::UpdateForCommandGeneration() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {}
|
EffectReverb::EffectReverb() : EffectGeneric(EffectType::Reverb) {}
|
||||||
EffectReverb::~EffectReverb() = default;
|
EffectReverb::~EffectReverb() = default;
|
||||||
|
|
||||||
void EffectReverb::Update(EffectInfo::InParams& in_params) {
|
void EffectReverb::Update(EffectInfo::InParams& in_params) {
|
||||||
const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
|
const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
|
||||||
auto& internal_params = GetParams();
|
auto& params = GetParams();
|
||||||
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto last_status = internal_params.status;
|
const auto last_status = params.status;
|
||||||
mix_id = in_params.mix_id;
|
mix_id = in_params.mix_id;
|
||||||
processing_order = in_params.processing_order;
|
processing_order = in_params.processing_order;
|
||||||
internal_params = *reverb_params;
|
params = *reverb_params;
|
||||||
if (!ValidChannelCountForEffect(reverb_params->channels)) {
|
if (!ValidChannelCountForEffect(reverb_params->channels)) {
|
||||||
internal_params.channels = internal_params.max_channels;
|
params.channels = params.max_channels;
|
||||||
}
|
}
|
||||||
enabled = in_params.is_enabled;
|
enabled = in_params.is_enabled;
|
||||||
|
|
||||||
if (last_status != ParameterStatus::Updated) {
|
if (last_status != ParameterStatus::Updated) {
|
||||||
internal_params.status = last_status;
|
params.status = last_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_params.is_new || skipped) {
|
if (in_params.is_new || skipped) {
|
||||||
usage = UsageState::Initialized;
|
usage = UsageState::Initialized;
|
||||||
internal_params.status = ParameterStatus::Initialized;
|
params.status = ParameterStatus::Initialized;
|
||||||
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,13 +166,13 @@ public:
|
||||||
std::array<u8, 0xa0> raw;
|
std::array<u8, 0xa0> raw;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(EffectInfo::InParams) == 0xc0, "InParams is an invalid size");
|
static_assert(sizeof(InParams) == 0xc0, "InParams is an invalid size");
|
||||||
|
|
||||||
struct OutParams {
|
struct OutParams {
|
||||||
UsageStatus status{};
|
UsageStatus status{};
|
||||||
INSERT_PADDING_BYTES(15);
|
INSERT_PADDING_BYTES(15);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
|
static_assert(sizeof(OutParams) == 0x10, "OutParams is an invalid size");
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AuxAddress {
|
struct AuxAddress {
|
||||||
|
@ -184,16 +184,16 @@ struct AuxAddress {
|
||||||
|
|
||||||
class EffectBase {
|
class EffectBase {
|
||||||
public:
|
public:
|
||||||
EffectBase(EffectType effect_type);
|
explicit EffectBase(EffectType effect_type_);
|
||||||
~EffectBase();
|
virtual ~EffectBase();
|
||||||
|
|
||||||
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
virtual void Update(EffectInfo::InParams& in_params) = 0;
|
||||||
virtual void UpdateForCommandGeneration() = 0;
|
virtual void UpdateForCommandGeneration() = 0;
|
||||||
UsageState GetUsage() const;
|
[[nodiscard]] UsageState GetUsage() const;
|
||||||
EffectType GetType() const;
|
[[nodiscard]] EffectType GetType() const;
|
||||||
bool IsEnabled() const;
|
[[nodiscard]] bool IsEnabled() const;
|
||||||
s32 GetMixID() const;
|
[[nodiscard]] s32 GetMixID() const;
|
||||||
s32 GetProcessingOrder() const;
|
[[nodiscard]] s32 GetProcessingOrder() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
UsageState usage{UsageState::Invalid};
|
UsageState usage{UsageState::Invalid};
|
||||||
|
@ -206,8 +206,7 @@ protected:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class EffectGeneric : public EffectBase {
|
class EffectGeneric : public EffectBase {
|
||||||
public:
|
public:
|
||||||
EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {}
|
explicit EffectGeneric(EffectType effect_type_) : EffectBase(effect_type_) {}
|
||||||
~EffectGeneric() = default;
|
|
||||||
|
|
||||||
T& GetParams() {
|
T& GetParams() {
|
||||||
return internal_params;
|
return internal_params;
|
||||||
|
@ -224,7 +223,7 @@ private:
|
||||||
class EffectStubbed : public EffectBase {
|
class EffectStubbed : public EffectBase {
|
||||||
public:
|
public:
|
||||||
explicit EffectStubbed();
|
explicit EffectStubbed();
|
||||||
~EffectStubbed();
|
~EffectStubbed() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
|
@ -233,7 +232,7 @@ public:
|
||||||
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
|
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
|
||||||
public:
|
public:
|
||||||
explicit EffectI3dl2Reverb();
|
explicit EffectI3dl2Reverb();
|
||||||
~EffectI3dl2Reverb();
|
~EffectI3dl2Reverb() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
|
@ -245,7 +244,7 @@ private:
|
||||||
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
|
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
|
||||||
public:
|
public:
|
||||||
explicit EffectBiquadFilter();
|
explicit EffectBiquadFilter();
|
||||||
~EffectBiquadFilter();
|
~EffectBiquadFilter() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
|
@ -254,14 +253,14 @@ public:
|
||||||
class EffectAuxInfo : public EffectGeneric<AuxInfo> {
|
class EffectAuxInfo : public EffectGeneric<AuxInfo> {
|
||||||
public:
|
public:
|
||||||
explicit EffectAuxInfo();
|
explicit EffectAuxInfo();
|
||||||
~EffectAuxInfo();
|
~EffectAuxInfo() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
const VAddr GetSendInfo() const;
|
[[nodiscard]] VAddr GetSendInfo() const;
|
||||||
const VAddr GetSendBuffer() const;
|
[[nodiscard]] VAddr GetSendBuffer() const;
|
||||||
const VAddr GetRecvInfo() const;
|
[[nodiscard]] VAddr GetRecvInfo() const;
|
||||||
const VAddr GetRecvBuffer() const;
|
[[nodiscard]] VAddr GetRecvBuffer() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VAddr send_info{};
|
VAddr send_info{};
|
||||||
|
@ -275,7 +274,7 @@ private:
|
||||||
class EffectDelay : public EffectGeneric<DelayParams> {
|
class EffectDelay : public EffectGeneric<DelayParams> {
|
||||||
public:
|
public:
|
||||||
explicit EffectDelay();
|
explicit EffectDelay();
|
||||||
~EffectDelay();
|
~EffectDelay() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
|
@ -287,7 +286,7 @@ private:
|
||||||
class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
|
class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
|
||||||
public:
|
public:
|
||||||
explicit EffectBufferMixer();
|
explicit EffectBufferMixer();
|
||||||
~EffectBufferMixer();
|
~EffectBufferMixer() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
|
@ -296,7 +295,7 @@ public:
|
||||||
class EffectReverb : public EffectGeneric<ReverbParams> {
|
class EffectReverb : public EffectGeneric<ReverbParams> {
|
||||||
public:
|
public:
|
||||||
explicit EffectReverb();
|
explicit EffectReverb();
|
||||||
~EffectReverb();
|
~EffectReverb() override;
|
||||||
|
|
||||||
void Update(EffectInfo::InParams& in_params) override;
|
void Update(EffectInfo::InParams& in_params) override;
|
||||||
void UpdateForCommandGeneration() override;
|
void UpdateForCommandGeneration() override;
|
||||||
|
@ -307,13 +306,13 @@ private:
|
||||||
|
|
||||||
class EffectContext {
|
class EffectContext {
|
||||||
public:
|
public:
|
||||||
explicit EffectContext(std::size_t effect_count);
|
explicit EffectContext(std::size_t effect_count_);
|
||||||
~EffectContext();
|
~EffectContext();
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
[[nodiscard]] std::size_t GetCount() const;
|
||||||
EffectBase* GetInfo(std::size_t i);
|
[[nodiscard]] EffectBase* GetInfo(std::size_t i);
|
||||||
EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
[[nodiscard]] EffectBase* RetargetEffect(std::size_t i, EffectType effect);
|
||||||
const EffectBase* GetInfo(std::size_t i) const;
|
[[nodiscard]] const EffectBase* GetInfo(std::size_t i) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::size_t effect_count{};
|
std::size_t effect_count{};
|
||||||
|
|
|
@ -14,9 +14,9 @@
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
InfoUpdater::InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
|
InfoUpdater::InfoUpdater(const std::vector<u8>& in_params_, std::vector<u8>& out_params_,
|
||||||
BehaviorInfo& behavior_info)
|
BehaviorInfo& behavior_info_)
|
||||||
: in_params(in_params), out_params(out_params), behavior_info(behavior_info) {
|
: in_params(in_params_), out_params(out_params_), behavior_info(behavior_info_) {
|
||||||
ASSERT(
|
ASSERT(
|
||||||
AudioCommon::CanConsumeBuffer(in_params.size(), 0, sizeof(AudioCommon::UpdateDataHeader)));
|
AudioCommon::CanConsumeBuffer(in_params.size(), 0, sizeof(AudioCommon::UpdateDataHeader)));
|
||||||
std::memcpy(&input_header, in_params.data(), sizeof(AudioCommon::UpdateDataHeader));
|
std::memcpy(&input_header, in_params.data(), sizeof(AudioCommon::UpdateDataHeader));
|
||||||
|
@ -64,7 +64,6 @@ bool InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& in_behavior_info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InfoUpdater::UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info) {
|
bool InfoUpdater::UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info) {
|
||||||
const auto force_mapping = behavior_info.IsMemoryPoolForceMappingEnabled();
|
|
||||||
const auto memory_pool_count = memory_pool_info.size();
|
const auto memory_pool_count = memory_pool_info.size();
|
||||||
const auto total_memory_pool_in = sizeof(ServerMemoryPoolInfo::InParams) * memory_pool_count;
|
const auto total_memory_pool_in = sizeof(ServerMemoryPoolInfo::InParams) * memory_pool_count;
|
||||||
const auto total_memory_pool_out = sizeof(ServerMemoryPoolInfo::OutParams) * memory_pool_count;
|
const auto total_memory_pool_out = sizeof(ServerMemoryPoolInfo::OutParams) * memory_pool_count;
|
||||||
|
@ -136,8 +135,8 @@ bool InfoUpdater::UpdateVoiceChannelResources(VoiceContext& voice_context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
std::vector<ServerMemoryPoolInfo>& memory_pool_info,
|
[[maybe_unused]] std::vector<ServerMemoryPoolInfo>& memory_pool_info,
|
||||||
VAddr audio_codec_dsp_addr) {
|
[[maybe_unused]] VAddr audio_codec_dsp_addr) {
|
||||||
const auto voice_count = voice_context.GetVoiceCount();
|
const auto voice_count = voice_context.GetVoiceCount();
|
||||||
std::vector<VoiceInfo::InParams> voice_in(voice_count);
|
std::vector<VoiceInfo::InParams> voice_in(voice_count);
|
||||||
std::vector<VoiceInfo::OutParams> voice_out(voice_count);
|
std::vector<VoiceInfo::OutParams> voice_out(voice_count);
|
||||||
|
@ -166,28 +165,28 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
|
|
||||||
// Update our voices
|
// Update our voices
|
||||||
for (std::size_t i = 0; i < voice_count; i++) {
|
for (std::size_t i = 0; i < voice_count; i++) {
|
||||||
auto& in_params = voice_in[i];
|
auto& voice_in_params = voice_in[i];
|
||||||
const auto channel_count = static_cast<std::size_t>(in_params.channel_count);
|
const auto channel_count = static_cast<std::size_t>(voice_in_params.channel_count);
|
||||||
// Skip if it's not currently in use
|
// Skip if it's not currently in use
|
||||||
if (!in_params.is_in_use) {
|
if (!voice_in_params.is_in_use) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Voice states for each channel
|
// Voice states for each channel
|
||||||
std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
|
std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
|
||||||
ASSERT(in_params.id < voice_count);
|
ASSERT(static_cast<std::size_t>(voice_in_params.id) < voice_count);
|
||||||
|
|
||||||
// Grab our current voice info
|
// Grab our current voice info
|
||||||
auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(in_params.id));
|
auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(voice_in_params.id));
|
||||||
|
|
||||||
ASSERT(channel_count <= AudioCommon::MAX_CHANNEL_COUNT);
|
ASSERT(channel_count <= AudioCommon::MAX_CHANNEL_COUNT);
|
||||||
|
|
||||||
// Get all our channel voice states
|
// Get all our channel voice states
|
||||||
for (std::size_t channel = 0; channel < channel_count; channel++) {
|
for (std::size_t channel = 0; channel < channel_count; channel++) {
|
||||||
voice_states[channel] =
|
voice_states[channel] =
|
||||||
&voice_context.GetState(in_params.voice_channel_resource_ids[channel]);
|
&voice_context.GetState(voice_in_params.voice_channel_resource_ids[channel]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_params.is_new) {
|
if (voice_in_params.is_new) {
|
||||||
// Default our values for our voice
|
// Default our values for our voice
|
||||||
voice_info.Initialize();
|
voice_info.Initialize();
|
||||||
if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
|
if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
|
||||||
|
@ -201,12 +200,12 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update our voice
|
// Update our voice
|
||||||
voice_info.UpdateParameters(in_params, behavior_info);
|
voice_info.UpdateParameters(voice_in_params, behavior_info);
|
||||||
// TODO(ogniK): Handle mapping errors with behavior info based on in params response
|
// TODO(ogniK): Handle mapping errors with behavior info based on in params response
|
||||||
|
|
||||||
// Update our wave buffers
|
// Update our wave buffers
|
||||||
voice_info.UpdateWaveBuffers(in_params, voice_states, behavior_info);
|
voice_info.UpdateWaveBuffers(voice_in_params, voice_states, behavior_info);
|
||||||
voice_info.WriteOutStatus(voice_out[i], in_params, voice_states);
|
voice_info.WriteOutStatus(voice_out[i], voice_in_params, voice_states);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, voice_out_size)) {
|
if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, voice_out_size)) {
|
||||||
|
@ -352,8 +351,8 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf
|
||||||
for (std::size_t i = 0; i < mix_count; i++) {
|
for (std::size_t i = 0; i < mix_count; i++) {
|
||||||
const auto& in = mix_in_params[i];
|
const auto& in = mix_in_params[i];
|
||||||
total_buffer_count += in.buffer_count;
|
total_buffer_count += in.buffer_count;
|
||||||
if (in.dest_mix_id > mix_count && in.dest_mix_id != AudioCommon::NO_MIX &&
|
if (static_cast<std::size_t>(in.dest_mix_id) > mix_count &&
|
||||||
in.mix_id != AudioCommon::FINAL_MIX) {
|
in.dest_mix_id != AudioCommon::NO_MIX && in.mix_id != AudioCommon::FINAL_MIX) {
|
||||||
LOG_ERROR(
|
LOG_ERROR(
|
||||||
Audio,
|
Audio,
|
||||||
"Invalid mix destination, mix_id={:X}, dest_mix_id={:X}, mix_buffer_count={:X}",
|
"Invalid mix destination, mix_id={:X}, dest_mix_id={:X}, mix_buffer_count={:X}",
|
||||||
|
@ -446,7 +445,7 @@ bool InfoUpdater::UpdatePerformanceBuffer() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InfoUpdater::UpdateErrorInfo(BehaviorInfo& in_behavior_info) {
|
bool InfoUpdater::UpdateErrorInfo([[maybe_unused]] BehaviorInfo& in_behavior_info) {
|
||||||
const auto total_beahvior_info_out = sizeof(BehaviorInfo::OutParams);
|
const auto total_beahvior_info_out = sizeof(BehaviorInfo::OutParams);
|
||||||
|
|
||||||
if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_beahvior_info_out)) {
|
if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_beahvior_info_out)) {
|
||||||
|
|
|
@ -21,8 +21,8 @@ class SplitterContext;
|
||||||
class InfoUpdater {
|
class InfoUpdater {
|
||||||
public:
|
public:
|
||||||
// TODO(ogniK): Pass process handle when we support it
|
// TODO(ogniK): Pass process handle when we support it
|
||||||
InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
|
InfoUpdater(const std::vector<u8>& in_params_, std::vector<u8>& out_params_,
|
||||||
BehaviorInfo& behavior_info);
|
BehaviorInfo& behavior_info_);
|
||||||
~InfoUpdater();
|
~InfoUpdater();
|
||||||
|
|
||||||
bool UpdateBehaviorInfo(BehaviorInfo& in_behavior_info);
|
bool UpdateBehaviorInfo(BehaviorInfo& in_behavior_info);
|
||||||
|
|
|
@ -10,11 +10,10 @@ namespace AudioCore {
|
||||||
|
|
||||||
ServerMemoryPoolInfo::ServerMemoryPoolInfo() = default;
|
ServerMemoryPoolInfo::ServerMemoryPoolInfo() = default;
|
||||||
ServerMemoryPoolInfo::~ServerMemoryPoolInfo() = default;
|
ServerMemoryPoolInfo::~ServerMemoryPoolInfo() = default;
|
||||||
bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_params,
|
|
||||||
ServerMemoryPoolInfo::OutParams& out_params) {
|
bool ServerMemoryPoolInfo::Update(const InParams& in_params, OutParams& out_params) {
|
||||||
// Our state does not need to be changed
|
// Our state does not need to be changed
|
||||||
if (in_params.state != ServerMemoryPoolInfo::State::RequestAttach &&
|
if (in_params.state != State::RequestAttach && in_params.state != State::RequestDetach) {
|
||||||
in_params.state != ServerMemoryPoolInfo::State::RequestDetach) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,11 +31,11 @@ bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_param
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_params.state == ServerMemoryPoolInfo::State::RequestAttach) {
|
if (in_params.state == State::RequestAttach) {
|
||||||
cpu_address = in_params.address;
|
cpu_address = in_params.address;
|
||||||
size = in_params.size;
|
size = in_params.size;
|
||||||
used = true;
|
used = true;
|
||||||
out_params.state = ServerMemoryPoolInfo::State::Attached;
|
out_params.state = State::Attached;
|
||||||
} else {
|
} else {
|
||||||
// Unexpected address
|
// Unexpected address
|
||||||
if (cpu_address != in_params.address) {
|
if (cpu_address != in_params.address) {
|
||||||
|
@ -54,7 +53,7 @@ bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_param
|
||||||
cpu_address = 0;
|
cpu_address = 0;
|
||||||
size = 0;
|
size = 0;
|
||||||
used = false;
|
used = false;
|
||||||
out_params.state = ServerMemoryPoolInfo::State::Detached;
|
out_params.state = State::Detached;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,19 +28,18 @@ public:
|
||||||
struct InParams {
|
struct InParams {
|
||||||
u64_le address{};
|
u64_le address{};
|
||||||
u64_le size{};
|
u64_le size{};
|
||||||
ServerMemoryPoolInfo::State state{};
|
State state{};
|
||||||
INSERT_PADDING_WORDS(3);
|
INSERT_PADDING_WORDS(3);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ServerMemoryPoolInfo::InParams) == 0x20, "InParams are an invalid size");
|
static_assert(sizeof(InParams) == 0x20, "InParams are an invalid size");
|
||||||
|
|
||||||
struct OutParams {
|
struct OutParams {
|
||||||
ServerMemoryPoolInfo::State state{};
|
State state{};
|
||||||
INSERT_PADDING_WORDS(3);
|
INSERT_PADDING_WORDS(3);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(ServerMemoryPoolInfo::OutParams) == 0x10, "OutParams are an invalid size");
|
static_assert(sizeof(OutParams) == 0x10, "OutParams are an invalid size");
|
||||||
|
|
||||||
bool Update(const ServerMemoryPoolInfo::InParams& in_params,
|
bool Update(const InParams& in_params, OutParams& out_params);
|
||||||
ServerMemoryPoolInfo::OutParams& out_params);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// There's another entry here which is the DSP address, however since we're not talking to the
|
// There's another entry here which is the DSP address, however since we're not talking to the
|
||||||
|
|
|
@ -53,7 +53,7 @@ void MixContext::UpdateDistancesFromFinalMix() {
|
||||||
auto mix_id = in_params.mix_id;
|
auto mix_id = in_params.mix_id;
|
||||||
// Needs to be referenced out of scope
|
// Needs to be referenced out of scope
|
||||||
s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
|
s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
|
||||||
for (; distance_to_final_mix < info_count; distance_to_final_mix++) {
|
for (; distance_to_final_mix < static_cast<s32>(info_count); distance_to_final_mix++) {
|
||||||
if (mix_id == AudioCommon::FINAL_MIX) {
|
if (mix_id == AudioCommon::FINAL_MIX) {
|
||||||
// If we're at the final mix, we're done
|
// If we're at the final mix, we're done
|
||||||
break;
|
break;
|
||||||
|
@ -77,7 +77,7 @@ void MixContext::UpdateDistancesFromFinalMix() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're out of range for our distance, mark it as no final mix
|
// If we're out of range for our distance, mark it as no final mix
|
||||||
if (distance_to_final_mix >= info_count) {
|
if (distance_to_final_mix >= static_cast<s32>(info_count)) {
|
||||||
distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
|
distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,17 +62,17 @@ public:
|
||||||
ServerMixInfo();
|
ServerMixInfo();
|
||||||
~ServerMixInfo();
|
~ServerMixInfo();
|
||||||
|
|
||||||
const ServerMixInfo::InParams& GetInParams() const;
|
[[nodiscard]] const ServerMixInfo::InParams& GetInParams() const;
|
||||||
ServerMixInfo::InParams& GetInParams();
|
[[nodiscard]] ServerMixInfo::InParams& GetInParams();
|
||||||
|
|
||||||
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
|
||||||
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
BehaviorInfo& behavior_info, SplitterContext& splitter_context,
|
||||||
EffectContext& effect_context);
|
EffectContext& effect_context);
|
||||||
bool HasAnyConnection() const;
|
[[nodiscard]] bool HasAnyConnection() const;
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
void SetEffectCount(std::size_t count);
|
void SetEffectCount(std::size_t count);
|
||||||
void ResetEffectProcessingOrder();
|
void ResetEffectProcessingOrder();
|
||||||
s32 GetEffectOrder(std::size_t i) const;
|
[[nodiscard]] s32 GetEffectOrder(std::size_t i) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<s32> effect_processing_order;
|
std::vector<s32> effect_processing_order;
|
||||||
|
@ -91,15 +91,15 @@ public:
|
||||||
void SortInfo();
|
void SortInfo();
|
||||||
bool TsortInfo(SplitterContext& splitter_context);
|
bool TsortInfo(SplitterContext& splitter_context);
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
[[nodiscard]] std::size_t GetCount() const;
|
||||||
ServerMixInfo& GetInfo(std::size_t i);
|
[[nodiscard]] ServerMixInfo& GetInfo(std::size_t i);
|
||||||
const ServerMixInfo& GetInfo(std::size_t i) const;
|
[[nodiscard]] const ServerMixInfo& GetInfo(std::size_t i) const;
|
||||||
ServerMixInfo& GetSortedInfo(std::size_t i);
|
[[nodiscard]] ServerMixInfo& GetSortedInfo(std::size_t i);
|
||||||
const ServerMixInfo& GetSortedInfo(std::size_t i) const;
|
[[nodiscard]] const ServerMixInfo& GetSortedInfo(std::size_t i) const;
|
||||||
ServerMixInfo& GetFinalMixInfo();
|
[[nodiscard]] ServerMixInfo& GetFinalMixInfo();
|
||||||
const ServerMixInfo& GetFinalMixInfo() const;
|
[[nodiscard]] const ServerMixInfo& GetFinalMixInfo() const;
|
||||||
EdgeMatrix& GetEdgeMatrix();
|
[[nodiscard]] EdgeMatrix& GetEdgeMatrix();
|
||||||
const EdgeMatrix& GetEdgeMatrix() const;
|
[[nodiscard]] const EdgeMatrix& GetEdgeMatrix() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CalcMixBufferOffset();
|
void CalcMixBufferOffset();
|
||||||
|
|
|
@ -5,17 +5,23 @@
|
||||||
#include "audio_core/sink_context.h"
|
#include "audio_core/sink_context.h"
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
SinkContext::SinkContext(std::size_t sink_count) : sink_count(sink_count) {}
|
SinkContext::SinkContext(std::size_t sink_count_) : sink_count{sink_count_} {}
|
||||||
SinkContext::~SinkContext() = default;
|
SinkContext::~SinkContext() = default;
|
||||||
|
|
||||||
std::size_t SinkContext::GetCount() const {
|
std::size_t SinkContext::GetCount() const {
|
||||||
return sink_count;
|
return sink_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SinkContext::UpdateMainSink(SinkInfo::InParams& in) {
|
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
||||||
|
ASSERT(in.type == SinkTypes::Device);
|
||||||
|
|
||||||
|
has_downmix_coefs = in.device.down_matrix_enabled;
|
||||||
|
if (has_downmix_coefs) {
|
||||||
|
downmix_coefficients = in.device.down_matrix_coef;
|
||||||
|
}
|
||||||
in_use = in.in_use;
|
in_use = in.in_use;
|
||||||
use_count = in.device.input_count;
|
use_count = in.device.input_count;
|
||||||
std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT);
|
buffers = in.device.input;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SinkContext::InUse() const {
|
bool SinkContext::InUse() const {
|
||||||
|
@ -28,4 +34,12 @@ std::vector<u8> SinkContext::OutputBuffers() const {
|
||||||
return buffer_ret;
|
return buffer_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SinkContext::HasDownMixingCoefficients() const {
|
||||||
|
return has_downmix_coefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
|
||||||
|
return downmix_coefficients;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
|
using DownmixCoefficients = std::array<float_le, 4>;
|
||||||
|
|
||||||
enum class SinkTypes : u8 {
|
enum class SinkTypes : u8 {
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
Device = 1,
|
Device = 1,
|
||||||
|
@ -40,7 +42,7 @@ public:
|
||||||
bool in_use;
|
bool in_use;
|
||||||
INSERT_UNION_PADDING_BYTES(5);
|
INSERT_UNION_PADDING_BYTES(5);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SinkInfo::CircularBufferIn) == 0x28,
|
static_assert(sizeof(CircularBufferIn) == 0x28,
|
||||||
"SinkInfo::CircularBufferIn is in invalid size");
|
"SinkInfo::CircularBufferIn is in invalid size");
|
||||||
|
|
||||||
struct DeviceIn {
|
struct DeviceIn {
|
||||||
|
@ -50,9 +52,9 @@ public:
|
||||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
|
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
|
||||||
INSERT_UNION_PADDING_BYTES(1);
|
INSERT_UNION_PADDING_BYTES(1);
|
||||||
bool down_matrix_enabled;
|
bool down_matrix_enabled;
|
||||||
std::array<float_le, 4> down_matrix_coef;
|
DownmixCoefficients down_matrix_coef;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
|
static_assert(sizeof(DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
|
||||||
|
|
||||||
struct InParams {
|
struct InParams {
|
||||||
SinkTypes type{};
|
SinkTypes type{};
|
||||||
|
@ -62,28 +64,33 @@ public:
|
||||||
INSERT_PADDING_WORDS(6);
|
INSERT_PADDING_WORDS(6);
|
||||||
union {
|
union {
|
||||||
// std::array<u8, 0x120> raw{};
|
// std::array<u8, 0x120> raw{};
|
||||||
SinkInfo::DeviceIn device;
|
DeviceIn device;
|
||||||
SinkInfo::CircularBufferIn circular_buffer;
|
CircularBufferIn circular_buffer;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SinkInfo::InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
|
static_assert(sizeof(InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
|
||||||
};
|
};
|
||||||
|
|
||||||
class SinkContext {
|
class SinkContext {
|
||||||
public:
|
public:
|
||||||
explicit SinkContext(std::size_t sink_count);
|
explicit SinkContext(std::size_t sink_count_);
|
||||||
~SinkContext();
|
~SinkContext();
|
||||||
|
|
||||||
std::size_t GetCount() const;
|
[[nodiscard]] std::size_t GetCount() const;
|
||||||
|
|
||||||
void UpdateMainSink(SinkInfo::InParams& in);
|
void UpdateMainSink(const SinkInfo::InParams& in);
|
||||||
bool InUse() const;
|
[[nodiscard]] bool InUse() const;
|
||||||
std::vector<u8> OutputBuffers() const;
|
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool HasDownMixingCoefficients() const;
|
||||||
|
[[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool in_use{false};
|
bool in_use{false};
|
||||||
s32 use_count{};
|
s32 use_count{};
|
||||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
||||||
std::size_t sink_count{};
|
std::size_t sink_count{};
|
||||||
|
bool has_downmix_coefs{false};
|
||||||
|
DownmixCoefficients downmix_coefficients{};
|
||||||
};
|
};
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id) : id(id) {}
|
ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id_) : id{id_} {}
|
||||||
ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
|
ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
|
||||||
|
|
||||||
void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
|
void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
|
||||||
|
@ -87,7 +87,7 @@ void ServerSplitterDestinationData::UpdateInternalState() {
|
||||||
needs_update = false;
|
needs_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerSplitterInfo::ServerSplitterInfo(s32 id) : id(id) {}
|
ServerSplitterInfo::ServerSplitterInfo(s32 id_) : id(id_) {}
|
||||||
ServerSplitterInfo::~ServerSplitterInfo() = default;
|
ServerSplitterInfo::~ServerSplitterInfo() = default;
|
||||||
|
|
||||||
void ServerSplitterInfo::InitializeInfos() {
|
void ServerSplitterInfo::InitializeInfos() {
|
||||||
|
@ -121,7 +121,7 @@ const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
|
ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
|
||||||
auto current_head = head;
|
auto* current_head = head;
|
||||||
for (std::size_t i = 0; i < depth; i++) {
|
for (std::size_t i = 0; i < depth; i++) {
|
||||||
if (current_head == nullptr) {
|
if (current_head == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -132,7 +132,7 @@ ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
|
const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
|
||||||
auto current_head = head;
|
auto* current_head = head;
|
||||||
for (std::size_t i = 0; i < depth; i++) {
|
for (std::size_t i = 0; i < depth; i++) {
|
||||||
if (current_head == nullptr) {
|
if (current_head == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -245,7 +245,7 @@ ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t i
|
||||||
const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
|
const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
|
||||||
std::size_t data) const {
|
std::size_t data) const {
|
||||||
ASSERT(info < info_count);
|
ASSERT(info < info_count);
|
||||||
auto& cur_info = GetInfo(info);
|
const auto& cur_info = GetInfo(info);
|
||||||
return cur_info.GetData(data);
|
return cur_info.GetData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,11 +267,11 @@ std::size_t SplitterContext::GetDataCount() const {
|
||||||
return data_count;
|
return data_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitterContext::Setup(std::size_t _info_count, std::size_t _data_count,
|
void SplitterContext::Setup(std::size_t info_count_, std::size_t data_count_,
|
||||||
bool is_splitter_bug_fixed) {
|
bool is_splitter_bug_fixed) {
|
||||||
|
|
||||||
info_count = _info_count;
|
info_count = info_count_;
|
||||||
data_count = _data_count;
|
data_count = data_count_;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < info_count; i++) {
|
for (std::size_t i = 0; i < info_count; i++) {
|
||||||
auto& splitter = infos.emplace_back(static_cast<s32>(i));
|
auto& splitter = infos.emplace_back(static_cast<s32>(i));
|
||||||
|
@ -306,7 +306,7 @@ bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& inpu
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.send_id < 0 || header.send_id > info_count) {
|
if (header.send_id < 0 || static_cast<std::size_t>(header.send_id) > info_count) {
|
||||||
LOG_ERROR(Audio, "Bad splitter data id");
|
LOG_ERROR(Audio, "Bad splitter data id");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -348,7 +348,7 @@ bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& inpu
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header.splitter_id < 0 || header.splitter_id > data_count) {
|
if (header.splitter_id < 0 || static_cast<std::size_t>(header.splitter_id) > data_count) {
|
||||||
LOG_ERROR(Audio, "Bad splitter data id");
|
LOG_ERROR(Audio, "Bad splitter data id");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -364,7 +364,7 @@ bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
|
||||||
// Clear our current destinations
|
// Clear our current destinations
|
||||||
auto* current_head = info.GetHead();
|
auto* current_head = info.GetHead();
|
||||||
while (current_head != nullptr) {
|
while (current_head != nullptr) {
|
||||||
auto next_head = current_head->GetNextDestination();
|
auto* next_head = current_head->GetNextDestination();
|
||||||
current_head->SetNextDestination(nullptr);
|
current_head->SetNextDestination(nullptr);
|
||||||
current_head = next_head;
|
current_head = next_head;
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,7 @@ const std::vector<s32>& NodeStates::GetIndexList() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeStates::PushTsortResult(s32 index) {
|
void NodeStates::PushTsortResult(s32 index) {
|
||||||
ASSERT(index < node_count);
|
ASSERT(index < static_cast<s32>(node_count));
|
||||||
index_list[index_pos++] = index;
|
index_list[index_pos++] = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,8 +471,8 @@ bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto node_count = edge_matrix.GetNodeCount();
|
const auto edge_node_count = edge_matrix.GetNodeCount();
|
||||||
for (s32 j = 0; j < static_cast<s32>(node_count); j++) {
|
for (s32 j = 0; j < static_cast<s32>(edge_node_count); j++) {
|
||||||
// Check if our node is connected to our edge matrix
|
// Check if our node is connected to our edge matrix
|
||||||
if (!edge_matrix.Connected(current_stack_index, j)) {
|
if (!edge_matrix.Connected(current_stack_index, j)) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -63,7 +63,7 @@ public:
|
||||||
NodeStates();
|
NodeStates();
|
||||||
~NodeStates();
|
~NodeStates();
|
||||||
|
|
||||||
void Initialize(std::size_t _node_count);
|
void Initialize(std::size_t node_count_);
|
||||||
bool Tsort(EdgeMatrix& edge_matrix);
|
bool Tsort(EdgeMatrix& edge_matrix);
|
||||||
std::size_t GetIndexPos() const;
|
std::size_t GetIndexPos() const;
|
||||||
const std::vector<s32>& GetIndexList() const;
|
const std::vector<s32>& GetIndexList() const;
|
||||||
|
@ -72,15 +72,15 @@ private:
|
||||||
void PushTsortResult(s32 index);
|
void PushTsortResult(s32 index);
|
||||||
bool DepthFirstSearch(EdgeMatrix& edge_matrix);
|
bool DepthFirstSearch(EdgeMatrix& edge_matrix);
|
||||||
void ResetState();
|
void ResetState();
|
||||||
void UpdateState(NodeStates::State state, std::size_t i);
|
void UpdateState(State state, std::size_t i);
|
||||||
NodeStates::State GetState(std::size_t i);
|
State GetState(std::size_t i);
|
||||||
|
|
||||||
std::size_t node_count{};
|
std::size_t node_count{};
|
||||||
std::vector<bool> was_node_found{};
|
std::vector<bool> was_node_found{};
|
||||||
std::vector<bool> was_node_completed{};
|
std::vector<bool> was_node_completed{};
|
||||||
std::size_t index_pos{};
|
std::size_t index_pos{};
|
||||||
std::vector<s32> index_list{};
|
std::vector<s32> index_list{};
|
||||||
NodeStates::Stack index_stack{};
|
Stack index_stack{};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SplitterMagic : u32_le {
|
enum class SplitterMagic : u32_le {
|
||||||
|
@ -97,8 +97,7 @@ public:
|
||||||
s32_le data_count{};
|
s32_le data_count{};
|
||||||
INSERT_PADDING_WORDS(5);
|
INSERT_PADDING_WORDS(5);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SplitterInfo::InHeader) == 0x20,
|
static_assert(sizeof(InHeader) == 0x20, "SplitterInfo::InHeader is an invalid size");
|
||||||
"SplitterInfo::InHeader is an invalid size");
|
|
||||||
|
|
||||||
struct InInfoPrams {
|
struct InInfoPrams {
|
||||||
SplitterMagic magic{};
|
SplitterMagic magic{};
|
||||||
|
@ -107,8 +106,7 @@ public:
|
||||||
s32_le length{};
|
s32_le length{};
|
||||||
s32_le resource_id_base{};
|
s32_le resource_id_base{};
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SplitterInfo::InInfoPrams) == 0x14,
|
static_assert(sizeof(InInfoPrams) == 0x14, "SplitterInfo::InInfoPrams is an invalid size");
|
||||||
"SplitterInfo::InInfoPrams is an invalid size");
|
|
||||||
|
|
||||||
struct InDestinationParams {
|
struct InDestinationParams {
|
||||||
SplitterMagic magic{};
|
SplitterMagic magic{};
|
||||||
|
@ -118,13 +116,13 @@ public:
|
||||||
bool in_use{};
|
bool in_use{};
|
||||||
INSERT_PADDING_BYTES(3);
|
INSERT_PADDING_BYTES(3);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(SplitterInfo::InDestinationParams) == 0x70,
|
static_assert(sizeof(InDestinationParams) == 0x70,
|
||||||
"SplitterInfo::InDestinationParams is an invalid size");
|
"SplitterInfo::InDestinationParams is an invalid size");
|
||||||
};
|
};
|
||||||
|
|
||||||
class ServerSplitterDestinationData {
|
class ServerSplitterDestinationData {
|
||||||
public:
|
public:
|
||||||
explicit ServerSplitterDestinationData(s32 id);
|
explicit ServerSplitterDestinationData(s32 id_);
|
||||||
~ServerSplitterDestinationData();
|
~ServerSplitterDestinationData();
|
||||||
|
|
||||||
void Update(SplitterInfo::InDestinationParams& header);
|
void Update(SplitterInfo::InDestinationParams& header);
|
||||||
|
@ -153,7 +151,7 @@ private:
|
||||||
|
|
||||||
class ServerSplitterInfo {
|
class ServerSplitterInfo {
|
||||||
public:
|
public:
|
||||||
explicit ServerSplitterInfo(s32 id);
|
explicit ServerSplitterInfo(s32 id_);
|
||||||
~ServerSplitterInfo();
|
~ServerSplitterInfo();
|
||||||
|
|
||||||
void InitializeInfos();
|
void InitializeInfos();
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/core_timing.h"
|
#include "core/core_timing.h"
|
||||||
#include "core/core_timing_util.h"
|
|
||||||
#include "core/settings.h"
|
#include "core/settings.h"
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
@ -32,10 +31,10 @@ u32 Stream::GetNumChannels() const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream::Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format format_,
|
||||||
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_)
|
ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)
|
||||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
: sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},
|
||||||
sink_stream{sink_stream}, core_timing{core_timing}, name{std::move(name_)} {
|
sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} {
|
||||||
release_event =
|
release_event =
|
||||||
Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||||
ReleaseActiveBuffer(ns_late);
|
ReleaseActiveBuffer(ns_late);
|
||||||
|
@ -123,7 +122,7 @@ bool Stream::QueueBuffer(BufferPtr&& buffer) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
bool Stream::ContainsBuffer([[maybe_unused]] Buffer::Tag tag) const {
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -131,7 +130,25 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
||||||
std::vector<Buffer::Tag> tags;
|
std::vector<Buffer::Tag> tags;
|
||||||
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||||
tags.push_back(released_buffers.front()->GetTag());
|
if (released_buffers.front()) {
|
||||||
|
tags.push_back(released_buffers.front()->GetTag());
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "Invalid tag in released_buffers!");
|
||||||
|
}
|
||||||
|
released_buffers.pop();
|
||||||
|
}
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers() {
|
||||||
|
std::vector<Buffer::Tag> tags;
|
||||||
|
tags.reserve(released_buffers.size());
|
||||||
|
while (!released_buffers.empty()) {
|
||||||
|
if (released_buffers.front()) {
|
||||||
|
tags.push_back(released_buffers.front()->GetTag());
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(false, "Invalid tag in released_buffers!");
|
||||||
|
}
|
||||||
released_buffers.pop();
|
released_buffers.pop();
|
||||||
}
|
}
|
||||||
return tags;
|
return tags;
|
||||||
|
|
|
@ -44,8 +44,8 @@ public:
|
||||||
/// Callback function type, used to change guest state on a buffer being released
|
/// Callback function type, used to change guest state on a buffer being released
|
||||||
using ReleaseCallback = std::function<void()>;
|
using ReleaseCallback = std::function<void()>;
|
||||||
|
|
||||||
Stream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, Format format,
|
Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format format_,
|
||||||
ReleaseCallback&& release_callback, SinkStream& sink_stream, std::string&& name_);
|
ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_);
|
||||||
|
|
||||||
/// Plays the audio stream
|
/// Plays the audio stream
|
||||||
void Play();
|
void Play();
|
||||||
|
@ -57,37 +57,40 @@ public:
|
||||||
bool QueueBuffer(BufferPtr&& buffer);
|
bool QueueBuffer(BufferPtr&& buffer);
|
||||||
|
|
||||||
/// Returns true if the audio stream contains a buffer with the specified tag
|
/// Returns true if the audio stream contains a buffer with the specified tag
|
||||||
bool ContainsBuffer(Buffer::Tag tag) const;
|
[[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const;
|
||||||
|
|
||||||
/// Returns a vector of recently released buffers specified by tag
|
/// Returns a vector of recently released buffers specified by tag
|
||||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
[[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||||
|
|
||||||
|
/// Returns a vector of all recently released buffers specified by tag
|
||||||
|
[[nodiscard]] std::vector<Buffer::Tag> GetTagsAndReleaseBuffers();
|
||||||
|
|
||||||
void SetVolume(float volume);
|
void SetVolume(float volume);
|
||||||
|
|
||||||
float GetVolume() const {
|
[[nodiscard]] float GetVolume() const {
|
||||||
return game_volume;
|
return game_volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the stream is currently playing
|
/// Returns true if the stream is currently playing
|
||||||
bool IsPlaying() const {
|
[[nodiscard]] bool IsPlaying() const {
|
||||||
return state == State::Playing;
|
return state == State::Playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of queued buffers
|
/// Returns the number of queued buffers
|
||||||
std::size_t GetQueueSize() const {
|
[[nodiscard]] std::size_t GetQueueSize() const {
|
||||||
return queued_buffers.size();
|
return queued_buffers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the sample rate
|
/// Gets the sample rate
|
||||||
u32 GetSampleRate() const {
|
[[nodiscard]] u32 GetSampleRate() const {
|
||||||
return sample_rate;
|
return sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the number of channels
|
/// Gets the number of channels
|
||||||
u32 GetNumChannels() const;
|
[[nodiscard]] u32 GetNumChannels() const;
|
||||||
|
|
||||||
/// Get the state
|
/// Get the state
|
||||||
State GetState() const;
|
[[nodiscard]] State GetState() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||||
|
@ -97,7 +100,7 @@ private:
|
||||||
void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
|
void ReleaseActiveBuffer(std::chrono::nanoseconds ns_late = {});
|
||||||
|
|
||||||
/// Gets the number of core cycles when the specified buffer will be released
|
/// Gets the number of core cycles when the specified buffer will be released
|
||||||
std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
[[nodiscard]] std::chrono::nanoseconds GetBufferReleaseNS(const Buffer& buffer) const;
|
||||||
|
|
||||||
u32 sample_rate; ///< Sample rate of the stream
|
u32 sample_rate; ///< Sample rate of the stream
|
||||||
Format format; ///< Format of the stream
|
Format format; ///< Format of the stream
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id) : id(id) {}
|
ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id_) : id(id_) {}
|
||||||
ServerVoiceChannelResource::~ServerVoiceChannelResource() = default;
|
ServerVoiceChannelResource::~ServerVoiceChannelResource() = default;
|
||||||
|
|
||||||
bool ServerVoiceChannelResource::InUse() const {
|
bool ServerVoiceChannelResource::InUse() const {
|
||||||
|
@ -128,7 +128,10 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
|
||||||
in_params.wave_buffer_count = voice_in.wave_buffer_count;
|
in_params.wave_buffer_count = voice_in.wave_buffer_count;
|
||||||
in_params.wave_bufffer_head = voice_in.wave_buffer_head;
|
in_params.wave_bufffer_head = voice_in.wave_buffer_head;
|
||||||
if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
|
if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
|
||||||
in_params.wave_buffer_flush_request_count += voice_in.wave_buffer_flush_request_count;
|
const auto in_request_count = in_params.wave_buffer_flush_request_count;
|
||||||
|
const auto voice_request_count = voice_in.wave_buffer_flush_request_count;
|
||||||
|
in_params.wave_buffer_flush_request_count =
|
||||||
|
static_cast<u8>(in_request_count + voice_request_count);
|
||||||
}
|
}
|
||||||
in_params.mix_id = voice_in.mix_id;
|
in_params.mix_id = voice_in.mix_id;
|
||||||
if (behavior_info.IsSplitterSupported()) {
|
if (behavior_info.IsSplitterSupported()) {
|
||||||
|
@ -206,7 +209,8 @@ void ServerVoiceInfo::UpdateWaveBuffers(
|
||||||
|
|
||||||
void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
|
void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
|
||||||
const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
|
const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
|
||||||
bool is_buffer_valid, BehaviorInfo& behavior_info) {
|
bool is_buffer_valid,
|
||||||
|
[[maybe_unused]] BehaviorInfo& behavior_info) {
|
||||||
if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
|
if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
|
||||||
out_wavebuffer.buffer_address = 0;
|
out_wavebuffer.buffer_address = 0;
|
||||||
out_wavebuffer.buffer_size = 0;
|
out_wavebuffer.buffer_size = 0;
|
||||||
|
@ -397,7 +401,7 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
|
||||||
return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
|
return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
VoiceContext::VoiceContext(std::size_t voice_count) : voice_count(voice_count) {
|
VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} {
|
||||||
for (std::size_t i = 0; i < voice_count; i++) {
|
for (std::size_t i = 0; i < voice_count; i++) {
|
||||||
voice_channel_resources.emplace_back(static_cast<s32>(i));
|
voice_channel_resources.emplace_back(static_cast<s32>(i));
|
||||||
sorted_voice_info.push_back(&voice_info.emplace_back());
|
sorted_voice_info.push_back(&voice_info.emplace_back());
|
||||||
|
@ -488,11 +492,11 @@ s32 VoiceContext::DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer,
|
||||||
|
|
||||||
// Fast path
|
// Fast path
|
||||||
if (channel_count == 1) {
|
if (channel_count == 1) {
|
||||||
for (std::size_t i = 0; i < samples_processed; i++) {
|
for (std::ptrdiff_t i = 0; i < samples_processed; i++) {
|
||||||
output_buffer[i] = buffer_data[i];
|
output_buffer[i] = buffer_data[i];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (std::size_t i = 0; i < samples_processed; i++) {
|
for (std::ptrdiff_t i = 0; i < samples_processed; i++) {
|
||||||
output_buffer[i] = buffer_data[i * channel_count + channel];
|
output_buffer[i] = buffer_data[i * channel_count + channel];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,12 +118,12 @@ public:
|
||||||
bool in_use{};
|
bool in_use{};
|
||||||
INSERT_PADDING_BYTES(11);
|
INSERT_PADDING_BYTES(11);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VoiceChannelResource::InParams) == 0x70, "InParams is an invalid size");
|
static_assert(sizeof(InParams) == 0x70, "InParams is an invalid size");
|
||||||
};
|
};
|
||||||
|
|
||||||
class ServerVoiceChannelResource {
|
class ServerVoiceChannelResource {
|
||||||
public:
|
public:
|
||||||
explicit ServerVoiceChannelResource(s32 id);
|
explicit ServerVoiceChannelResource(s32 id_);
|
||||||
~ServerVoiceChannelResource();
|
~ServerVoiceChannelResource();
|
||||||
|
|
||||||
bool InUse() const;
|
bool InUse() const;
|
||||||
|
@ -174,7 +174,7 @@ public:
|
||||||
BehaviorFlags behavior_flags{};
|
BehaviorFlags behavior_flags{};
|
||||||
INSERT_PADDING_BYTES(16);
|
INSERT_PADDING_BYTES(16);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VoiceInfo::InParams) == 0x170, "InParams is an invalid size");
|
static_assert(sizeof(InParams) == 0x170, "InParams is an invalid size");
|
||||||
|
|
||||||
struct OutParams {
|
struct OutParams {
|
||||||
u64_le played_sample_count{};
|
u64_le played_sample_count{};
|
||||||
|
@ -182,7 +182,7 @@ public:
|
||||||
u8 voice_dropped{};
|
u8 voice_dropped{};
|
||||||
INSERT_PADDING_BYTES(3);
|
INSERT_PADDING_BYTES(3);
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VoiceInfo::OutParams) == 0x10, "OutParams is an invalid size");
|
static_assert(sizeof(OutParams) == 0x10, "OutParams is an invalid size");
|
||||||
};
|
};
|
||||||
|
|
||||||
class ServerVoiceInfo {
|
class ServerVoiceInfo {
|
||||||
|
@ -263,7 +263,7 @@ private:
|
||||||
|
|
||||||
class VoiceContext {
|
class VoiceContext {
|
||||||
public:
|
public:
|
||||||
VoiceContext(std::size_t voice_count);
|
explicit VoiceContext(std::size_t voice_count_);
|
||||||
~VoiceContext();
|
~VoiceContext();
|
||||||
|
|
||||||
std::size_t GetVoiceCount() const;
|
std::size_t GetVoiceCount() const;
|
||||||
|
|
|
@ -102,7 +102,9 @@ add_library(common STATIC
|
||||||
atomic_ops.h
|
atomic_ops.h
|
||||||
detached_tasks.cpp
|
detached_tasks.cpp
|
||||||
detached_tasks.h
|
detached_tasks.h
|
||||||
|
bit_cast.h
|
||||||
bit_field.h
|
bit_field.h
|
||||||
|
bit_set.h
|
||||||
bit_util.h
|
bit_util.h
|
||||||
cityhash.cpp
|
cityhash.cpp
|
||||||
cityhash.h
|
cityhash.h
|
||||||
|
@ -111,6 +113,7 @@ add_library(common STATIC
|
||||||
common_paths.h
|
common_paths.h
|
||||||
common_types.h
|
common_types.h
|
||||||
concepts.h
|
concepts.h
|
||||||
|
div_ceil.h
|
||||||
dynamic_library.cpp
|
dynamic_library.cpp
|
||||||
dynamic_library.h
|
dynamic_library.h
|
||||||
fiber.cpp
|
fiber.cpp
|
||||||
|
@ -132,13 +135,10 @@ add_library(common STATIC
|
||||||
math_util.h
|
math_util.h
|
||||||
memory_detect.cpp
|
memory_detect.cpp
|
||||||
memory_detect.h
|
memory_detect.h
|
||||||
memory_hook.cpp
|
|
||||||
memory_hook.h
|
|
||||||
microprofile.cpp
|
microprofile.cpp
|
||||||
microprofile.h
|
microprofile.h
|
||||||
microprofileui.h
|
microprofileui.h
|
||||||
misc.cpp
|
misc.cpp
|
||||||
multi_level_queue.h
|
|
||||||
page_table.cpp
|
page_table.cpp
|
||||||
page_table.h
|
page_table.h
|
||||||
param_package.cpp
|
param_package.cpp
|
||||||
|
@ -150,6 +150,8 @@ add_library(common STATIC
|
||||||
scope_exit.h
|
scope_exit.h
|
||||||
spin_lock.cpp
|
spin_lock.cpp
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
|
stream.cpp
|
||||||
|
stream.h
|
||||||
string_util.cpp
|
string_util.cpp
|
||||||
string_util.h
|
string_util.h
|
||||||
swap.h
|
swap.h
|
||||||
|
@ -158,6 +160,8 @@ add_library(common STATIC
|
||||||
thread.cpp
|
thread.cpp
|
||||||
thread.h
|
thread.h
|
||||||
thread_queue_list.h
|
thread_queue_list.h
|
||||||
|
thread_worker.cpp
|
||||||
|
thread_worker.h
|
||||||
threadsafe_queue.h
|
threadsafe_queue.h
|
||||||
time_zone.cpp
|
time_zone.cpp
|
||||||
time_zone.h
|
time_zone.h
|
||||||
|
@ -188,8 +192,28 @@ if(ARCHITECTURE_x86_64)
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_definitions(common PRIVATE
|
||||||
|
# The standard library doesn't provide any replacement for codecvt yet
|
||||||
|
# so we can disable this deprecation warning for the time being.
|
||||||
|
_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
|
||||||
|
)
|
||||||
|
target_compile_options(common PRIVATE
|
||||||
|
/W4
|
||||||
|
/WX
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
target_compile_options(common PRIVATE
|
||||||
|
-Werror
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
create_target_directory_groups(common)
|
create_target_directory_groups(common)
|
||||||
find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
|
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
|
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
|
||||||
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak)
|
target_link_libraries(common PRIVATE lz4::lz4 xbyak)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(common PRIVATE zstd::zstd)
|
||||||
|
else()
|
||||||
|
target_link_libraries(common PRIVATE zstd)
|
||||||
|
endif()
|
||||||
|
|
22
src/common/bit_cast.h
Normal file
22
src/common/bit_cast.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
template <typename To, typename From>
|
||||||
|
[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> &&
|
||||||
|
std::is_trivially_copyable_v<To>,
|
||||||
|
To>
|
||||||
|
BitCast(const From& src) noexcept {
|
||||||
|
To dst;
|
||||||
|
std::memcpy(&dst, &src, sizeof(To));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
99
src/common/bit_set.h
Normal file
99
src/common/bit_set.h
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU General Public License,
|
||||||
|
* version 2, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <bit>
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/bit_util.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
template <typename Storage, size_t N>
|
||||||
|
class BitSet {
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr BitSet() = default;
|
||||||
|
|
||||||
|
constexpr void SetBit(size_t i) {
|
||||||
|
this->words[i / FlagsPerWord] |= GetBitMask(i % FlagsPerWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void ClearBit(size_t i) {
|
||||||
|
this->words[i / FlagsPerWord] &= ~GetBitMask(i % FlagsPerWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t CountLeadingZero() const {
|
||||||
|
for (size_t i = 0; i < NumWords; i++) {
|
||||||
|
if (this->words[i]) {
|
||||||
|
return FlagsPerWord * i + CountLeadingZeroImpl(this->words[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FlagsPerWord * NumWords;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetNextSet(size_t n) const {
|
||||||
|
for (size_t i = (n + 1) / FlagsPerWord; i < NumWords; i++) {
|
||||||
|
Storage word = this->words[i];
|
||||||
|
if (!IsAligned(n + 1, FlagsPerWord)) {
|
||||||
|
word &= GetBitMask(n % FlagsPerWord) - 1;
|
||||||
|
}
|
||||||
|
if (word) {
|
||||||
|
return FlagsPerWord * i + CountLeadingZeroImpl(word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FlagsPerWord * NumWords;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static_assert(std::is_unsigned_v<Storage>);
|
||||||
|
static_assert(sizeof(Storage) <= sizeof(u64));
|
||||||
|
|
||||||
|
static constexpr size_t FlagsPerWord = BitSize<Storage>();
|
||||||
|
static constexpr size_t NumWords = AlignUp(N, FlagsPerWord) / FlagsPerWord;
|
||||||
|
|
||||||
|
static constexpr auto CountLeadingZeroImpl(Storage word) {
|
||||||
|
return std::countl_zero(static_cast<unsigned long long>(word)) -
|
||||||
|
(BitSize<unsigned long long>() - FlagsPerWord);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr Storage GetBitMask(size_t bit) {
|
||||||
|
return Storage(1) << (FlagsPerWord - 1 - bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<Storage, NumWords> words{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using BitSet8 = impl::BitSet<u8, N>;
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using BitSet16 = impl::BitSet<u16, N>;
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using BitSet32 = impl::BitSet<u32, N>;
|
||||||
|
|
||||||
|
template <size_t N>
|
||||||
|
using BitSet64 = impl::BitSet<u64, N>;
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -31,4 +31,8 @@ concept DerivedFrom = requires {
|
||||||
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Replace with std::convertible_to when libc++ implements it.
|
||||||
|
template <typename From, typename To>
|
||||||
|
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
26
src/common/div_ceil.h
Normal file
26
src/common/div_ceil.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
/// Ceiled integer division.
|
||||||
|
template <typename N, typename D>
|
||||||
|
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeil(N number,
|
||||||
|
D divisor) {
|
||||||
|
return static_cast<N>((static_cast<D>(number) + divisor - 1) / divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ceiled integer division with logarithmic divisor in base 2
|
||||||
|
template <typename N, typename D>
|
||||||
|
requires std::is_integral_v<N>&& std::is_unsigned_v<D>[[nodiscard]] constexpr N DivCeilLog2(
|
||||||
|
N value, D alignment_log2) {
|
||||||
|
return static_cast<N>((static_cast<D>(value) + (D(1) << alignment_log2) - 1) >> alignment_log2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
|
@ -4,129 +4,51 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fiber.h"
|
#include "common/fiber.h"
|
||||||
#if defined(_WIN32) || defined(WIN32)
|
#include "common/spin_lock.h"
|
||||||
#include <windows.h>
|
#include "common/virtual_buffer.h"
|
||||||
#else
|
|
||||||
#include <boost/context/detail/fcontext.hpp>
|
#include <boost/context/detail/fcontext.hpp>
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
|
constexpr std::size_t default_stack_size = 256 * 1024;
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(WIN32)
|
|
||||||
|
|
||||||
struct Fiber::FiberImpl {
|
struct Fiber::FiberImpl {
|
||||||
LPVOID handle = nullptr;
|
FiberImpl() : stack{default_stack_size}, rewind_stack{default_stack_size} {}
|
||||||
LPVOID rewind_handle = nullptr;
|
|
||||||
|
VirtualBuffer<u8> stack;
|
||||||
|
VirtualBuffer<u8> rewind_stack;
|
||||||
|
|
||||||
|
SpinLock guard{};
|
||||||
|
std::function<void(void*)> entry_point;
|
||||||
|
std::function<void(void*)> rewind_point;
|
||||||
|
void* rewind_parameter{};
|
||||||
|
void* start_parameter{};
|
||||||
|
std::shared_ptr<Fiber> previous_fiber;
|
||||||
|
bool is_thread_fiber{};
|
||||||
|
bool released{};
|
||||||
|
|
||||||
|
u8* stack_limit{};
|
||||||
|
u8* rewind_stack_limit{};
|
||||||
|
boost::context::detail::fcontext_t context{};
|
||||||
|
boost::context::detail::fcontext_t rewind_context{};
|
||||||
};
|
};
|
||||||
|
|
||||||
void Fiber::Start() {
|
void Fiber::SetStartParameter(void* new_parameter) {
|
||||||
ASSERT(previous_fiber != nullptr);
|
impl->start_parameter = new_parameter;
|
||||||
previous_fiber->guard.unlock();
|
|
||||||
previous_fiber.reset();
|
|
||||||
entry_point(start_parameter);
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::OnRewind() {
|
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
|
||||||
ASSERT(impl->handle != nullptr);
|
impl->rewind_point = std::move(rewind_func);
|
||||||
DeleteFiber(impl->handle);
|
impl->rewind_parameter = rewind_param;
|
||||||
impl->handle = impl->rewind_handle;
|
|
||||||
impl->rewind_handle = nullptr;
|
|
||||||
rewind_point(rewind_parameter);
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::FiberStartFunc(void* fiber_parameter) {
|
|
||||||
auto fiber = static_cast<Fiber*>(fiber_parameter);
|
|
||||||
fiber->Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fiber::RewindStartFunc(void* fiber_parameter) {
|
|
||||||
auto fiber = static_cast<Fiber*>(fiber_parameter);
|
|
||||||
fiber->OnRewind();
|
|
||||||
}
|
|
||||||
|
|
||||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
|
||||||
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
|
|
||||||
impl = std::make_unique<FiberImpl>();
|
|
||||||
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
|
||||||
|
|
||||||
Fiber::~Fiber() {
|
|
||||||
if (released) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Make sure the Fiber is not being used
|
|
||||||
const bool locked = guard.try_lock();
|
|
||||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
|
||||||
if (locked) {
|
|
||||||
guard.unlock();
|
|
||||||
}
|
|
||||||
DeleteFiber(impl->handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fiber::Exit() {
|
|
||||||
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
|
|
||||||
if (!is_thread_fiber) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ConvertFiberToThread();
|
|
||||||
guard.unlock();
|
|
||||||
released = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
|
|
||||||
rewind_point = std::move(rewind_func);
|
|
||||||
rewind_parameter = start_parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fiber::Rewind() {
|
|
||||||
ASSERT(rewind_point);
|
|
||||||
ASSERT(impl->rewind_handle == nullptr);
|
|
||||||
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
|
|
||||||
SwitchToFiber(impl->rewind_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
|
|
||||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
|
||||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
|
||||||
to->guard.lock();
|
|
||||||
to->previous_fiber = from;
|
|
||||||
SwitchToFiber(to->impl->handle);
|
|
||||||
ASSERT(from->previous_fiber != nullptr);
|
|
||||||
from->previous_fiber->guard.unlock();
|
|
||||||
from->previous_fiber.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
|
||||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
|
||||||
fiber->guard.lock();
|
|
||||||
fiber->impl->handle = ConvertThreadToFiber(nullptr);
|
|
||||||
fiber->is_thread_fiber = true;
|
|
||||||
return fiber;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
struct Fiber::FiberImpl {
|
|
||||||
alignas(64) std::array<u8, default_stack_size> stack;
|
|
||||||
alignas(64) std::array<u8, default_stack_size> rewind_stack;
|
|
||||||
u8* stack_limit;
|
|
||||||
u8* rewind_stack_limit;
|
|
||||||
boost::context::detail::fcontext_t context;
|
|
||||||
boost::context::detail::fcontext_t rewind_context;
|
|
||||||
};
|
|
||||||
|
|
||||||
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
|
||||||
ASSERT(previous_fiber != nullptr);
|
ASSERT(impl->previous_fiber != nullptr);
|
||||||
previous_fiber->impl->context = transfer.fctx;
|
impl->previous_fiber->impl->context = transfer.fctx;
|
||||||
previous_fiber->guard.unlock();
|
impl->previous_fiber->impl->guard.unlock();
|
||||||
previous_fiber.reset();
|
impl->previous_fiber.reset();
|
||||||
entry_point(start_parameter);
|
impl->entry_point(impl->start_parameter);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,23 +59,24 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
|
||||||
u8* tmp = impl->stack_limit;
|
u8* tmp = impl->stack_limit;
|
||||||
impl->stack_limit = impl->rewind_stack_limit;
|
impl->stack_limit = impl->rewind_stack_limit;
|
||||||
impl->rewind_stack_limit = tmp;
|
impl->rewind_stack_limit = tmp;
|
||||||
rewind_point(rewind_parameter);
|
impl->rewind_point(impl->rewind_parameter);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
|
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
|
||||||
auto fiber = static_cast<Fiber*>(transfer.data);
|
auto* fiber = static_cast<Fiber*>(transfer.data);
|
||||||
fiber->Start(transfer);
|
fiber->Start(transfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
|
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
|
||||||
auto fiber = static_cast<Fiber*>(transfer.data);
|
auto* fiber = static_cast<Fiber*>(transfer.data);
|
||||||
fiber->OnRewind(transfer);
|
fiber->OnRewind(transfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
|
||||||
: entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
|
: impl{std::make_unique<FiberImpl>()} {
|
||||||
impl = std::make_unique<FiberImpl>();
|
impl->entry_point = std::move(entry_point_func);
|
||||||
|
impl->start_parameter = start_parameter;
|
||||||
impl->stack_limit = impl->stack.data();
|
impl->stack_limit = impl->stack.data();
|
||||||
impl->rewind_stack_limit = impl->rewind_stack.data();
|
impl->rewind_stack_limit = impl->rewind_stack.data();
|
||||||
u8* stack_base = impl->stack_limit + default_stack_size;
|
u8* stack_base = impl->stack_limit + default_stack_size;
|
||||||
|
@ -161,37 +84,31 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
|
||||||
boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
|
boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
|
|
||||||
rewind_point = std::move(rewind_func);
|
|
||||||
rewind_parameter = start_parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
|
||||||
|
|
||||||
Fiber::~Fiber() {
|
Fiber::~Fiber() {
|
||||||
if (released) {
|
if (impl->released) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Make sure the Fiber is not being used
|
// Make sure the Fiber is not being used
|
||||||
const bool locked = guard.try_lock();
|
const bool locked = impl->guard.try_lock();
|
||||||
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
ASSERT_MSG(locked, "Destroying a fiber that's still running");
|
||||||
if (locked) {
|
if (locked) {
|
||||||
guard.unlock();
|
impl->guard.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::Exit() {
|
void Fiber::Exit() {
|
||||||
|
ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
|
||||||
ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
|
if (!impl->is_thread_fiber) {
|
||||||
if (!is_thread_fiber) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
guard.unlock();
|
impl->guard.unlock();
|
||||||
released = true;
|
impl->released = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::Rewind() {
|
void Fiber::Rewind() {
|
||||||
ASSERT(rewind_point);
|
ASSERT(impl->rewind_point);
|
||||||
ASSERT(impl->rewind_context == nullptr);
|
ASSERT(impl->rewind_context == nullptr);
|
||||||
u8* stack_base = impl->rewind_stack_limit + default_stack_size;
|
u8* stack_base = impl->rewind_stack_limit + default_stack_size;
|
||||||
impl->rewind_context =
|
impl->rewind_context =
|
||||||
|
@ -199,24 +116,23 @@ void Fiber::Rewind() {
|
||||||
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
boost::context::detail::jump_fcontext(impl->rewind_context, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
|
void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
|
||||||
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
|
||||||
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
ASSERT_MSG(to != nullptr, "Next fiber is null!");
|
||||||
to->guard.lock();
|
to->impl->guard.lock();
|
||||||
to->previous_fiber = from;
|
to->impl->previous_fiber = from;
|
||||||
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
|
||||||
ASSERT(from->previous_fiber != nullptr);
|
ASSERT(from->impl->previous_fiber != nullptr);
|
||||||
from->previous_fiber->impl->context = transfer.fctx;
|
from->impl->previous_fiber->impl->context = transfer.fctx;
|
||||||
from->previous_fiber->guard.unlock();
|
from->impl->previous_fiber->impl->guard.unlock();
|
||||||
from->previous_fiber.reset();
|
from->impl->previous_fiber.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
|
||||||
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
|
||||||
fiber->guard.lock();
|
fiber->impl->guard.lock();
|
||||||
fiber->is_thread_fiber = true;
|
fiber->impl->is_thread_fiber = true;
|
||||||
return fiber;
|
return fiber;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -7,14 +7,9 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "common/spin_lock.h"
|
|
||||||
|
|
||||||
#if !defined(_WIN32) && !defined(WIN32)
|
|
||||||
namespace boost::context::detail {
|
namespace boost::context::detail {
|
||||||
struct transfer_t;
|
struct transfer_t;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
|
@ -46,10 +41,10 @@ public:
|
||||||
|
|
||||||
/// Yields control from Fiber 'from' to Fiber 'to'
|
/// Yields control from Fiber 'from' to Fiber 'to'
|
||||||
/// Fiber 'from' must be the currently running fiber.
|
/// Fiber 'from' must be the currently running fiber.
|
||||||
static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
|
static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
|
||||||
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
|
||||||
|
|
||||||
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
|
void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
|
||||||
|
|
||||||
void Rewind();
|
void Rewind();
|
||||||
|
|
||||||
|
@ -57,36 +52,18 @@ public:
|
||||||
void Exit();
|
void Exit();
|
||||||
|
|
||||||
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
|
||||||
void SetStartParameter(void* new_parameter) {
|
void SetStartParameter(void* new_parameter);
|
||||||
start_parameter = new_parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Fiber();
|
Fiber();
|
||||||
|
|
||||||
#if defined(_WIN32) || defined(WIN32)
|
|
||||||
void OnRewind();
|
|
||||||
void Start();
|
|
||||||
static void FiberStartFunc(void* fiber_parameter);
|
|
||||||
static void RewindStartFunc(void* fiber_parameter);
|
|
||||||
#else
|
|
||||||
void OnRewind(boost::context::detail::transfer_t& transfer);
|
void OnRewind(boost::context::detail::transfer_t& transfer);
|
||||||
void Start(boost::context::detail::transfer_t& transfer);
|
void Start(boost::context::detail::transfer_t& transfer);
|
||||||
static void FiberStartFunc(boost::context::detail::transfer_t transfer);
|
static void FiberStartFunc(boost::context::detail::transfer_t transfer);
|
||||||
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
|
static void RewindStartFunc(boost::context::detail::transfer_t transfer);
|
||||||
#endif
|
|
||||||
|
|
||||||
struct FiberImpl;
|
struct FiberImpl;
|
||||||
|
|
||||||
SpinLock guard{};
|
|
||||||
std::function<void(void*)> entry_point;
|
|
||||||
std::function<void(void*)> rewind_point;
|
|
||||||
void* rewind_parameter{};
|
|
||||||
void* start_parameter{};
|
|
||||||
std::shared_ptr<Fiber> previous_fiber;
|
|
||||||
std::unique_ptr<FiberImpl> impl;
|
std::unique_ptr<FiberImpl> impl;
|
||||||
bool is_thread_fiber{};
|
|
||||||
bool released{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
|
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
|
||||||
const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
|
const auto callback = [recursion](u64*, const std::string& directory,
|
||||||
const std::string& virtual_name) -> bool {
|
const std::string& virtual_name) {
|
||||||
std::string new_path = directory + DIR_SEP_CHR + virtual_name;
|
const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
|
||||||
|
|
||||||
if (IsDirectory(new_path)) {
|
if (IsDirectory(new_path)) {
|
||||||
if (recursion == 0)
|
if (recursion == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
return DeleteDirRecursively(new_path, recursion - 1);
|
return DeleteDirRecursively(new_path, recursion - 1);
|
||||||
}
|
}
|
||||||
return Delete(new_path);
|
return Delete(new_path);
|
||||||
|
@ -492,7 +493,8 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CopyDir(const std::string& source_path, const std::string& dest_path) {
|
void CopyDir([[maybe_unused]] const std::string& source_path,
|
||||||
|
[[maybe_unused]] const std::string& dest_path) {
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if (source_path == dest_path) {
|
if (source_path == dest_path) {
|
||||||
return;
|
return;
|
||||||
|
@ -553,7 +555,7 @@ std::optional<std::string> GetCurrentDir() {
|
||||||
std::string strDir = dir;
|
std::string strDir = dir;
|
||||||
#endif
|
#endif
|
||||||
free(dir);
|
free(dir);
|
||||||
return std::move(strDir);
|
return strDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SetCurrentDir(const std::string& directory) {
|
bool SetCurrentDir(const std::string& directory) {
|
||||||
|
@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
|
||||||
|
|
||||||
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
|
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
|
||||||
std::array<char, 4>& extension) {
|
std::array<char, 4>& extension) {
|
||||||
const std::string forbidden_characters = ".\"/\\[]:;=, ";
|
static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
|
||||||
|
|
||||||
// On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
|
// On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
|
||||||
short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
|
short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
|
||||||
extension = {{' ', ' ', ' ', '\0'}};
|
extension = {{' ', ' ', ' ', '\0'}};
|
||||||
|
|
||||||
std::string::size_type point = filename.rfind('.');
|
auto point = filename.rfind('.');
|
||||||
if (point == filename.size() - 1)
|
if (point == filename.size() - 1) {
|
||||||
point = filename.rfind('.', point);
|
point = filename.rfind('.', point);
|
||||||
|
}
|
||||||
|
|
||||||
// Get short name.
|
// Get short name.
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (char letter : filename.substr(0, point)) {
|
for (char letter : filename.substr(0, point)) {
|
||||||
if (forbidden_characters.find(letter, 0) != std::string::npos)
|
if (forbidden_characters.find(letter, 0) != std::string::npos) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
if (j == 8) {
|
if (j == 8) {
|
||||||
// TODO(Link Mauve): also do that for filenames containing a space.
|
// TODO(Link Mauve): also do that for filenames containing a space.
|
||||||
// TODO(Link Mauve): handle multiple files having the same short name.
|
// TODO(Link Mauve): handle multiple files having the same short name.
|
||||||
|
@ -794,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
|
||||||
short_name[7] = '1';
|
short_name[7] = '1';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
short_name[j++] = toupper(letter);
|
short_name[j++] = static_cast<char>(std::toupper(letter));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get extension.
|
// Get extension.
|
||||||
if (point != std::string::npos) {
|
if (point != std::string::npos) {
|
||||||
j = 0;
|
j = 0;
|
||||||
for (char letter : filename.substr(point + 1, 3))
|
for (char letter : filename.substr(point + 1, 3)) {
|
||||||
extension[j++] = toupper(letter);
|
extension[j++] = static_cast<char>(std::toupper(letter));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -232,7 +232,7 @@ public:
|
||||||
|
|
||||||
void Swap(IOFile& other) noexcept;
|
void Swap(IOFile& other) noexcept;
|
||||||
|
|
||||||
[[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
|
bool Open(const std::string& filename, const char openmode[], int flags = 0);
|
||||||
bool Close();
|
bool Close();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -16,14 +16,14 @@ namespace Common {
|
||||||
|
|
||||||
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
|
||||||
if (c >= 65 && c <= 70) {
|
if (c >= 65 && c <= 70) {
|
||||||
return c - 55;
|
return static_cast<u8>(c - 55);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c >= 97 && c <= 102) {
|
if (c >= 97 && c <= 102) {
|
||||||
return c - 87;
|
return static_cast<u8>(c - 87);
|
||||||
}
|
}
|
||||||
|
|
||||||
return c - 48;
|
return static_cast<u8>(c - 48);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
||||||
|
@ -33,11 +33,11 @@ template <std::size_t Size, bool le = false>
|
||||||
std::array<u8, Size> out{};
|
std::array<u8, Size> out{};
|
||||||
if constexpr (le) {
|
if constexpr (le) {
|
||||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
|
||||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
for (std::size_t i = 0; i < 2 * Size; i += 2) {
|
||||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "common/logging/text_formatter.h"
|
#include "common/logging/text_formatter.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/threadsafe_queue.h"
|
#include "common/threadsafe_queue.h"
|
||||||
|
#include "core/settings.h"
|
||||||
|
|
||||||
namespace Log {
|
namespace Log {
|
||||||
|
|
||||||
|
@ -152,10 +153,19 @@ FileBackend::FileBackend(const std::string& filename)
|
||||||
void FileBackend::Write(const Entry& entry) {
|
void FileBackend::Write(const Entry& entry) {
|
||||||
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
||||||
// know)
|
// know)
|
||||||
constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
constexpr std::size_t MAX_BYTES_WRITTEN = 100 * 1024 * 1024;
|
||||||
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
|
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
if (!file.IsOpen()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN_EXTENDED) {
|
||||||
|
return;
|
||||||
|
} else if (!Settings::values.extended_logging && bytes_written > MAX_BYTES_WRITTEN) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||||
if (entry.log_level >= Level::Error) {
|
if (entry.log_level >= Level::Error) {
|
||||||
file.Flush();
|
file.Flush();
|
||||||
|
@ -222,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||||
SUB(Service, NPNS) \
|
SUB(Service, NPNS) \
|
||||||
SUB(Service, NS) \
|
SUB(Service, NS) \
|
||||||
SUB(Service, NVDRV) \
|
SUB(Service, NVDRV) \
|
||||||
|
SUB(Service, OLSC) \
|
||||||
SUB(Service, PCIE) \
|
SUB(Service, PCIE) \
|
||||||
SUB(Service, PCTL) \
|
SUB(Service, PCTL) \
|
||||||
SUB(Service, PCV) \
|
SUB(Service, PCV) \
|
||||||
|
@ -274,7 +285,6 @@ const char* GetLogClassName(Class log_class) {
|
||||||
case Class::Count:
|
case Class::Count:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
UNREACHABLE();
|
|
||||||
return "Invalid";
|
return "Invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,7 +303,6 @@ const char* GetLevelName(Level log_level) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#undef LVL
|
#undef LVL
|
||||||
UNREACHABLE();
|
|
||||||
return "Invalid";
|
return "Invalid";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ enum class Class : ClassType {
|
||||||
Service_NPNS, ///< The NPNS service
|
Service_NPNS, ///< The NPNS service
|
||||||
Service_NS, ///< The NS services
|
Service_NS, ///< The NS services
|
||||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||||
|
Service_OLSC, ///< The OLSC service
|
||||||
Service_PCIE, ///< The PCIe service
|
Service_PCIE, ///< The PCIe service
|
||||||
Service_PCTL, ///< The PCTL (Parental control) service
|
Service_PCTL, ///< The PCTL (Parental control) service
|
||||||
Service_PCV, ///< The PCV service
|
Service_PCV, ///< The PCV service
|
||||||
|
|
|
@ -20,14 +20,14 @@ struct Rectangle {
|
||||||
|
|
||||||
constexpr Rectangle() = default;
|
constexpr Rectangle() = default;
|
||||||
|
|
||||||
constexpr Rectangle(T left, T top, T right, T bottom)
|
constexpr Rectangle(T left_, T top_, T right_, T bottom_)
|
||||||
: left(left), top(top), right(right), bottom(bottom) {}
|
: left(left_), top(top_), right(right_), bottom(bottom_) {}
|
||||||
|
|
||||||
[[nodiscard]] T GetWidth() const {
|
[[nodiscard]] T GetWidth() const {
|
||||||
if constexpr (std::is_floating_point_v<T>) {
|
if constexpr (std::is_floating_point_v<T>) {
|
||||||
return std::abs(right - left);
|
return std::abs(right - left);
|
||||||
} else {
|
} else {
|
||||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(right - left)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ struct Rectangle {
|
||||||
if constexpr (std::is_floating_point_v<T>) {
|
if constexpr (std::is_floating_point_v<T>) {
|
||||||
return std::abs(bottom - top);
|
return std::abs(bottom - top);
|
||||||
} else {
|
} else {
|
||||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(bottom - top)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "common/memory_hook.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
MemoryHook::~MemoryHook() = default;
|
|
||||||
|
|
||||||
} // namespace Common
|
|
|
@ -1,47 +0,0 @@
|
||||||
// Copyright 2016 Citra Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Memory hooks have two purposes:
|
|
||||||
* 1. To allow reads and writes to a region of memory to be intercepted. This is used to implement
|
|
||||||
* texture forwarding and memory breakpoints for debugging.
|
|
||||||
* 2. To allow for the implementation of MMIO devices.
|
|
||||||
*
|
|
||||||
* A hook may be mapped to multiple regions of memory.
|
|
||||||
*
|
|
||||||
* If a std::nullopt or false is returned from a function, the read/write request is passed through
|
|
||||||
* to the underlying memory region.
|
|
||||||
*/
|
|
||||||
class MemoryHook {
|
|
||||||
public:
|
|
||||||
virtual ~MemoryHook();
|
|
||||||
|
|
||||||
virtual std::optional<bool> IsValidAddress(VAddr addr) = 0;
|
|
||||||
|
|
||||||
virtual std::optional<u8> Read8(VAddr addr) = 0;
|
|
||||||
virtual std::optional<u16> Read16(VAddr addr) = 0;
|
|
||||||
virtual std::optional<u32> Read32(VAddr addr) = 0;
|
|
||||||
virtual std::optional<u64> Read64(VAddr addr) = 0;
|
|
||||||
|
|
||||||
virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0;
|
|
||||||
|
|
||||||
virtual bool Write8(VAddr addr, u8 data) = 0;
|
|
||||||
virtual bool Write16(VAddr addr, u16 data) = 0;
|
|
||||||
virtual bool Write32(VAddr addr, u32 data) = 0;
|
|
||||||
virtual bool Write64(VAddr addr, u64 data) = 0;
|
|
||||||
|
|
||||||
virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using MemoryHookPointer = std::shared_ptr<MemoryHook>;
|
|
||||||
} // namespace Common
|
|
|
@ -16,16 +16,23 @@
|
||||||
// Call directly after the command or use the error num.
|
// Call directly after the command or use the error num.
|
||||||
// This function might change the error code.
|
// This function might change the error code.
|
||||||
std::string GetLastErrorMsg() {
|
std::string GetLastErrorMsg() {
|
||||||
static const std::size_t buff_size = 255;
|
static constexpr std::size_t buff_size = 255;
|
||||||
char err_str[buff_size];
|
char err_str[buff_size];
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
|
||||||
|
return std::string(err_str, buff_size);
|
||||||
|
#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
|
||||||
|
// Thread safe (GNU-specific)
|
||||||
|
const char* str = strerror_r(errno, err_str, buff_size);
|
||||||
|
return std::string(str);
|
||||||
#else
|
#else
|
||||||
// Thread safe (XSI-compliant)
|
// Thread safe (XSI-compliant)
|
||||||
strerror_r(errno, err_str, buff_size);
|
const int success = strerror_r(errno, err_str, buff_size);
|
||||||
|
if (success != 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return std::string(err_str);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return std::string(err_str, buff_size);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,345 +0,0 @@
|
||||||
// Copyright 2019 TuxSH
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <iterator>
|
|
||||||
#include <list>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "common/bit_util.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
|
|
||||||
namespace Common {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A MultiLevelQueue is a type of priority queue which has the following characteristics:
|
|
||||||
* - iteratable through each of its elements.
|
|
||||||
* - back can be obtained.
|
|
||||||
* - O(1) add, lookup (both front and back)
|
|
||||||
* - discrete priorities and a max of 64 priorities (limited domain)
|
|
||||||
* This type of priority queue is normaly used for managing threads within an scheduler
|
|
||||||
*/
|
|
||||||
template <typename T, std::size_t Depth>
|
|
||||||
class MultiLevelQueue {
|
|
||||||
public:
|
|
||||||
using value_type = T;
|
|
||||||
using reference = value_type&;
|
|
||||||
using const_reference = const value_type&;
|
|
||||||
using pointer = value_type*;
|
|
||||||
using const_pointer = const value_type*;
|
|
||||||
|
|
||||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
|
||||||
using size_type = std::size_t;
|
|
||||||
|
|
||||||
template <bool is_constant>
|
|
||||||
class iterator_impl {
|
|
||||||
public:
|
|
||||||
using iterator_category = std::bidirectional_iterator_tag;
|
|
||||||
using value_type = T;
|
|
||||||
using pointer = std::conditional_t<is_constant, T*, const T*>;
|
|
||||||
using reference = std::conditional_t<is_constant, const T&, T&>;
|
|
||||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
|
||||||
|
|
||||||
friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
|
|
||||||
if (lhs.IsEnd() && rhs.IsEnd())
|
|
||||||
return true;
|
|
||||||
return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
|
|
||||||
}
|
|
||||||
|
|
||||||
friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
|
|
||||||
return !operator==(lhs, rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
reference operator*() const {
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer operator->() const {
|
|
||||||
return it.operator->();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator_impl& operator++() {
|
|
||||||
if (IsEnd()) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
++it;
|
|
||||||
|
|
||||||
if (it == GetEndItForPrio()) {
|
|
||||||
u64 prios = mlq.used_priorities;
|
|
||||||
prios &= ~((1ULL << (current_priority + 1)) - 1);
|
|
||||||
if (prios == 0) {
|
|
||||||
current_priority = static_cast<u32>(mlq.depth());
|
|
||||||
} else {
|
|
||||||
current_priority = CountTrailingZeroes64(prios);
|
|
||||||
it = GetBeginItForPrio();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator_impl& operator--() {
|
|
||||||
if (IsEnd()) {
|
|
||||||
if (mlq.used_priorities != 0) {
|
|
||||||
current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
|
|
||||||
it = GetEndItForPrio();
|
|
||||||
--it;
|
|
||||||
}
|
|
||||||
} else if (it == GetBeginItForPrio()) {
|
|
||||||
u64 prios = mlq.used_priorities;
|
|
||||||
prios &= (1ULL << current_priority) - 1;
|
|
||||||
if (prios != 0) {
|
|
||||||
current_priority = CountTrailingZeroes64(prios);
|
|
||||||
it = GetEndItForPrio();
|
|
||||||
--it;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
--it;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator_impl operator++(int) {
|
|
||||||
const iterator_impl v{*this};
|
|
||||||
++(*this);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator_impl operator--(int) {
|
|
||||||
const iterator_impl v{*this};
|
|
||||||
--(*this);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
// allow implicit const->non-const
|
|
||||||
iterator_impl(const iterator_impl<false>& other)
|
|
||||||
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
|
||||||
|
|
||||||
iterator_impl(const iterator_impl<true>& other)
|
|
||||||
: mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
|
||||||
|
|
||||||
iterator_impl& operator=(const iterator_impl<false>& other) {
|
|
||||||
mlq = other.mlq;
|
|
||||||
it = other.it;
|
|
||||||
current_priority = other.current_priority;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend class iterator_impl<true>;
|
|
||||||
iterator_impl() = default;
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class MultiLevelQueue;
|
|
||||||
using container_ref =
|
|
||||||
std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
|
|
||||||
using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
|
|
||||||
typename std::list<T>::iterator>;
|
|
||||||
|
|
||||||
explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
|
|
||||||
: mlq(mlq), it(it), current_priority(current_priority) {}
|
|
||||||
explicit iterator_impl(container_ref mlq, u32 current_priority)
|
|
||||||
: mlq(mlq), it(), current_priority(current_priority) {}
|
|
||||||
|
|
||||||
bool IsEnd() const {
|
|
||||||
return current_priority == mlq.depth();
|
|
||||||
}
|
|
||||||
|
|
||||||
list_iterator GetBeginItForPrio() const {
|
|
||||||
return mlq.levels[current_priority].begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
list_iterator GetEndItForPrio() const {
|
|
||||||
return mlq.levels[current_priority].end();
|
|
||||||
}
|
|
||||||
|
|
||||||
container_ref mlq;
|
|
||||||
list_iterator it;
|
|
||||||
u32 current_priority;
|
|
||||||
};
|
|
||||||
|
|
||||||
using iterator = iterator_impl<false>;
|
|
||||||
using const_iterator = iterator_impl<true>;
|
|
||||||
|
|
||||||
void add(const T& element, u32 priority, bool send_back = true) {
|
|
||||||
if (send_back)
|
|
||||||
levels[priority].push_back(element);
|
|
||||||
else
|
|
||||||
levels[priority].push_front(element);
|
|
||||||
used_priorities |= 1ULL << priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(const T& element, u32 priority) {
|
|
||||||
auto it = ListIterateTo(levels[priority], element);
|
|
||||||
if (it == levels[priority].end())
|
|
||||||
return;
|
|
||||||
levels[priority].erase(it);
|
|
||||||
if (levels[priority].empty()) {
|
|
||||||
used_priorities &= ~(1ULL << priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
|
||||||
remove(element, old_priority);
|
|
||||||
add(element, new_priority, !adjust_front);
|
|
||||||
}
|
|
||||||
void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
|
||||||
adjust(*it, old_priority, new_priority, adjust_front);
|
|
||||||
}
|
|
||||||
|
|
||||||
void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
|
|
||||||
ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
|
|
||||||
ListIterateTo(levels[priority], element));
|
|
||||||
|
|
||||||
other.used_priorities |= 1ULL << priority;
|
|
||||||
|
|
||||||
if (levels[priority].empty()) {
|
|
||||||
used_priorities &= ~(1ULL << priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
|
||||||
transfer_to_front(*it, priority, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
|
|
||||||
ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
|
|
||||||
ListIterateTo(levels[priority], element));
|
|
||||||
|
|
||||||
other.used_priorities |= 1ULL << priority;
|
|
||||||
|
|
||||||
if (levels[priority].empty()) {
|
|
||||||
used_priorities &= ~(1ULL << priority);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
|
||||||
transfer_to_back(*it, priority, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
void yield(u32 priority, std::size_t n = 1) {
|
|
||||||
ListShiftForward(levels[priority], n);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::size_t depth() const {
|
|
||||||
return Depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::size_t size(u32 priority) const {
|
|
||||||
return levels[priority].size();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::size_t size() const {
|
|
||||||
u64 priorities = used_priorities;
|
|
||||||
std::size_t size = 0;
|
|
||||||
while (priorities != 0) {
|
|
||||||
const u64 current_priority = CountTrailingZeroes64(priorities);
|
|
||||||
size += levels[current_priority].size();
|
|
||||||
priorities &= ~(1ULL << current_priority);
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const {
|
|
||||||
return used_priorities == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool empty(u32 priority) const {
|
|
||||||
return (used_priorities & (1ULL << priority)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
|
|
||||||
const u64 priorities =
|
|
||||||
max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
|
|
||||||
return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
|
|
||||||
const u64 priorities = min_priority >= Depth - 1
|
|
||||||
? used_priorities
|
|
||||||
: (used_priorities & ((1ULL << (min_priority + 1)) - 1));
|
|
||||||
return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
|
|
||||||
const u32 priority = highest_priority_set(max_prio);
|
|
||||||
return priority == Depth ? cend()
|
|
||||||
: const_iterator{*this, levels[priority].cbegin(), priority};
|
|
||||||
}
|
|
||||||
[[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
|
|
||||||
return cbegin(max_prio);
|
|
||||||
}
|
|
||||||
[[nodiscard]] iterator begin(u32 max_prio = 0) {
|
|
||||||
const u32 priority = highest_priority_set(max_prio);
|
|
||||||
return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
|
|
||||||
return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
|
|
||||||
}
|
|
||||||
[[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
|
|
||||||
return cend(min_prio);
|
|
||||||
}
|
|
||||||
[[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
|
|
||||||
return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] T& front(u32 max_priority = 0) {
|
|
||||||
const u32 priority = highest_priority_set(max_priority);
|
|
||||||
return levels[priority == Depth ? 0 : priority].front();
|
|
||||||
}
|
|
||||||
[[nodiscard]] const T& front(u32 max_priority = 0) const {
|
|
||||||
const u32 priority = highest_priority_set(max_priority);
|
|
||||||
return levels[priority == Depth ? 0 : priority].front();
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] T& back(u32 min_priority = Depth - 1) {
|
|
||||||
const u32 priority = lowest_priority_set(min_priority); // intended
|
|
||||||
return levels[priority == Depth ? 63 : priority].back();
|
|
||||||
}
|
|
||||||
[[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
|
|
||||||
const u32 priority = lowest_priority_set(min_priority); // intended
|
|
||||||
return levels[priority == Depth ? 63 : priority].back();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() {
|
|
||||||
used_priorities = 0;
|
|
||||||
for (std::size_t i = 0; i < Depth; i++) {
|
|
||||||
levels[i].clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
using const_list_iterator = typename std::list<T>::const_iterator;
|
|
||||||
|
|
||||||
static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
|
|
||||||
if (shift >= list.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto begin_range = list.begin();
|
|
||||||
const auto end_range = std::next(begin_range, shift);
|
|
||||||
list.splice(list.end(), list, begin_range, end_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ListSplice(std::list<T>& in_list, const_list_iterator position,
|
|
||||||
std::list<T>& out_list, const_list_iterator element) {
|
|
||||||
in_list.splice(position, out_list, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
|
|
||||||
const T& element) {
|
|
||||||
auto it = list.cbegin();
|
|
||||||
while (it != list.cend() && *it != element) {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<std::list<T>, Depth> levels;
|
|
||||||
u64 used_priorities = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Common
|
|
|
@ -8,18 +8,12 @@ namespace Common {
|
||||||
|
|
||||||
PageTable::PageTable() = default;
|
PageTable::PageTable() = default;
|
||||||
|
|
||||||
PageTable::~PageTable() = default;
|
PageTable::~PageTable() noexcept = default;
|
||||||
|
|
||||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
|
||||||
bool has_attribute) {
|
const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
|
||||||
const std::size_t num_page_table_entries{1ULL
|
|
||||||
<< (address_space_width_in_bits - page_size_in_bits)};
|
|
||||||
pointers.resize(num_page_table_entries);
|
pointers.resize(num_page_table_entries);
|
||||||
backing_addr.resize(num_page_table_entries);
|
backing_addr.resize(num_page_table_entries);
|
||||||
|
|
||||||
if (has_attribute) {
|
|
||||||
attributes.resize(num_page_table_entries);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -4,12 +4,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <atomic>
|
||||||
|
#include <tuple>
|
||||||
#include <boost/icl/interval_map.hpp>
|
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/memory_hook.h"
|
|
||||||
#include "common/virtual_buffer.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
@ -22,27 +20,6 @@ enum class PageType : u8 {
|
||||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||||
/// invalidation
|
/// invalidation
|
||||||
RasterizerCachedMemory,
|
RasterizerCachedMemory,
|
||||||
/// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
|
|
||||||
Special,
|
|
||||||
/// Page is allocated for use.
|
|
||||||
Allocated,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SpecialRegion {
|
|
||||||
enum class Type {
|
|
||||||
DebugHook,
|
|
||||||
IODevice,
|
|
||||||
} type;
|
|
||||||
|
|
||||||
MemoryHookPointer handler;
|
|
||||||
|
|
||||||
[[nodiscard]] bool operator<(const SpecialRegion& other) const {
|
|
||||||
return std::tie(type, handler) < std::tie(other.type, other.handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool operator==(const SpecialRegion& other) const {
|
|
||||||
return std::tie(type, handler) == std::tie(other.type, other.handler);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,27 +27,84 @@ struct SpecialRegion {
|
||||||
* mimics the way a real CPU page table works.
|
* mimics the way a real CPU page table works.
|
||||||
*/
|
*/
|
||||||
struct PageTable {
|
struct PageTable {
|
||||||
PageTable();
|
/// Number of bits reserved for attribute tagging.
|
||||||
~PageTable();
|
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||||
|
static constexpr int ATTRIBUTE_BITS = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resizes the page table to be able to accomodate enough pages within
|
* Pair of host pointer and page type attribute.
|
||||||
|
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||||
|
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||||
|
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||||
|
*/
|
||||||
|
class PageInfo {
|
||||||
|
public:
|
||||||
|
/// Returns the page pointer
|
||||||
|
[[nodiscard]] u8* Pointer() const noexcept {
|
||||||
|
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the page type attribute
|
||||||
|
[[nodiscard]] PageType Type() const noexcept {
|
||||||
|
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||||
|
[[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept {
|
||||||
|
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||||
|
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the raw representation of the page information.
|
||||||
|
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||||
|
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||||
|
return raw.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a page pointer and type pair atomically
|
||||||
|
void Store(u8* pointer, PageType type) noexcept {
|
||||||
|
raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpack a pointer from a page info raw representation
|
||||||
|
[[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept {
|
||||||
|
return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpack a page type from a page info raw representation
|
||||||
|
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||||
|
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<uintptr_t> raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
PageTable();
|
||||||
|
~PageTable() noexcept;
|
||||||
|
|
||||||
|
PageTable(const PageTable&) = delete;
|
||||||
|
PageTable& operator=(const PageTable&) = delete;
|
||||||
|
|
||||||
|
PageTable(PageTable&&) noexcept = default;
|
||||||
|
PageTable& operator=(PageTable&&) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the page table to be able to accommodate enough pages within
|
||||||
* a given address space.
|
* a given address space.
|
||||||
*
|
*
|
||||||
* @param address_space_width_in_bits The address size width in bits.
|
* @param address_space_width_in_bits The address size width in bits.
|
||||||
|
* @param page_size_in_bits The page size in bits.
|
||||||
*/
|
*/
|
||||||
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
|
void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
|
||||||
bool has_attribute);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
* Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||||
* corresponding entry in the `attributes` vector is of type `Memory`.
|
* corresponding attribute element is of type `Memory`.
|
||||||
*/
|
*/
|
||||||
VirtualBuffer<u8*> pointers;
|
VirtualBuffer<PageInfo> pointers;
|
||||||
|
|
||||||
VirtualBuffer<u64> backing_addr;
|
VirtualBuffer<u64> backing_addr;
|
||||||
|
|
||||||
VirtualBuffer<PageType> attributes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
struct ScopeExitHelper {
|
struct ScopeExitHelper {
|
||||||
explicit ScopeExitHelper(Func&& func) : func(std::move(func)) {}
|
explicit ScopeExitHelper(Func&& func_) : func(std::move(func_)) {}
|
||||||
~ScopeExitHelper() {
|
~ScopeExitHelper() {
|
||||||
if (active) {
|
if (active) {
|
||||||
func();
|
func();
|
||||||
|
|
|
@ -15,6 +15,14 @@ namespace Common {
|
||||||
*/
|
*/
|
||||||
class SpinLock {
|
class SpinLock {
|
||||||
public:
|
public:
|
||||||
|
SpinLock() = default;
|
||||||
|
|
||||||
|
SpinLock(const SpinLock&) = delete;
|
||||||
|
SpinLock& operator=(const SpinLock&) = delete;
|
||||||
|
|
||||||
|
SpinLock(SpinLock&&) = delete;
|
||||||
|
SpinLock& operator=(SpinLock&&) = delete;
|
||||||
|
|
||||||
void lock();
|
void lock();
|
||||||
void unlock();
|
void unlock();
|
||||||
[[nodiscard]] bool try_lock();
|
[[nodiscard]] bool try_lock();
|
||||||
|
|
47
src/common/stream.cpp
Normal file
47
src/common/stream.cpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright 2020 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "common/stream.h"
|
||||||
|
|
||||||
|
namespace Common {
|
||||||
|
|
||||||
|
Stream::Stream() = default;
|
||||||
|
Stream::~Stream() = default;
|
||||||
|
|
||||||
|
void Stream::Seek(s32 offset, SeekOrigin origin) {
|
||||||
|
if (origin == SeekOrigin::SetOrigin) {
|
||||||
|
if (offset < 0) {
|
||||||
|
position = 0;
|
||||||
|
} else if (position >= buffer.size()) {
|
||||||
|
position = buffer.size();
|
||||||
|
} else {
|
||||||
|
position = offset;
|
||||||
|
}
|
||||||
|
} else if (origin == SeekOrigin::FromCurrentPos) {
|
||||||
|
Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
|
||||||
|
} else if (origin == SeekOrigin::FromEnd) {
|
||||||
|
Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 Stream::ReadByte() {
|
||||||
|
if (position < buffer.size()) {
|
||||||
|
return buffer[position++];
|
||||||
|
} else {
|
||||||
|
throw std::out_of_range("Attempting to read a byte not within the buffer range");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stream::WriteByte(u8 byte) {
|
||||||
|
if (position == buffer.size()) {
|
||||||
|
buffer.push_back(byte);
|
||||||
|
position++;
|
||||||
|
} else {
|
||||||
|
buffer.insert(buffer.begin() + position, byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Common
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue