Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 84239b87ac | |||
| 36b3cc7cf0 | |||
| ca68f0f91d |
@@ -1,4 +0,0 @@
|
||||
dist/languages/* linguist-vendored
|
||||
dist/qt_themes/* linguist-vendored
|
||||
externals/* linguist-vendored
|
||||
*.h linguist-language=cpp
|
||||
@@ -31,12 +31,3 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus
|
||||
url = https://github.com/ogniK5377/opus.git
|
||||
[submodule "soundtouch"]
|
||||
path = externals/soundtouch
|
||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||
[submodule "libressl"]
|
||||
path = externals/libressl
|
||||
url = https://github.com/citra-emu/ext-libressl-portable.git
|
||||
[submodule "discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/discordapp/discord-rpc.git
|
||||
|
||||
+5
-16
@@ -20,28 +20,13 @@ matrix:
|
||||
install: "./.travis/linux/deps.sh"
|
||||
script: "./.travis/linux/build.sh"
|
||||
after_success: "./.travis/linux/upload.sh"
|
||||
cache: ccache
|
||||
- os: osx
|
||||
env: NAME="macos build"
|
||||
sudo: false
|
||||
osx_image: xcode10
|
||||
osx_image: xcode9.3
|
||||
install: "./.travis/macos/deps.sh"
|
||||
script: "./.travis/macos/build.sh"
|
||||
after_success: "./.travis/macos/upload.sh"
|
||||
cache: ccache
|
||||
- os: linux
|
||||
env: NAME="MinGW build"
|
||||
sudo: required
|
||||
dist: trusty
|
||||
services: docker
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- p7zip-full
|
||||
install: "./.travis/linux-mingw/deps.sh"
|
||||
script: "./.travis/linux-mingw/build.sh"
|
||||
after_success: "./.travis/linux-mingw/upload.sh"
|
||||
cache: ccache
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
@@ -57,3 +42,7 @@ notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://api.yuzu-emu.org/code/travis/notify
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ccache
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \
|
||||
if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \
|
||||
dist/*.svg dist/*.xml; then
|
||||
echo Trailing whitespace found, aborting
|
||||
exit 1
|
||||
|
||||
@@ -11,9 +11,6 @@ if [ -z $TRAVIS_TAG ]; then
|
||||
RELEASE_NAME=head
|
||||
else
|
||||
RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1)
|
||||
if [ "$NAME" = "MinGW build" ]; then
|
||||
RELEASE_NAME="${RELEASE_NAME}-mingw"
|
||||
fi
|
||||
fi
|
||||
|
||||
mv "$REV_NAME" $RELEASE_NAME
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
# List of environment variables to be shared with Docker containers
|
||||
CI
|
||||
TRAVIS
|
||||
CONTINUOUS_INTEGRATION
|
||||
TRAVIS_BRANCH
|
||||
TRAVIS_BUILD_ID
|
||||
TRAVIS_BUILD_NUMBER
|
||||
TRAVIS_COMMIT
|
||||
TRAVIS_JOB_ID
|
||||
TRAVIS_JOB_NUMBER
|
||||
TRAVIS_REPO_SLUG
|
||||
TRAVIS_TAG
|
||||
|
||||
# yuzu specific flags
|
||||
ENABLE_COMPATIBILITY_REPORTING
|
||||
USE_DISCORD_PRESENCE
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
mkdir "$HOME/.ccache" || true
|
||||
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull ubuntu:18.04
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
cd /yuzu
|
||||
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
|
||||
apt-get update
|
||||
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
|
||||
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
|
||||
apt-get update
|
||||
apt-get install -y ${MINGW_PACKAGES}
|
||||
|
||||
# fix a problem in current MinGW headers
|
||||
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
|
||||
# override Travis CI unreasonable ccache size
|
||||
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
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
|
||||
# Clean up the dirty hacks
|
||||
rm /bin/uname && mv /bin/uname1 /bin/uname
|
||||
rm /bin/cmd
|
||||
|
||||
ccache -s
|
||||
|
||||
echo "Tests skipped"
|
||||
#ctest -VV -C Release
|
||||
|
||||
echo 'Prepare binaries...'
|
||||
cd ..
|
||||
mkdir package
|
||||
|
||||
QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/'
|
||||
find build/ -name "yuzu*.exe" -exec cp {} 'package' \;
|
||||
|
||||
# copy Qt plugins
|
||||
mkdir package/platforms
|
||||
cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/
|
||||
cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/
|
||||
cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/
|
||||
rm -f package/mediaservice/*d.dll
|
||||
|
||||
for i in package/*.exe; do
|
||||
# we need to process pdb here, however, cv2pdb
|
||||
# does not work here, so we just simply strip all the debug symbols
|
||||
x86_64-w64-mingw32-strip "${i}"
|
||||
done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
|
||||
@@ -1,106 +0,0 @@
|
||||
import pefile
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import queue
|
||||
import shutil
|
||||
|
||||
# constant definitions
|
||||
KNOWN_SYS_DLLS = ['WINMM.DLL', 'MSVCRT.DLL', 'VERSION.DLL', 'MPR.DLL',
|
||||
'DWMAPI.DLL', 'UXTHEME.DLL', 'DNSAPI.DLL', 'IPHLPAPI.DLL']
|
||||
# below is for Ubuntu 18.04 with specified PPA enabled, if you are using
|
||||
# other distro or different repositories, change the following accordingly
|
||||
DLL_PATH = [
|
||||
'/usr/x86_64-w64-mingw32/bin/',
|
||||
'/usr/x86_64-w64-mingw32/lib/',
|
||||
'/usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/'
|
||||
]
|
||||
|
||||
missing = []
|
||||
|
||||
|
||||
def parse_imports(file_name):
|
||||
results = []
|
||||
pe = pefile.PE(file_name, fast_load=True)
|
||||
pe.parse_data_directories()
|
||||
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
current = entry.dll.decode()
|
||||
current_u = current.upper() # b/c Windows is often case insensitive
|
||||
# here we filter out system dlls
|
||||
# dll w/ names like *32.dll are likely to be system dlls
|
||||
if current_u.upper() not in KNOWN_SYS_DLLS and not re.match(string=current_u, pattern=r'.*32\.DLL'):
|
||||
results.append(current)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def parse_imports_recursive(file_name, path_list=[]):
|
||||
q = queue.Queue() # create a FIFO queue
|
||||
# file_name can be a string or a list for the convience
|
||||
if isinstance(file_name, str):
|
||||
q.put(file_name)
|
||||
elif isinstance(file_name, list):
|
||||
for i in file_name:
|
||||
q.put(i)
|
||||
full_list = []
|
||||
while q.qsize():
|
||||
current = q.get_nowait()
|
||||
print('> %s' % current)
|
||||
deps = parse_imports(current)
|
||||
# if this dll does not have any import, ignore it
|
||||
if not deps:
|
||||
continue
|
||||
for dep in deps:
|
||||
# the dependency already included in the list, skip
|
||||
if dep in full_list:
|
||||
continue
|
||||
# find the requested dll in the provided paths
|
||||
full_path = find_dll(dep)
|
||||
if not full_path:
|
||||
missing.append(dep)
|
||||
continue
|
||||
full_list.append(dep)
|
||||
q.put(full_path)
|
||||
path_list.append(full_path)
|
||||
return full_list
|
||||
|
||||
|
||||
def find_dll(name):
|
||||
for path in DLL_PATH:
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
if name.lower() == f.lower():
|
||||
return os.path.join(root, f)
|
||||
|
||||
|
||||
def deploy(name, dst, dry_run=False):
|
||||
dlls_path = []
|
||||
parse_imports_recursive(name, dlls_path)
|
||||
for dll_entry in dlls_path:
|
||||
if not dry_run:
|
||||
shutil.copy(dll_entry, dst)
|
||||
else:
|
||||
print('[Dry-Run] Copy %s to %s' % (dll_entry, dst))
|
||||
print('Deploy completed.')
|
||||
return dlls_path
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print('Usage: %s [files to examine ...] [target deploy directory]')
|
||||
return 1
|
||||
to_deploy = sys.argv[1:-1]
|
||||
tgt_dir = sys.argv[-1]
|
||||
if not os.path.isdir(tgt_dir):
|
||||
print('%s is not a directory.' % tgt_dir)
|
||||
return 1
|
||||
print('Scanning dependencies...')
|
||||
deploy(to_deploy, tgt_dir)
|
||||
if missing:
|
||||
print('Following DLLs are not found: %s' % ('\n'.join(missing)))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
. .travis/common/pre-upload.sh
|
||||
|
||||
REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.gz"
|
||||
COMPRESSION_FLAGS="-czvf"
|
||||
|
||||
mkdir "$REV_NAME"
|
||||
# get around the permission issues
|
||||
cp -r package/* "$REV_NAME"
|
||||
|
||||
. .travis/common/post-upload.sh
|
||||
@@ -1,4 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
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 ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
|
||||
@@ -5,10 +5,14 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
|
||||
|
||||
cd /yuzu
|
||||
|
||||
export PATH=/usr/lib/ccache:$PATH
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
||||
mkdir build && cd build
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=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 -G Ninja
|
||||
ccache --show-stats > ccache_before
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
ninja
|
||||
|
||||
ccache -s
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.12
|
||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
mkdir build && cd build
|
||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
||||
ccache --show-stats > ccache_before
|
||||
cmake --version
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
|
||||
ccache -s
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
+39
-60
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
|
||||
# CMake 3.6 required for FindBoost to define IMPORTED libs properly on unknown Boost versions
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
include(DownloadExternals)
|
||||
@@ -15,29 +15,25 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
|
||||
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks)
|
||||
endif()
|
||||
|
||||
# Sanity check : Check that all submodules are present
|
||||
# =======================================================================
|
||||
|
||||
function(check_submodules_present)
|
||||
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
|
||||
foreach(module ${gitmodules})
|
||||
string(REGEX REPLACE "path *= *" "" module ${module})
|
||||
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
|
||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
|
||||
message(FATAL_ERROR "Git submodule ${module} not found. "
|
||||
"Please run: git submodule update --init --recursive")
|
||||
endif()
|
||||
@@ -45,19 +41,6 @@ function(check_submodules_present)
|
||||
endfunction()
|
||||
check_submodules_present()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
COPYONLY)
|
||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
message(STATUS "Downloading compatibility list for yuzu...")
|
||||
file(DOWNLOAD
|
||||
https://api.yuzu-emu.org/gamedb/
|
||||
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||
endif()
|
||||
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
endif()
|
||||
|
||||
# Detect current compilation architecture and create standard definitions
|
||||
# =======================================================================
|
||||
|
||||
@@ -83,12 +66,10 @@ if (NOT ENABLE_GENERIC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
detect_architecture("_M_ARM64" ARM64)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
detect_architecture("__aarch64__" ARM64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -127,6 +108,8 @@ else()
|
||||
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
|
||||
add_definitions(/DWIN32_LEAN_AND_MEAN)
|
||||
|
||||
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||
|
||||
# Tweak optimization settings
|
||||
@@ -170,7 +153,7 @@ endif()
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
# glibc, which may default to 32 bits. glibc allows this to be configured
|
||||
# by setting _FILE_OFFSET_BITS.
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
endif()
|
||||
|
||||
@@ -178,6 +161,10 @@ endif()
|
||||
set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
|
||||
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
|
||||
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
|
||||
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
@@ -185,13 +172,13 @@ find_package(Boost 1.63.0 QUIET)
|
||||
if (NOT Boost_FOUND)
|
||||
message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
|
||||
|
||||
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost")
|
||||
set(Boost_NO_SYSTEM_PATHS OFF)
|
||||
find_package(Boost QUIET REQUIRED)
|
||||
endif()
|
||||
|
||||
# Output binaries to bin/
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
@@ -200,8 +187,8 @@ find_package(Threads REQUIRED)
|
||||
if (ENABLE_SDL2)
|
||||
if (YUZU_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.0.8")
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.0.5")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||
endif()
|
||||
@@ -233,7 +220,7 @@ 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 1920) AND ARCHITECTURE_x86_64)
|
||||
if (MSVC14 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.")
|
||||
@@ -260,25 +247,17 @@ if (YUZU_USE_BUNDLED_UNICORN)
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
|
||||
set(UNICORN_PREFIX ${CMAKE_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)
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh cross-win64
|
||||
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()
|
||||
|
||||
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}
|
||||
)
|
||||
# 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
|
||||
@@ -292,7 +271,6 @@ 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()
|
||||
@@ -301,7 +279,7 @@ endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.10.0-msvc2015_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
@@ -325,7 +303,7 @@ endif()
|
||||
# ======================================
|
||||
|
||||
IF (APPLE)
|
||||
find_library(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
|
||||
FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
@@ -344,6 +322,14 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
ENDIF (APPLE)
|
||||
|
||||
# MINGW: GCC does not support codecvt, so use iconv instead
|
||||
if (UNIX OR MINGW)
|
||||
find_library(ICONV_LIBRARY NAMES iconv)
|
||||
if (ICONV_LIBRARY)
|
||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup a custom clang-format target (if clang-format can be found) that will run
|
||||
# against all the src files. This should be used before making a pull request.
|
||||
# =======================================================================
|
||||
@@ -352,12 +338,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
|
||||
find_program(CLANG_FORMAT
|
||||
NAMES clang-format${CLANG_FORMAT_POSTFIX}
|
||||
clang-format
|
||||
PATHS ${PROJECT_BINARY_DIR}/externals)
|
||||
PATHS ${CMAKE_BINARY_DIR}/externals)
|
||||
# if find_program doesn't find it, try to download from externals
|
||||
if (NOT CLANG_FORMAT)
|
||||
if (WIN32)
|
||||
message(STATUS "Clang format not found! Downloading...")
|
||||
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
file(DOWNLOAD
|
||||
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
|
||||
"${CLANG_FORMAT}" SHOW_PROGRESS
|
||||
@@ -373,7 +359,7 @@ if (NOT CLANG_FORMAT)
|
||||
endif()
|
||||
|
||||
if (CLANG_FORMAT)
|
||||
set(SRCS ${PROJECT_SOURCE_DIR}/src)
|
||||
set(SRCS ${CMAKE_SOURCE_DIR}/src)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
@@ -430,13 +416,6 @@ enable_testing()
|
||||
add_subdirectory(externals)
|
||||
add_subdirectory(src)
|
||||
|
||||
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
|
||||
if(ENABLE_QT)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
|
||||
else()
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd)
|
||||
endif()
|
||||
|
||||
|
||||
# Installation instructions
|
||||
# =========================
|
||||
@@ -446,10 +425,10 @@ endif()
|
||||
# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
|
||||
endif()
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
# Actually a hack, w/o this will cause some strange errors
|
||||
set(CMAKE_HOST_WIN32 TRUE)
|
||||
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
|
||||
set(SDL2_PATH ${MINGW_PREFIX})
|
||||
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
|
||||
|
||||
# Specify the cross compiler
|
||||
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc-posix)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++-posix)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
|
||||
# Mingw tools
|
||||
set(STRIP ${MINGW_TOOL_PREFIX}strip)
|
||||
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
|
||||
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
|
||||
|
||||
# ccache wrapper
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache found in PATH")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else(CCACHE)
|
||||
message(WARNING "USE_CCACHE enabled, but no ccache found")
|
||||
endif(CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
# Search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
|
||||
# Echo modified cmake vars to screen for debugging purposes
|
||||
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
|
||||
message("")
|
||||
message("Custom cmake vars: (blank = system default)")
|
||||
message("-----------------------------------------")
|
||||
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
|
||||
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
|
||||
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
|
||||
message("* WINDRES : ${WINDRES}")
|
||||
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
|
||||
message("* STRIP : ${STRIP}")
|
||||
message("* USE_CCACHE : ${USE_CCACHE}")
|
||||
message("")
|
||||
# So that the debug info only appears once
|
||||
set(ENV{MINGW_DEBUG_INFO} SHOWN)
|
||||
endif()
|
||||
+1
-1
@@ -22,7 +22,7 @@ If clang format is found, then cmake will add a custom build target that can be
|
||||
* Don't ever introduce new external dependencies into Core
|
||||
* Don't use any platform specific code in Core
|
||||
* Use namespaces often
|
||||
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
|
||||
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`. The only exception to this rule is for casting between two numeric types, where C-style casts are encouraged for brevity and readability.
|
||||
|
||||
### Naming Rules
|
||||
* Functions: `PascalCase`
|
||||
|
||||
+6
-3
@@ -39,12 +39,11 @@ before_build:
|
||||
- mkdir %BUILD_TYPE%_build
|
||||
- cd %BUILD_TYPE%_build
|
||||
- ps: |
|
||||
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
|
||||
}
|
||||
- cd ..
|
||||
|
||||
@@ -163,6 +162,10 @@ artifacts:
|
||||
- path: $(BUILD_ZIP)
|
||||
name: build
|
||||
type: zip
|
||||
- path: $(BUILD_SYMBOLS)
|
||||
name: debugsymbols
|
||||
- path: $(BUILD_UPDATE)
|
||||
name: update
|
||||
|
||||
deploy:
|
||||
provider: GitHub
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="compatibility_list">
|
||||
<file>compatibility_list.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
Vendored
-31
@@ -43,16 +43,10 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
|
||||
# Open Source Archives
|
||||
add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
|
||||
|
||||
# Unicorn
|
||||
add_library(unicorn-headers INTERFACE)
|
||||
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
|
||||
|
||||
# SoundTouch
|
||||
add_subdirectory(soundtouch)
|
||||
|
||||
# Xbyak
|
||||
if (ARCHITECTURE_x86_64)
|
||||
# Defined before "dynarmic" above
|
||||
@@ -70,28 +64,3 @@ if(ENABLE_CUBEB)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
# DiscordRPC
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
|
||||
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||
|
||||
# lurlparser
|
||||
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
|
||||
|
||||
# httplib
|
||||
add_library(httplib INTERFACE)
|
||||
target_include_directories(httplib INTERFACE ./httplib)
|
||||
|
||||
# JSON
|
||||
add_library(json-headers INTERFACE)
|
||||
target_include_directories(json-headers INTERFACE ./json)
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule externals/boost updated: 0b920df1c9...d80e506e17
Vendored
-1
Submodule externals/discord-rpc deleted from e32d001809
Vendored
+1
-1
Submodule externals/dynarmic updated: 4e6848d1c9...4f96c63025
Vendored
+1
-1
Submodule externals/fmt updated: 3e75ad9822...c2ce7e4f07
Vendored
-15
@@ -1,15 +0,0 @@
|
||||
From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3
|
||||
|
||||
MIT License
|
||||
|
||||
===
|
||||
|
||||
cpp-httplib
|
||||
|
||||
A C++11 header-only HTTP library.
|
||||
|
||||
It's extremely easy to setup. Just include httplib.h file in your code!
|
||||
|
||||
Inspired by Sinatra and express.
|
||||
|
||||
© 2017 Yuji Hirose
|
||||
Vendored
-2344
File diff suppressed because it is too large
Load Diff
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
JSON for Modern C++
|
||||
===================
|
||||
|
||||
v3.1.2
|
||||
|
||||
This is a mirror providing the single required header file.
|
||||
|
||||
The original repository can be found at:
|
||||
https://github.com/nlohmann/json/commit/d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe
|
||||
Vendored
-17300
File diff suppressed because it is too large
Load Diff
Vendored
-1
Submodule externals/libressl deleted from 7d01cb01cb
Vendored
-8
@@ -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})
|
||||
Vendored
-265
@@ -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;
|
||||
}
|
||||
Vendored
-78
@@ -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
|
||||
Vendored
-19
@@ -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++.
|
||||
Vendored
+1
-1
Submodule externals/mbedtls updated: a280e602f3...d409b75a4c
-16
@@ -1,16 +0,0 @@
|
||||
add_library(open_source_archives
|
||||
src/FontChineseSimplified.cpp
|
||||
src/FontChineseTraditional.cpp
|
||||
src/FontExtendedChineseSimplified.cpp
|
||||
src/FontKorean.cpp
|
||||
src/FontNintendoExtended.cpp
|
||||
src/FontStandard.cpp
|
||||
include/FontChineseSimplified.h
|
||||
include/FontChineseTraditional.h
|
||||
include/FontExtendedChineseSimplified.h
|
||||
include/FontKorean.h
|
||||
include/FontNintendoExtended.h
|
||||
include/FontStandard.h
|
||||
)
|
||||
|
||||
target_include_directories(open_source_archives PUBLIC include)
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
These files were generated by https://github.com/FearlessTobi/yuzu_system_archives at git commit 0a24b0c9f38d71fb2c4bba5645a39029e539a5ec. To generate the files use the run.sh inside that repository.
|
||||
|
||||
The follwing system archives are currently included:
|
||||
- JPN/EUR/USA System Font
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontChineseSimplified;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 222236> FontChineseTraditional;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 293516> FontExtendedChineseSimplified;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontKorean;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 172064> FontNintendoExtended;
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontStandard;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
-18112
File diff suppressed because it is too large
Load Diff
-14344
File diff suppressed because it is too large
Load Diff
-18112
File diff suppressed because it is too large
Load Diff
Vendored
-1
Submodule externals/soundtouch deleted from 060181eaf2
Vendored
+1
-1
Submodule externals/xbyak updated: 1de435ed04...71b75f653f
@@ -13,6 +13,3 @@ endif()
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(yuzu)
|
||||
endif()
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
add_subdirectory(web_service)
|
||||
endif()
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
add_library(audio_core STATIC
|
||||
algorithm/filter.cpp
|
||||
algorithm/filter.h
|
||||
algorithm/interpolate.cpp
|
||||
algorithm/interpolate.h
|
||||
audio_out.cpp
|
||||
audio_out.h
|
||||
audio_renderer.cpp
|
||||
@@ -11,14 +7,12 @@ add_library(audio_core STATIC
|
||||
codec.cpp
|
||||
codec.h
|
||||
null_sink.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
sink.h
|
||||
sink_details.cpp
|
||||
sink_details.h
|
||||
sink_stream.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
time_stretch.cpp
|
||||
time_stretch.h
|
||||
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||
)
|
||||
@@ -26,7 +20,6 @@ add_library(audio_core STATIC
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
target_link_libraries(audio_core PRIVATE SoundTouch)
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/filter.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
Filter Filter::LowPass(double cutoff, double Q) {
|
||||
const double w0 = 2.0 * M_PI * cutoff;
|
||||
const double sin_w0 = std::sin(w0);
|
||||
const double cos_w0 = std::cos(w0);
|
||||
const double alpha = sin_w0 / (2 * Q);
|
||||
|
||||
const double a0 = 1 + alpha;
|
||||
const double a1 = -2.0 * cos_w0;
|
||||
const double a2 = 1 - alpha;
|
||||
const double b0 = 0.5 * (1 - cos_w0);
|
||||
const double b1 = 1.0 * (1 - cos_w0);
|
||||
const double b2 = 0.5 * (1 - cos_w0);
|
||||
|
||||
return {a0, a1, a2, b0, b1, b2};
|
||||
}
|
||||
|
||||
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)
|
||||
: a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
|
||||
|
||||
void Filter::Process(std::vector<s16>& signal) {
|
||||
const std::size_t num_frames = signal.size() / 2;
|
||||
for (std::size_t i = 0; i < num_frames; i++) {
|
||||
std::rotate(in.begin(), in.end() - 1, in.end());
|
||||
std::rotate(out.begin(), out.end() - 1, out.end());
|
||||
|
||||
for (std::size_t ch = 0; ch < channel_count; ch++) {
|
||||
in[0][ch] = signal[i * channel_count + ch];
|
||||
|
||||
out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] -
|
||||
a2 * out[2][ch];
|
||||
|
||||
signal[i * 2 + ch] = static_cast<s16>(std::clamp(out[0][ch], -32768.0, 32767.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the appropriate Q for each biquad in a cascading filter.
|
||||
/// @param total_count The total number of biquads to be cascaded.
|
||||
/// @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) {
|
||||
const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
|
||||
return 1.0 / (2.0 * std::cos(pole));
|
||||
}
|
||||
|
||||
CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) {
|
||||
std::vector<Filter> cascade(cascade_size);
|
||||
for (std::size_t i = 0; i < cascade_size; i++) {
|
||||
cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i));
|
||||
}
|
||||
return CascadingFilter{std::move(cascade)};
|
||||
}
|
||||
|
||||
CascadingFilter::CascadingFilter() = default;
|
||||
CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
|
||||
|
||||
void CascadingFilter::Process(std::vector<s16>& signal) {
|
||||
for (auto& filter : filters) {
|
||||
filter.Process(signal);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// Digital biquad filter:
|
||||
///
|
||||
/// b0 + b1 z^-1 + b2 z^-2
|
||||
/// H(z) = ------------------------
|
||||
/// a0 + a1 z^-1 + b2 z^-2
|
||||
class Filter {
|
||||
public:
|
||||
/// Creates a low-pass filter.
|
||||
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
|
||||
/// @param Q Determines the quality factor of this filter.
|
||||
static Filter LowPass(double cutoff, double Q = 0.7071);
|
||||
|
||||
/// Passthrough filter.
|
||||
Filter();
|
||||
|
||||
Filter(double a0, double a1, double a2, double b0, double b1, double b2);
|
||||
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t channel_count = 2;
|
||||
|
||||
/// Coefficients are in normalized form (a0 = 1.0).
|
||||
double a1, a2, b0, b1, b2;
|
||||
/// Input History
|
||||
std::array<std::array<double, channel_count>, 3> in;
|
||||
/// Output History
|
||||
std::array<std::array<double, channel_count>, 3> out;
|
||||
};
|
||||
|
||||
/// Cascade filters to build up higher-order filters from lower-order ones.
|
||||
class CascadingFilter {
|
||||
public:
|
||||
/// Creates a cascading low-pass filter.
|
||||
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
|
||||
/// @param cascade_size Number of biquads in cascade.
|
||||
static CascadingFilter LowPass(double cutoff, std::size_t cascade_size);
|
||||
|
||||
/// Passthrough.
|
||||
CascadingFilter();
|
||||
|
||||
explicit CascadingFilter(std::vector<Filter> filters);
|
||||
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
private:
|
||||
std::vector<Filter> filters;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// The Lanczos kernel
|
||||
static double Lanczos(std::size_t a, double x) {
|
||||
if (x == 0.0)
|
||||
return 1.0;
|
||||
const double px = M_PI * x;
|
||||
return a * std::sin(px) * std::sin(px / a) / (px * px);
|
||||
}
|
||||
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
|
||||
if (input.size() < 2)
|
||||
return {};
|
||||
|
||||
if (ratio <= 0) {
|
||||
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
|
||||
ratio = 1.0;
|
||||
}
|
||||
|
||||
if (ratio != state.current_ratio) {
|
||||
const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio);
|
||||
state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3);
|
||||
state.current_ratio = ratio;
|
||||
}
|
||||
state.nyquist.Process(input);
|
||||
|
||||
constexpr std::size_t taps = InterpolationState::lanczos_taps;
|
||||
const std::size_t num_frames = input.size() / 2;
|
||||
|
||||
std::vector<s16> output;
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
|
||||
|
||||
double& pos = state.position;
|
||||
auto& h = state.history;
|
||||
for (std::size_t i = 0; i < num_frames; ++i) {
|
||||
std::rotate(h.begin(), h.end() - 1, h.end());
|
||||
h[0][0] = input[i * 2 + 0];
|
||||
h[0][1] = input[i * 2 + 1];
|
||||
|
||||
while (pos <= 1.0) {
|
||||
double l = 0.0;
|
||||
double r = 0.0;
|
||||
for (std::size_t j = 0; j < h.size(); j++) {
|
||||
l += Lanczos(taps, pos + j - taps + 1) * h[j][0];
|
||||
r += Lanczos(taps, pos + j - taps + 1) * h[j][1];
|
||||
}
|
||||
output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
|
||||
|
||||
pos += ratio;
|
||||
}
|
||||
pos -= 1.0;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/filter.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct InterpolationState {
|
||||
static constexpr std::size_t lanczos_taps = 4;
|
||||
static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
|
||||
|
||||
double current_ratio = 0.0;
|
||||
CascadingFilter nyquist;
|
||||
std::array<std::array<s16, 2>, history_size> history = {};
|
||||
double position = 0;
|
||||
};
|
||||
|
||||
/// Interpolates input signal to produce output signal.
|
||||
/// @param input The signal to interpolate.
|
||||
/// @param ratio Interpolation ratio.
|
||||
/// ratio > 1.0 results in fewer output samples.
|
||||
/// ratio < 1.0 results in more output samples.
|
||||
/// @returns Output signal.
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio);
|
||||
|
||||
/// Interpolates input signal to produce output signal.
|
||||
/// @param input The signal to interpolate.
|
||||
/// @param input_rate The sample rate of input.
|
||||
/// @param output_rate The desired sample rate of the output.
|
||||
/// @returns Output signal.
|
||||
inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
|
||||
u32 input_rate, u32 output_rate) {
|
||||
const double ratio = static_cast<double>(input_rate) / static_cast<double>(output_rate);
|
||||
return Interpolate(state, std::move(input), ratio);
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -39,8 +39,7 @@ StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&&
|
||||
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||
std::size_t max_count) {
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
|
||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
Stream::ReleaseCallback&& release_callback);
|
||||
|
||||
/// 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, size_t max_count);
|
||||
|
||||
/// Starts an audio stream for playback
|
||||
void StartStream(StreamPtr stream);
|
||||
|
||||
@@ -2,13 +2,9 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "audio_core/audio_out.h"
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "audio_core/codec.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -16,94 +12,20 @@ namespace AudioCore {
|
||||
constexpr u32 STREAM_SAMPLE_RATE{48000};
|
||||
constexpr u32 STREAM_NUM_CHANNELS{2};
|
||||
|
||||
class AudioRenderer::VoiceState {
|
||||
public:
|
||||
bool IsPlaying() const {
|
||||
return is_in_use && info.play_state == PlayState::Started;
|
||||
}
|
||||
|
||||
const VoiceOutStatus& GetOutStatus() const {
|
||||
return out_status;
|
||||
}
|
||||
|
||||
const VoiceInfo& GetInfo() const {
|
||||
return info;
|
||||
}
|
||||
|
||||
VoiceInfo& GetInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
void SetWaveIndex(std::size_t index);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count);
|
||||
void UpdateState();
|
||||
void RefreshBuffer();
|
||||
|
||||
private:
|
||||
bool is_in_use{};
|
||||
bool is_refresh_pending{};
|
||||
std::size_t wave_index{};
|
||||
std::size_t offset{};
|
||||
Codec::ADPCMState adpcm_state{};
|
||||
InterpolationState interp_state{};
|
||||
std::vector<s16> samples;
|
||||
VoiceOutStatus out_status{};
|
||||
VoiceInfo info{};
|
||||
};
|
||||
|
||||
class AudioRenderer::EffectState {
|
||||
public:
|
||||
const EffectOutStatus& GetOutStatus() const {
|
||||
return out_status;
|
||||
}
|
||||
|
||||
const EffectInStatus& GetInfo() const {
|
||||
return info;
|
||||
}
|
||||
|
||||
EffectInStatus& GetInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
void UpdateState();
|
||||
|
||||
private:
|
||||
EffectOutStatus out_status{};
|
||||
EffectInStatus info{};
|
||||
};
|
||||
AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event)
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
|
||||
effects(params.effect_count) {
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
|
||||
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
|
||||
[=]() { buffer_event->Signal(); });
|
||||
audio_out->StartStream(stream);
|
||||
audio_core = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_core->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
|
||||
[=]() { buffer_event->Signal(); });
|
||||
audio_core->StartStream(stream);
|
||||
|
||||
QueueMixedBuffer(0);
|
||||
QueueMixedBuffer(1);
|
||||
QueueMixedBuffer(2);
|
||||
}
|
||||
|
||||
AudioRenderer::~AudioRenderer() = default;
|
||||
|
||||
u32 AudioRenderer::GetSampleRate() const {
|
||||
return worker_params.sample_rate;
|
||||
}
|
||||
|
||||
u32 AudioRenderer::GetSampleCount() const {
|
||||
return worker_params.sample_count;
|
||||
}
|
||||
|
||||
u32 AudioRenderer::GetMixBufferCount() const {
|
||||
return worker_params.mix_buffer_count;
|
||||
}
|
||||
|
||||
Stream::State AudioRenderer::GetStreamState() const {
|
||||
return stream->GetState();
|
||||
}
|
||||
|
||||
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
|
||||
// Copy UpdateDataHeader struct
|
||||
UpdateDataHeader config{};
|
||||
@@ -117,29 +39,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
memory_pool_count * sizeof(MemoryPoolInfo));
|
||||
|
||||
// Copy VoiceInfo structs
|
||||
std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
|
||||
config.memory_pools_size + config.voice_resource_size};
|
||||
size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
|
||||
config.voice_resource_size};
|
||||
for (auto& voice : voices) {
|
||||
std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
|
||||
voice_offset += sizeof(VoiceInfo);
|
||||
}
|
||||
|
||||
std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
|
||||
config.memory_pools_size + config.voice_resource_size +
|
||||
config.voices_size};
|
||||
for (auto& effect : effects) {
|
||||
std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
|
||||
effect_offset += sizeof(EffectInStatus);
|
||||
}
|
||||
|
||||
// Update memory pool state
|
||||
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
|
||||
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
|
||||
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
|
||||
memory_pool[index].state = MemoryPoolStates::Attached;
|
||||
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
|
||||
memory_pool[index].state = MemoryPoolStates::Detached;
|
||||
}
|
||||
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
|
||||
offset += sizeof(VoiceInfo);
|
||||
}
|
||||
|
||||
// Update voices
|
||||
@@ -153,8 +57,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& effect : effects) {
|
||||
effect.UpdateState();
|
||||
// Update memory pool state
|
||||
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
|
||||
for (size_t index = 0; index < memory_pool.size(); ++index) {
|
||||
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
|
||||
memory_pool[index].state = MemoryPoolStates::Attached;
|
||||
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
|
||||
memory_pool[index].state = MemoryPoolStates::Detached;
|
||||
}
|
||||
}
|
||||
|
||||
// Release previous buffers and queue next ones for playback
|
||||
@@ -170,30 +80,22 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
|
||||
response_data.memory_pools_size);
|
||||
|
||||
// Copy output voice status
|
||||
std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
|
||||
size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
|
||||
for (const auto& voice : voices) {
|
||||
std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
|
||||
sizeof(VoiceOutStatus));
|
||||
voice_out_status_offset += sizeof(VoiceOutStatus);
|
||||
}
|
||||
|
||||
std::size_t effect_out_status_offset{
|
||||
sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
|
||||
response_data.voice_resource_size};
|
||||
for (const auto& effect : effects) {
|
||||
std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
|
||||
sizeof(EffectOutStatus));
|
||||
effect_out_status_offset += sizeof(EffectOutStatus);
|
||||
}
|
||||
return output_params;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
|
||||
void AudioRenderer::VoiceState::SetWaveIndex(size_t index) {
|
||||
wave_index = index & 3;
|
||||
is_refresh_pending = true;
|
||||
}
|
||||
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) {
|
||||
if (!IsPlaying()) {
|
||||
return {};
|
||||
}
|
||||
@@ -202,9 +104,9 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_co
|
||||
RefreshBuffer();
|
||||
}
|
||||
|
||||
const std::size_t max_size{samples.size() - offset};
|
||||
const std::size_t dequeue_offset{offset};
|
||||
std::size_t size{sample_count * STREAM_NUM_CHANNELS};
|
||||
const size_t max_size{samples.size() - offset};
|
||||
const size_t dequeue_offset{offset};
|
||||
size_t size{sample_count * STREAM_NUM_CHANNELS};
|
||||
if (size > max_size) {
|
||||
size = max_size;
|
||||
}
|
||||
@@ -269,7 +171,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
case 1:
|
||||
// 1 channel is upsampled to 2 channel
|
||||
samples.resize(new_samples.size() * 2);
|
||||
for (std::size_t index = 0; index < new_samples.size(); ++index) {
|
||||
for (size_t index = 0; index < new_samples.size(); ++index) {
|
||||
samples[index * 2] = new_samples[index];
|
||||
samples[index * 2 + 1] = new_samples[index];
|
||||
}
|
||||
@@ -285,35 +187,15 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
break;
|
||||
}
|
||||
|
||||
samples =
|
||||
Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE);
|
||||
|
||||
is_refresh_pending = false;
|
||||
}
|
||||
|
||||
void AudioRenderer::EffectState::UpdateState() {
|
||||
if (info.is_new) {
|
||||
out_status.state = EffectStatus::New;
|
||||
} else {
|
||||
if (info.type == Effect::Aux) {
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
|
||||
"Aux buffers tried to update");
|
||||
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
|
||||
"Aux buffers tried to update");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr s16 ClampToS16(s32 value) {
|
||||
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
||||
}
|
||||
|
||||
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
constexpr std::size_t BUFFER_SIZE{512};
|
||||
constexpr size_t BUFFER_SIZE{512};
|
||||
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
|
||||
|
||||
for (auto& voice : voices) {
|
||||
@@ -321,7 +203,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t offset{};
|
||||
size_t offset{};
|
||||
s64 samples_remaining{BUFFER_SIZE};
|
||||
while (samples_remaining > 0) {
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
|
||||
@@ -330,7 +212,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
break;
|
||||
}
|
||||
|
||||
samples_remaining -= samples.size() / stream->GetNumChannels();
|
||||
samples_remaining -= samples.size();
|
||||
|
||||
for (const auto& sample : samples) {
|
||||
const s32 buffer_sample{buffer[offset]};
|
||||
@@ -339,11 +221,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
}
|
||||
}
|
||||
}
|
||||
audio_out->QueueBuffer(stream, tag, std::move(buffer));
|
||||
audio_core->QueueBuffer(stream, tag, std::move(buffer));
|
||||
}
|
||||
|
||||
void AudioRenderer::ReleaseAndQueueBuffers() {
|
||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
|
||||
const auto released_buffers{audio_core->GetTagsAndReleaseBuffers(stream, 2)};
|
||||
for (const auto& tag : released_buffers) {
|
||||
QueueMixedBuffer(tag);
|
||||
}
|
||||
|
||||
@@ -8,40 +8,25 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/audio_out.h"
|
||||
#include "audio_core/codec.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
#include "core/hle/kernel/event.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioOut;
|
||||
|
||||
enum class PlayState : u8 {
|
||||
Started = 0,
|
||||
Stopped = 1,
|
||||
Paused = 2,
|
||||
};
|
||||
|
||||
enum class Effect : u8 {
|
||||
None = 0,
|
||||
Aux = 2,
|
||||
};
|
||||
|
||||
enum class EffectStatus : u8 {
|
||||
None = 0,
|
||||
New = 1,
|
||||
};
|
||||
|
||||
struct AudioRendererParameter {
|
||||
u32_le sample_rate;
|
||||
u32_le sample_count;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le unknown_8;
|
||||
u32_le unknown_c;
|
||||
u32_le voice_count;
|
||||
u32_le sink_count;
|
||||
@@ -138,43 +123,6 @@ struct VoiceOutStatus {
|
||||
};
|
||||
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
|
||||
|
||||
struct AuxInfo {
|
||||
std::array<u8, 24> input_mix_buffers;
|
||||
std::array<u8, 24> output_mix_buffers;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le sample_rate; // Stored in the aux buffer currently
|
||||
u32_le sampe_count;
|
||||
u64_le send_buffer_info;
|
||||
u64_le send_buffer_base;
|
||||
|
||||
u64_le return_buffer_info;
|
||||
u64_le return_buffer_base;
|
||||
};
|
||||
static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
|
||||
|
||||
struct EffectInStatus {
|
||||
Effect type;
|
||||
u8 is_new;
|
||||
u8 is_enabled;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u32_le mix_id;
|
||||
u64_le buffer_base;
|
||||
u64_le buffer_sz;
|
||||
s32_le priority;
|
||||
INSERT_PADDING_BYTES(4);
|
||||
union {
|
||||
std::array<u8, 0xa0> raw;
|
||||
AuxInfo aux_info;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
|
||||
|
||||
struct EffectOutStatus {
|
||||
EffectStatus state;
|
||||
INSERT_PADDING_BYTES(0xf);
|
||||
};
|
||||
static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
|
||||
|
||||
struct UpdateDataHeader {
|
||||
UpdateDataHeader() {}
|
||||
|
||||
@@ -209,25 +157,49 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
|
||||
~AudioRenderer();
|
||||
|
||||
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
|
||||
void QueueMixedBuffer(Buffer::Tag tag);
|
||||
void ReleaseAndQueueBuffers();
|
||||
u32 GetSampleRate() const;
|
||||
u32 GetSampleCount() const;
|
||||
u32 GetMixBufferCount() const;
|
||||
Stream::State GetStreamState() const;
|
||||
|
||||
private:
|
||||
class EffectState;
|
||||
class VoiceState;
|
||||
class VoiceState {
|
||||
public:
|
||||
bool IsPlaying() const {
|
||||
return is_in_use && info.play_state == PlayState::Started;
|
||||
}
|
||||
|
||||
const VoiceOutStatus& GetOutStatus() const {
|
||||
return out_status;
|
||||
}
|
||||
|
||||
const VoiceInfo& GetInfo() const {
|
||||
return info;
|
||||
}
|
||||
|
||||
VoiceInfo& Info() {
|
||||
return info;
|
||||
}
|
||||
|
||||
void SetWaveIndex(size_t index);
|
||||
std::vector<s16> DequeueSamples(size_t sample_count);
|
||||
void UpdateState();
|
||||
void RefreshBuffer();
|
||||
|
||||
private:
|
||||
bool is_in_use{};
|
||||
bool is_refresh_pending{};
|
||||
size_t wave_index{};
|
||||
size_t offset{};
|
||||
Codec::ADPCMState adpcm_state{};
|
||||
std::vector<s16> samples;
|
||||
VoiceOutStatus out_status{};
|
||||
VoiceInfo info{};
|
||||
};
|
||||
|
||||
AudioRendererParameter worker_params;
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
std::vector<VoiceState> voices;
|
||||
std::vector<EffectState> effects;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
std::unique_ptr<AudioCore::AudioOut> audio_core;
|
||||
AudioCore::StreamPtr stream;
|
||||
};
|
||||
|
||||
|
||||
+10
-10
@@ -8,27 +8,27 @@
|
||||
|
||||
namespace AudioCore::Codec {
|
||||
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff,
|
||||
ADPCMState& state) {
|
||||
// GC-ADPCM with scale factor and variable coefficients.
|
||||
// Frames are 8 bytes long containing 14 samples each.
|
||||
// Samples are 4 bits (one nibble) long.
|
||||
|
||||
constexpr std::size_t FRAME_LEN = 8;
|
||||
constexpr std::size_t SAMPLES_PER_FRAME = 14;
|
||||
constexpr size_t FRAME_LEN = 8;
|
||||
constexpr size_t SAMPLES_PER_FRAME = 14;
|
||||
constexpr std::array<int, 16> SIGNED_NIBBLES = {
|
||||
{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 ret_size =
|
||||
const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
|
||||
const size_t ret_size =
|
||||
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
|
||||
std::vector<s16> ret(ret_size);
|
||||
|
||||
int yn1 = state.yn1, yn2 = state.yn2;
|
||||
|
||||
const std::size_t NUM_FRAMES =
|
||||
const size_t NUM_FRAMES =
|
||||
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
|
||||
for (std::size_t framei = 0; framei < NUM_FRAMES; framei++) {
|
||||
for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
|
||||
const int frame_header = data[framei * FRAME_LEN];
|
||||
const int scale = 1 << (frame_header & 0xF);
|
||||
const int idx = (frame_header >> 4) & 0x7;
|
||||
@@ -53,9 +53,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM
|
||||
return static_cast<s16>(val);
|
||||
};
|
||||
|
||||
std::size_t outputi = framei * SAMPLES_PER_FRAME;
|
||||
std::size_t datai = framei * FRAME_LEN + 1;
|
||||
for (std::size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
|
||||
size_t outputi = framei * SAMPLES_PER_FRAME;
|
||||
size_t datai = framei * FRAME_LEN + 1;
|
||||
for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
|
||||
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
|
||||
ret[outputi] = sample1;
|
||||
outputi++;
|
||||
|
||||
@@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>;
|
||||
* @param state ADPCM state, this is updated with new state
|
||||
* @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* const data, size_t size, const ADPCM_Coeff& coeff,
|
||||
ADPCMState& state);
|
||||
|
||||
}; // namespace AudioCore::Codec
|
||||
|
||||
@@ -3,23 +3,26 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
|
||||
#include "audio_core/cubeb_sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/ring_buffer.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class CubebSinkStream final : public SinkStream {
|
||||
class SinkStreamImpl final : public SinkStream {
|
||||
public:
|
||||
CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
const std::string& name)
|
||||
: ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate,
|
||||
num_channels} {
|
||||
SinkStreamImpl(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
const std::string& name)
|
||||
: ctx{ctx}, num_channels{num_channels_} {
|
||||
|
||||
if (num_channels == 6) {
|
||||
// 6-channel audio does not seem to work with cubeb + SDL, so we downsample this to 2
|
||||
// channel for now
|
||||
is_6_channel = true;
|
||||
num_channels = 2;
|
||||
}
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = sample_rate;
|
||||
@@ -34,7 +37,7 @@ public:
|
||||
|
||||
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
|
||||
¶ms, std::max(512u, minimum_latency),
|
||||
&CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback,
|
||||
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
|
||||
this) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
|
||||
return;
|
||||
@@ -46,7 +49,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~CubebSinkStream() {
|
||||
~SinkStreamImpl() {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
@@ -58,32 +61,25 @@ public:
|
||||
cubeb_stream_destroy(stream_backend);
|
||||
}
|
||||
|
||||
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
|
||||
if (source_num_channels > num_channels) {
|
||||
// Downsample 6 channels to 2
|
||||
std::vector<s16> buf;
|
||||
buf.reserve(samples.size() * num_channels / source_num_channels);
|
||||
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
|
||||
for (std::size_t ch = 0; ch < num_channels; ch++) {
|
||||
buf.push_back(samples[i + ch]);
|
||||
}
|
||||
}
|
||||
queue.Push(buf);
|
||||
void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) override {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
queue.Push(samples);
|
||||
}
|
||||
queue.reserve(queue.size() + samples.size() * GetNumChannels());
|
||||
|
||||
std::size_t SamplesInQueue(u32 num_channels) const override {
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
return queue.Size() / num_channels;
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
should_flush = true;
|
||||
if (is_6_channel) {
|
||||
// Downsample 6 channels to 2
|
||||
const size_t sample_count_copy_size = samples.size() * 2;
|
||||
queue.reserve(sample_count_copy_size);
|
||||
for (size_t i = 0; i < samples.size(); i += num_channels) {
|
||||
queue.push_back(samples[i]);
|
||||
queue.push_back(samples[i + 1]);
|
||||
}
|
||||
} else {
|
||||
// Copy as-is
|
||||
std::copy(samples.begin(), samples.end(), std::back_inserter(queue));
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetNumChannels() const {
|
||||
@@ -96,11 +92,9 @@ private:
|
||||
cubeb* ctx{};
|
||||
cubeb_stream* stream_backend{};
|
||||
u32 num_channels{};
|
||||
bool is_6_channel{};
|
||||
|
||||
Common::RingBuffer<s16, 0x10000> queue;
|
||||
std::array<s16, 2> last_frame;
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
std::vector<s16> queue;
|
||||
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames);
|
||||
@@ -119,11 +113,10 @@ CubebSink::CubebSink(std::string target_device_name) {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||
} else {
|
||||
const auto collection_end{collection.device + collection.count};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
return info.friendly_name != nullptr &&
|
||||
target_device_name == info.friendly_name;
|
||||
})};
|
||||
const auto device{std::find_if(collection.device, collection_end,
|
||||
[&](const cubeb_device_info& device) {
|
||||
return target_device_name == device.friendly_name;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
}
|
||||
@@ -147,59 +140,42 @@ CubebSink::~CubebSink() {
|
||||
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) {
|
||||
sink_streams.push_back(
|
||||
std::make_unique<CubebSinkStream>(ctx, sample_rate, num_channels, output_device, name));
|
||||
std::make_unique<SinkStreamImpl>(ctx, sample_rate, num_channels, output_device, name));
|
||||
return *sink_streams.back();
|
||||
}
|
||||
|
||||
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
|
||||
long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data);
|
||||
u8* buffer = reinterpret_cast<u8*>(output_buffer);
|
||||
|
||||
if (!impl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::size_t num_channels = impl->GetNumChannels();
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
const size_t frames_to_write{
|
||||
std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};
|
||||
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
const std::size_t out_frames =
|
||||
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
|
||||
samples_written = out_frames * num_channels;
|
||||
memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels());
|
||||
impl->queue.erase(impl->queue.begin(),
|
||||
impl->queue.begin() + frames_to_write * impl->GetNumChannels());
|
||||
|
||||
if (impl->should_flush) {
|
||||
impl->time_stretch.Flush();
|
||||
impl->should_flush = false;
|
||||
}
|
||||
} else {
|
||||
samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
}
|
||||
|
||||
if (samples_written >= num_channels) {
|
||||
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
|
||||
num_channels * sizeof(s16));
|
||||
}
|
||||
|
||||
// Fill the rest of the frames with last_frame
|
||||
for (std::size_t i = samples_written; i < samples_to_write; i += num_channels) {
|
||||
std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16));
|
||||
if (frames_to_write < num_frames) {
|
||||
// Fill the rest of the frames with silence
|
||||
memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0,
|
||||
(num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels());
|
||||
}
|
||||
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
||||
void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
||||
|
||||
std::vector<std::string> ListCubebSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return {};
|
||||
}
|
||||
@@ -208,7 +184,7 @@ std::vector<std::string> ListCubebSinkDevices() {
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||
} else {
|
||||
for (std::size_t i = 0; i < collection.count; i++) {
|
||||
for (size_t i = 0; i < collection.count; i++) {
|
||||
const cubeb_device_info& device = collection.device[i];
|
||||
if (device.friendly_name) {
|
||||
device_list.emplace_back(device.friendly_name);
|
||||
|
||||
@@ -21,12 +21,6 @@ public:
|
||||
private:
|
||||
struct NullSinkStreamImpl final : SinkStream {
|
||||
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
|
||||
|
||||
std::size_t SamplesInQueue(u32 /*num_channels*/) const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Flush() override {}
|
||||
} null_sink_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const std::vector<SinkDetails> g_sink_details = {
|
||||
[] { return std::vector<std::string>{"null"}; }},
|
||||
};
|
||||
|
||||
const SinkDetails& GetSinkDetails(std::string_view sink_id) {
|
||||
const SinkDetails& GetSinkDetails(std::string sink_id) {
|
||||
auto iter =
|
||||
std::find_if(g_sink_details.begin(), g_sink_details.end(),
|
||||
[sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -32,6 +30,6 @@ struct SinkDetails {
|
||||
|
||||
extern const std::vector<SinkDetails> g_sink_details;
|
||||
|
||||
const SinkDetails& GetSinkDetails(std::string_view sink_id);
|
||||
const SinkDetails& GetSinkDetails(std::string sink_id);
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -25,10 +25,6 @@ public:
|
||||
* @param samples Samples in interleaved stereo PCM16 format.
|
||||
*/
|
||||
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
|
||||
|
||||
virtual std::size_t SamplesInQueue(u32 num_channels) const = 0;
|
||||
|
||||
virtual void Flush() = 0;
|
||||
};
|
||||
|
||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
@@ -17,7 +16,7 @@
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr std::size_t MaxAudioBufferCount{32};
|
||||
constexpr size_t MaxAudioBufferCount{32};
|
||||
|
||||
u32 Stream::GetNumChannels() const {
|
||||
switch (format) {
|
||||
@@ -48,16 +47,11 @@ void Stream::Play() {
|
||||
}
|
||||
|
||||
void Stream::Stop() {
|
||||
state = State::Stopped;
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
}
|
||||
|
||||
@@ -78,7 +72,6 @@ static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
void Stream::PlayNextBuffer() {
|
||||
if (!IsPlaying()) {
|
||||
// Ensure we are in playing state before playing the next buffer
|
||||
sink_stream.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -89,7 +82,6 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
if (queued_buffers.empty()) {
|
||||
// No queued buffers - we are effectively paused
|
||||
sink_stream.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,7 +89,6 @@ void Stream::PlayNextBuffer() {
|
||||
queued_buffers.pop();
|
||||
|
||||
VolumeAdjustSamples(active_buffer->Samples());
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
@@ -124,9 +115,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
|
||||
std::vector<Buffer::Tag> tags;
|
||||
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
released_buffers.pop();
|
||||
}
|
||||
|
||||
+11
-17
@@ -11,16 +11,13 @@
|
||||
#include <queue>
|
||||
|
||||
#include "audio_core/buffer.h"
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
#include "core/core_timing.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SinkStream;
|
||||
|
||||
/**
|
||||
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
|
||||
*/
|
||||
@@ -33,12 +30,6 @@ public:
|
||||
Multi51Channel16,
|
||||
};
|
||||
|
||||
/// Current state of the stream
|
||||
enum class State {
|
||||
Stopped,
|
||||
Playing,
|
||||
};
|
||||
|
||||
/// Callback function type, used to change guest state on a buffer being released
|
||||
using ReleaseCallback = std::function<void()>;
|
||||
|
||||
@@ -58,7 +49,7 @@ public:
|
||||
bool ContainsBuffer(Buffer::Tag tag) const;
|
||||
|
||||
/// Returns a vector of recently released buffers specified by tag
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
|
||||
|
||||
/// Returns true if the stream is currently playing
|
||||
bool IsPlaying() const {
|
||||
@@ -66,7 +57,7 @@ public:
|
||||
}
|
||||
|
||||
/// Returns the number of queued buffers
|
||||
std::size_t GetQueueSize() const {
|
||||
size_t GetQueueSize() const {
|
||||
return queued_buffers.size();
|
||||
}
|
||||
|
||||
@@ -78,10 +69,13 @@ public:
|
||||
/// Gets the number of channels
|
||||
u32 GetNumChannels() const;
|
||||
|
||||
/// Get the state
|
||||
State GetState() const;
|
||||
|
||||
private:
|
||||
/// Current state of the stream
|
||||
enum class State {
|
||||
Stopped,
|
||||
Playing,
|
||||
};
|
||||
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
void PlayNextBuffer();
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
|
||||
m_sound_touch.setChannels(channel_count);
|
||||
m_sound_touch.setSampleRate(sample_rate);
|
||||
m_sound_touch.setPitch(1.0);
|
||||
m_sound_touch.setTempo(1.0);
|
||||
}
|
||||
|
||||
void TimeStretcher::Clear() {
|
||||
m_sound_touch.clear();
|
||||
}
|
||||
|
||||
void TimeStretcher::Flush() {
|
||||
m_sound_touch.flush();
|
||||
}
|
||||
|
||||
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
std::size_t num_out) {
|
||||
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
|
||||
|
||||
// We were given actual_samples number of samples, and num_samples were requested from us.
|
||||
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
|
||||
|
||||
const double max_latency = 0.25; // seconds
|
||||
const double max_backlog = m_sample_rate * max_latency;
|
||||
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
|
||||
if (backlog_fullness > 4.0) {
|
||||
// Too many samples in backlog: Don't push anymore on
|
||||
num_in = 0;
|
||||
}
|
||||
|
||||
// We ideally want the backlog to be about 50% full.
|
||||
// This gives some headroom both ways to prevent underflow and overflow.
|
||||
// We tweak current_ratio to encourage this.
|
||||
constexpr double tweak_time_scale = 0.05; // seconds
|
||||
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
|
||||
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
|
||||
|
||||
// This low-pass filter smoothes out variance in the calculated stretch ratio.
|
||||
// The time-scale determines how responsive this filter is.
|
||||
constexpr double lpf_time_scale = 0.712; // seconds
|
||||
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
|
||||
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
|
||||
|
||||
// Place a lower limit of 5% speed. When a game boots up, there will be
|
||||
// many silence samples. These do not need to be timestretched.
|
||||
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
|
||||
m_sound_touch.setTempo(m_stretch_ratio);
|
||||
|
||||
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
|
||||
backlog_fullness);
|
||||
|
||||
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
|
||||
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <SoundTouch.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class TimeStretcher {
|
||||
public:
|
||||
TimeStretcher(u32 sample_rate, u32 channel_count);
|
||||
|
||||
/// @param in Input sample buffer
|
||||
/// @param num_in Number of input frames in `in`
|
||||
/// @param out Output sample buffer
|
||||
/// @param num_out Desired number of output frames in `out`
|
||||
/// @returns Actual number of frames written to `out`
|
||||
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
u32 m_sample_rate;
|
||||
soundtouch::SoundTouch m_sound_touch;
|
||||
double m_stretch_ratio = 1.0;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,16 +1,13 @@
|
||||
# Generate cpp with Git revision from template
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Bleeding Edge) to the scm_rev file as well
|
||||
set(REPO_NAME "")
|
||||
set(BUILD_VERSION "0")
|
||||
if ($ENV{CI})
|
||||
if ($ENV{TRAVIS})
|
||||
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
|
||||
set(BUILD_TAG $ENV{TRAVIS_TAG})
|
||||
elseif($ENV{APPVEYOR})
|
||||
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
|
||||
endif()
|
||||
# regex capture the string nightly or canary into CMAKE_MATCH_1
|
||||
# regex capture the string nightly or bleeding-edge into CMAKE_MATCH_1
|
||||
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
# capitalize the first letter of each word in the repo name.
|
||||
@@ -19,21 +16,10 @@ if ($ENV{CI})
|
||||
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
|
||||
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
|
||||
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
|
||||
# this leaves a trailing space on the last word, but we actually want that
|
||||
# because of how it's styled in the title bar.
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ")
|
||||
endforeach()
|
||||
if (BUILD_TAG)
|
||||
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
set(BUILD_VERSION ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
if (BUILD_VERSION)
|
||||
# This leaves a trailing space on the last word, but we actually want that
|
||||
# because of how it's styled in the title bar.
|
||||
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
|
||||
else()
|
||||
set(BUILD_FULLNAME "")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
|
||||
@@ -41,10 +27,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
|
||||
add_library(common STATIC
|
||||
alignment.h
|
||||
assert.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
break_points.cpp
|
||||
break_points.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
@@ -54,8 +40,6 @@ add_library(common STATIC
|
||||
file_util.cpp
|
||||
file_util.h
|
||||
hash.h
|
||||
hex_util.cpp
|
||||
hex_util.h
|
||||
logging/backend.cpp
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
@@ -64,6 +48,8 @@ add_library(common STATIC
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_util.cpp
|
||||
memory_util.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
@@ -71,7 +57,6 @@ add_library(common STATIC
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
quaternion.h
|
||||
ring_buffer.h
|
||||
scm_rev.cpp
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
@@ -87,7 +72,6 @@ add_library(common STATIC
|
||||
timer.cpp
|
||||
timer.h
|
||||
vector_math.h
|
||||
web_result.h
|
||||
)
|
||||
|
||||
if(ARCHITECTURE_x86_64)
|
||||
|
||||
+2
-14
@@ -8,27 +8,15 @@
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, std::size_t size) {
|
||||
constexpr T AlignUp(T value, size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignDown(T value, std::size_t size) {
|
||||
constexpr T AlignDown(T value, size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool Is4KBAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return (value & 0xFFF) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool IsWordAligned(T value) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return (value & 0b11) == 0;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -129,8 +129,8 @@ private:
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
static constexpr size_t position = Position;
|
||||
static constexpr size_t bits = Bits;
|
||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
|
||||
/**
|
||||
@@ -178,7 +178,8 @@ public:
|
||||
return ExtractValue(storage);
|
||||
}
|
||||
|
||||
constexpr explicit operator bool() const {
|
||||
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
|
||||
constexpr FORCE_INLINE bool ToBool() const {
|
||||
return Value() != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,14 +170,14 @@ public:
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
|
||||
static BitSet AllTrue(std::size_t count) {
|
||||
static BitSet AllTrue(size_t count) {
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](std::size_t bit) {
|
||||
Ref operator[](size_t bit) {
|
||||
return Ref(this, (IntTy)1 << bit);
|
||||
}
|
||||
const Ref operator[](std::size_t bit) const {
|
||||
const Ref operator[](size_t bit) const {
|
||||
return (*const_cast<BitSet*>(this))[bit];
|
||||
}
|
||||
bool operator==(BitSet other) const {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include "common/break_points.h"
|
||||
|
||||
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
|
||||
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
|
||||
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
|
||||
return it != m_BreakPoints.end();
|
||||
}
|
||||
|
||||
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
|
||||
auto cond = [&iAddress](const TBreakPoint& bp) {
|
||||
return bp.iAddress == iAddress && bp.bTemporary;
|
||||
};
|
||||
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
|
||||
return it != m_BreakPoints.end();
|
||||
}
|
||||
|
||||
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
|
||||
TBreakPointsStr bps;
|
||||
for (auto breakpoint : m_BreakPoints) {
|
||||
if (!breakpoint.bTemporary) {
|
||||
std::stringstream bp;
|
||||
bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
|
||||
bps.push_back(bp.str());
|
||||
}
|
||||
}
|
||||
|
||||
return bps;
|
||||
}
|
||||
|
||||
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
|
||||
for (auto bps_item : bps) {
|
||||
TBreakPoint bp;
|
||||
std::stringstream bpstr;
|
||||
bpstr << std::hex << bps_item;
|
||||
bpstr >> bp.iAddress;
|
||||
bp.bOn = bps_item.find("n") != bps_item.npos;
|
||||
bp.bTemporary = false;
|
||||
Add(bp);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(const TBreakPoint& bp) {
|
||||
if (!IsAddressBreakPoint(bp.iAddress)) {
|
||||
m_BreakPoints.push_back(bp);
|
||||
// if (jit)
|
||||
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(u32 em_address, bool temp) {
|
||||
if (!IsAddressBreakPoint(em_address)) // only add new addresses
|
||||
{
|
||||
TBreakPoint pt; // breakpoint settings
|
||||
pt.bOn = true;
|
||||
pt.bTemporary = temp;
|
||||
pt.iAddress = em_address;
|
||||
|
||||
m_BreakPoints.push_back(pt);
|
||||
|
||||
// if (jit)
|
||||
// jit->GetBlockCache()->InvalidateICache(em_address, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Remove(u32 em_address) {
|
||||
auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
|
||||
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
|
||||
if (it != m_BreakPoints.end())
|
||||
m_BreakPoints.erase(it);
|
||||
}
|
||||
|
||||
void BreakPoints::Clear() {
|
||||
// if (jit)
|
||||
//{
|
||||
// std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
|
||||
// [](const TBreakPoint& bp)
|
||||
// {
|
||||
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
|
||||
// }
|
||||
// );
|
||||
//}
|
||||
|
||||
m_BreakPoints.clear();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
struct TBreakPoint {
|
||||
u32 iAddress;
|
||||
bool bOn;
|
||||
bool bTemporary;
|
||||
};
|
||||
|
||||
// Code breakpoints.
|
||||
class BreakPoints {
|
||||
public:
|
||||
typedef std::vector<TBreakPoint> TBreakPoints;
|
||||
typedef std::vector<std::string> TBreakPointsStr;
|
||||
|
||||
const TBreakPoints& GetBreakPoints() {
|
||||
return m_BreakPoints;
|
||||
}
|
||||
|
||||
TBreakPointsStr GetStrings() const;
|
||||
void AddFromStrings(const TBreakPointsStr& bps);
|
||||
|
||||
// is address breakpoint
|
||||
bool IsAddressBreakPoint(u32 iAddress) const;
|
||||
bool IsTempBreakPoint(u32 iAddress) const;
|
||||
|
||||
// Add BreakPoint
|
||||
void Add(u32 em_address, bool temp = false);
|
||||
void Add(const TBreakPoint& bp);
|
||||
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 iAddress);
|
||||
void Clear();
|
||||
|
||||
void DeleteByAddress(u32 Address);
|
||||
|
||||
private:
|
||||
TBreakPoints m_BreakPoints;
|
||||
u32 m_iBreakOnCount;
|
||||
};
|
||||
+11
-11
@@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
static uint64 HashLen0to16(const char* s, size_t len) {
|
||||
if (len >= 8) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) + k2;
|
||||
@@ -141,7 +141,7 @@ static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
|
||||
// This probably works well for 16-byte strings as well, but it may be overkill
|
||||
// in that case.
|
||||
static uint64 HashLen17to32(const char* s, std::size_t len) {
|
||||
static uint64 HashLen17to32(const char* s, size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k1;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
@@ -170,7 +170,7 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint
|
||||
}
|
||||
|
||||
// Return an 8-byte hash for 33 to 64 bytes.
|
||||
static uint64 HashLen33to64(const char* s, std::size_t len) {
|
||||
static uint64 HashLen33to64(const char* s, size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k2;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
@@ -191,7 +191,7 @@ static uint64 HashLen33to64(const char* s, std::size_t len) {
|
||||
return b + x;
|
||||
}
|
||||
|
||||
uint64 CityHash64(const char* s, std::size_t len) {
|
||||
uint64 CityHash64(const char* s, size_t len) {
|
||||
if (len <= 32) {
|
||||
if (len <= 16) {
|
||||
return HashLen0to16(s, len);
|
||||
@@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, std::size_t len) {
|
||||
x = x * k1 + Fetch64(s);
|
||||
|
||||
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
||||
len = (len - 1) & ~static_cast<std::size_t>(63);
|
||||
len = (len - 1) & ~static_cast<size_t>(63);
|
||||
do {
|
||||
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
|
||||
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||
@@ -229,17 +229,17 @@ uint64 CityHash64(const char* s, std::size_t len) {
|
||||
HashLen16(v.second, w.second) + x);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
|
||||
uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) {
|
||||
return CityHash64WithSeeds(s, len, k2, seed);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
|
||||
uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) {
|
||||
return HashLen16(CityHash64(s, len) - seed0, seed1);
|
||||
}
|
||||
|
||||
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
|
||||
// of any length representable in signed long. Based on City and Murmur.
|
||||
static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
|
||||
uint64 a = Uint128Low64(seed);
|
||||
uint64 b = Uint128High64(seed);
|
||||
uint64 c = 0;
|
||||
@@ -269,7 +269,7 @@ static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
return uint128(a ^ b, HashLen16(b, a));
|
||||
}
|
||||
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
|
||||
if (len < 128) {
|
||||
return CityMurmur(s, len, seed);
|
||||
}
|
||||
@@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
w.first *= 9;
|
||||
v.first *= k0;
|
||||
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
|
||||
for (std::size_t tail_done = 0; tail_done < len;) {
|
||||
for (size_t tail_done = 0; tail_done < len;) {
|
||||
tail_done += 32;
|
||||
y = Rotate(x + y, 42) * k0 + v.second;
|
||||
w.first += Fetch64(s + len - tail_done + 16);
|
||||
@@ -331,7 +331,7 @@ uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
|
||||
}
|
||||
|
||||
uint128 CityHash128(const char* s, std::size_t len) {
|
||||
uint128 CityHash128(const char* s, size_t len) {
|
||||
return len >= 16
|
||||
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
|
||||
: CityHash128WithSeed(s, len, uint128(k0, k1));
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
#include <utility>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> // for std::size_t.
|
||||
#include <stdlib.h> // for size_t.
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -77,22 +77,22 @@ inline uint64_t Uint128High64(const uint128& x) {
|
||||
}
|
||||
|
||||
// Hash function for a byte array.
|
||||
uint64_t CityHash64(const char* buf, std::size_t len);
|
||||
uint64_t CityHash64(const char* buf, size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||
// hashed into the result.
|
||||
uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
|
||||
uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed);
|
||||
|
||||
// Hash function for a byte array. For convenience, two seeds are also
|
||||
// hashed into the result.
|
||||
uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
|
||||
uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1);
|
||||
|
||||
// Hash function for a byte array.
|
||||
uint128 CityHash128(const char* s, std::size_t len);
|
||||
uint128 CityHash128(const char* s, size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||
// hashed into the result.
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
|
||||
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed);
|
||||
|
||||
// Hash 128 input bits down to 64 bits of output.
|
||||
// This is intended to be a reasonably good hash function.
|
||||
|
||||
@@ -33,8 +33,6 @@
|
||||
#define NAND_DIR "nand"
|
||||
#define SYSDATA_DIR "sysdata"
|
||||
#define KEYS_DIR "keys"
|
||||
#define LOAD_DIR "load"
|
||||
#define DUMP_DIR "dump"
|
||||
#define LOG_DIR "log"
|
||||
|
||||
// Filenames
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/detached_tasks.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
DetachedTasks* DetachedTasks::instance = nullptr;
|
||||
|
||||
DetachedTasks::DetachedTasks() {
|
||||
ASSERT(instance == nullptr);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
void DetachedTasks::WaitForAllTasks() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [this]() { return count == 0; });
|
||||
}
|
||||
|
||||
DetachedTasks::~DetachedTasks() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
ASSERT(count == 0);
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
void DetachedTasks::AddTask(std::function<void()> task) {
|
||||
std::unique_lock<std::mutex> lock(instance->mutex);
|
||||
++instance->count;
|
||||
std::thread([task{std::move(task)}]() {
|
||||
task();
|
||||
std::unique_lock<std::mutex> lock(instance->mutex);
|
||||
--instance->count;
|
||||
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* A background manager which ensures that all detached task is finished before program exits.
|
||||
*
|
||||
* Some tasks, telemetry submission for example, prefer executing asynchronously and don't care
|
||||
* about the result. These tasks are suitable for std::thread::detach(). However, this is unsafe if
|
||||
* the task is launched just before the program exits (which is a common case for telemetry), so we
|
||||
* need to block on these tasks on program exit.
|
||||
*
|
||||
* To make detached task safe, a single DetachedTasks object should be placed in the main(), and
|
||||
* call WaitForAllTasks() after all program execution but before global/static variable destruction.
|
||||
* Any potentially unsafe detached task should be executed via DetachedTasks::AddTask.
|
||||
*/
|
||||
class DetachedTasks {
|
||||
public:
|
||||
DetachedTasks();
|
||||
~DetachedTasks();
|
||||
void WaitForAllTasks();
|
||||
|
||||
static void AddTask(std::function<void()> task);
|
||||
|
||||
private:
|
||||
static DetachedTasks* instance;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
+17
-38
@@ -15,24 +15,21 @@
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// windows.h needs to be included before other windows headers
|
||||
#include <direct.h> // getcwd
|
||||
#include <commdlg.h> // for GetSaveFileName
|
||||
#include <direct.h> // getcwd
|
||||
#include <io.h>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h> // for SHGetFolderPath
|
||||
#include <tchar.h>
|
||||
#include "common/string_util.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// 64 bit offsets for MSVC
|
||||
// 64 bit offsets for windows
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#define fileno _fileno
|
||||
#endif
|
||||
|
||||
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
|
||||
#define atoll _atoi64
|
||||
#define stat _stat64
|
||||
#define fstat _fstat64
|
||||
|
||||
#define fileno _fileno
|
||||
#else
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h>
|
||||
@@ -79,7 +76,7 @@ namespace FileUtil {
|
||||
// Modifies argument.
|
||||
static void StripTailDirSlashes(std::string& fname) {
|
||||
if (fname.length() > 1) {
|
||||
std::size_t i = fname.length();
|
||||
size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
|
||||
--i;
|
||||
fname.resize(i);
|
||||
@@ -204,7 +201,7 @@ bool CreateFullPath(const std::string& fullPath) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::size_t position = 0;
|
||||
size_t position = 0;
|
||||
while (true) {
|
||||
// Find next sub path
|
||||
position = fullPath.find(DIR_SEP_CHR, position);
|
||||
@@ -302,7 +299,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
std::array<char, 1024> buffer;
|
||||
while (!feof(input.get())) {
|
||||
// read input
|
||||
std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
|
||||
size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
|
||||
if (rnum != buffer.size()) {
|
||||
if (ferror(input.get()) != 0) {
|
||||
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
|
||||
@@ -312,7 +309,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
}
|
||||
|
||||
// write output
|
||||
std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
|
||||
size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
|
||||
if (wnum != rnum) {
|
||||
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
|
||||
destFilename, GetLastErrorMsg());
|
||||
@@ -708,8 +705,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
#endif
|
||||
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
|
||||
// TODO: Put the logs in a better location for each OS
|
||||
@@ -755,21 +750,15 @@ std::string GetHactoolConfigurationPath() {
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string GetNANDRegistrationDir(bool system) {
|
||||
if (system)
|
||||
return GetUserPath(UserPath::NANDDir) + "system/Contents/registered/";
|
||||
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
|
||||
}
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
||||
}
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
IOFile file(filename, text_file ? "r" : "rb");
|
||||
|
||||
if (!file.IsOpen())
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
str.resize(static_cast<u32>(file.GetSize()));
|
||||
return file.ReadArray(&str[0], str.size());
|
||||
@@ -834,7 +823,7 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
|
||||
std::string_view GetParentPath(std::string_view path) {
|
||||
const auto name_bck_index = path.rfind('\\');
|
||||
const auto name_fwd_index = path.rfind('/');
|
||||
std::size_t name_index;
|
||||
size_t name_index;
|
||||
|
||||
if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
|
||||
name_index = std::min(name_bck_index, name_fwd_index);
|
||||
@@ -873,7 +862,7 @@ std::string_view GetFilename(std::string_view path) {
|
||||
}
|
||||
|
||||
std::string_view GetExtensionFromFilename(std::string_view name) {
|
||||
const std::size_t index = name.rfind('.');
|
||||
const size_t index = name.rfind('.');
|
||||
|
||||
if (index == std::string_view::npos) {
|
||||
return {};
|
||||
@@ -895,21 +884,11 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
|
||||
std::string SanitizePath(std::string_view path_) {
|
||||
std::string path(path_);
|
||||
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
|
||||
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
|
||||
|
||||
if (directory_separator == DirectorySeparator::PlatformDefault) {
|
||||
#ifdef _WIN32
|
||||
type1 = '/';
|
||||
type2 = '\\';
|
||||
#endif
|
||||
}
|
||||
|
||||
std::replace(path.begin(), path.end(), type1, type2);
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
path.erase(std::unique(path.begin(), path.end(),
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
|
||||
path.end());
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
+15
-24
@@ -29,8 +29,6 @@ enum class UserPath {
|
||||
NANDDir,
|
||||
RootDir,
|
||||
SDMCDir,
|
||||
LoadDir,
|
||||
DumpDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
@@ -131,8 +129,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
|
||||
|
||||
std::string GetHactoolConfigurationPath();
|
||||
|
||||
std::string GetNANDRegistrationDir(bool system = false);
|
||||
|
||||
// Returns the path to where the sys file are
|
||||
std::string GetSysDirectory();
|
||||
|
||||
@@ -145,9 +141,8 @@ const std::string& GetExeDirectory();
|
||||
std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
|
||||
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
|
||||
size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
@@ -180,19 +175,15 @@ std::string_view RemoveTrailingSlash(std::string_view path);
|
||||
|
||||
// Creates a new vector containing indices [first, last) from the original.
|
||||
template <typename T>
|
||||
std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
|
||||
std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) {
|
||||
if (first >= last)
|
||||
return {};
|
||||
last = std::min<std::size_t>(last, vector.size());
|
||||
last = std::min<size_t>(last, vector.size());
|
||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||
}
|
||||
|
||||
enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
std::string SanitizePath(std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
|
||||
std::string SanitizePath(std::string_view path);
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
@@ -216,47 +207,47 @@ public:
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
std::size_t ReadArray(T* data, std::size_t length) const {
|
||||
size_t ReadArray(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
return std::fread(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t WriteArray(const T* data, std::size_t length) {
|
||||
size_t WriteArray(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
return std::fwrite(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t ReadBytes(T* data, std::size_t length) const {
|
||||
size_t ReadBytes(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t WriteBytes(const T* data, std::size_t length) {
|
||||
size_t WriteBytes(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t WriteObject(const T& object) {
|
||||
size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
std::size_t WriteString(const std::string& str) {
|
||||
size_t WriteString(const std::string& str) {
|
||||
return WriteArray(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
@@ -285,7 +276,7 @@ private:
|
||||
template <typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
|
||||
#ifdef _MSC_VER
|
||||
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
|
||||
fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
|
||||
#else
|
||||
fstream.open(filename.c_str(), openmode);
|
||||
#endif
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ namespace Common {
|
||||
* @param len Length of data (in bytes) to compute hash over
|
||||
* @returns 64-bit hash value that was computed over the data block
|
||||
*/
|
||||
static inline u64 ComputeHash64(const void* data, std::size_t len) {
|
||||
static inline u64 ComputeHash64(const void* data, size_t len) {
|
||||
return CityHash64(static_cast<const char*>(data), len);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ struct HashableStruct {
|
||||
return !(*this == o);
|
||||
};
|
||||
|
||||
std::size_t Hash() const {
|
||||
size_t Hash() const {
|
||||
return Common::ComputeStructHash64(state);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
u8 ToHexNibble(char c1) {
|
||||
if (c1 >= 65 && c1 <= 70)
|
||||
return c1 - 55;
|
||||
if (c1 >= 97 && c1 <= 102)
|
||||
return c1 - 87;
|
||||
if (c1 >= 48 && c1 <= 57)
|
||||
return c1 - 48;
|
||||
LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
|
||||
std::vector<u8> out(str.size() / 2);
|
||||
if (little_endian) {
|
||||
for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
} else {
|
||||
for (std::size_t i = 0; i < str.size(); i += 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
|
||||
std::string out;
|
||||
for (u8 c : vector)
|
||||
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
|
||||
if (len != 32) {
|
||||
LOG_ERROR(Common,
|
||||
"Attempting to parse string to array that is not of correct size (expected=32, "
|
||||
"actual={}).",
|
||||
len);
|
||||
return {};
|
||||
}
|
||||
return HexStringToArray<16>(str);
|
||||
}
|
||||
|
||||
std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
|
||||
if (len != 64) {
|
||||
LOG_ERROR(Common,
|
||||
"Attempting to parse string to array that is not of correct size (expected=64, "
|
||||
"actual={}).",
|
||||
len);
|
||||
return {};
|
||||
}
|
||||
return HexStringToArray<32>(str);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,46 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
u8 ToHexNibble(char c1);
|
||||
|
||||
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
|
||||
|
||||
template <std::size_t Size, bool le = false>
|
||||
std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
std::array<u8, Size> out{};
|
||||
if constexpr (le) {
|
||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
} else {
|
||||
for (std::size_t i = 0; i < 2 * Size; i += 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
|
||||
|
||||
template <std::size_t Size>
|
||||
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
|
||||
std::string out;
|
||||
for (u8 c : array)
|
||||
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
|
||||
std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
|
||||
|
||||
} // namespace Common
|
||||
@@ -12,8 +12,7 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#include <windows.h> // For OutputDebugStringA
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#else
|
||||
#define _SH_DENYWR 0
|
||||
#endif
|
||||
@@ -136,22 +135,16 @@ FileBackend::FileBackend(const std::string& filename)
|
||||
void FileBackend::Write(const Entry& entry) {
|
||||
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
||||
// know)
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
||||
constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
||||
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
|
||||
return;
|
||||
}
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
|
||||
if (entry.log_level >= Level::Error) {
|
||||
file.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerBackend::Write(const Entry& entry) {
|
||||
#ifdef _WIN32
|
||||
::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
@@ -178,22 +171,15 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
SUB(Service, EUPLD) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, FGM) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, IRS) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
@@ -202,22 +188,17 @@ void DebuggerBackend::Write(const Entry& entry) {
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NPNS) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, PCV) \
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, PSM) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, TCAP) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
@@ -313,14 +294,13 @@ Backend* GetBackend(std::string_view backend_name) {
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args) {
|
||||
auto& instance = Impl::Instance();
|
||||
const auto& filter = instance.GetGlobalFilter();
|
||||
auto filter = Impl::Instance().GetGlobalFilter();
|
||||
if (!filter.CheckMessage(log_class, log_level))
|
||||
return;
|
||||
|
||||
Entry entry =
|
||||
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
|
||||
|
||||
instance.PushEntry(std::move(entry));
|
||||
Impl::Instance().PushEntry(std::move(entry));
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -100,21 +100,7 @@ public:
|
||||
|
||||
private:
|
||||
FileUtil::IOFile file;
|
||||
std::size_t bytes_written;
|
||||
};
|
||||
|
||||
/**
|
||||
* Backend that writes to Visual Studio's output window
|
||||
*/
|
||||
class DebuggerBackend : public Backend {
|
||||
public:
|
||||
static const char* Name() {
|
||||
return "debugger";
|
||||
}
|
||||
const char* GetName() const override {
|
||||
return Name();
|
||||
}
|
||||
void Write(const Entry& entry) override;
|
||||
size_t bytes_written;
|
||||
};
|
||||
|
||||
void AddBackend(std::unique_ptr<Backend> backend);
|
||||
|
||||
@@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) {
|
||||
}
|
||||
|
||||
void Filter::SetClassLevel(Class log_class, Level level) {
|
||||
class_levels[static_cast<std::size_t>(log_class)] = level;
|
||||
class_levels[static_cast<size_t>(log_class)] = level;
|
||||
}
|
||||
|
||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
@@ -93,8 +93,7 @@ void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
}
|
||||
|
||||
bool Filter::CheckMessage(Class log_class, Level level) const {
|
||||
return static_cast<u8>(level) >=
|
||||
static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
|
||||
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
|
||||
}
|
||||
|
||||
bool Filter::IsDebug() const {
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Log {
|
||||
class Filter {
|
||||
public:
|
||||
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
||||
explicit Filter(Level default_level = Level::Info);
|
||||
Filter(Level default_level = Level::Info);
|
||||
|
||||
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
||||
void ResetAll(Level level);
|
||||
@@ -49,6 +49,6 @@ public:
|
||||
bool IsDebug() const;
|
||||
|
||||
private:
|
||||
std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;
|
||||
std::array<Level, (size_t)Class::Count> class_levels;
|
||||
};
|
||||
} // namespace Log
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace Log {
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
///< pollute logs.
|
||||
/// pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
///< completed.
|
||||
Critical, ///< Major problems during execution that threaten the stability of the entire
|
||||
///< application.
|
||||
/// completed.
|
||||
Critical, ///< Major problems during execution that threathen the stability of the entire
|
||||
/// application.
|
||||
|
||||
Count ///< Total number of logging levels
|
||||
};
|
||||
@@ -49,7 +49,7 @@ enum class Class : ClassType {
|
||||
Kernel, ///< The HLE implementation of the CTR kernel
|
||||
Kernel_SVC, ///< Kernel system calls
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
///< should have its own subclass.
|
||||
/// should have its own subclass.
|
||||
Service_ACC, ///< The ACC (Accounts) service
|
||||
Service_AM, ///< The AM (Applet manager) service
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
@@ -58,22 +58,15 @@ enum class Class : ClassType {
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
Service_EUPLD, ///< The error upload service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_FGM, ///< The FGM service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_IRS, ///< The IRS service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
@@ -82,22 +75,17 @@ enum class Class : ClassType {
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NPNS, ///< The NPNS service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_PCV, ///< The PCV service
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_PSM, ///< The PSM service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_TCAP, ///< The TCAP service.
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
|
||||
@@ -31,7 +31,7 @@ std::string FormatLogMessage(const Entry& entry) {
|
||||
}
|
||||
|
||||
void PrintMessage(const Entry& entry) {
|
||||
const auto str = FormatLogMessage(entry).append(1, '\n');
|
||||
auto str = FormatLogMessage(entry) + '\n';
|
||||
fputs(str.c_str(), stderr);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ void PrintColoredMessage(const Entry& entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO original_info = {};
|
||||
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
|
||||
GetConsoleScreenBufferInfo(console_handle, &original_info);
|
||||
|
||||
WORD color = 0;
|
||||
|
||||
@@ -15,6 +15,6 @@ struct Entry;
|
||||
std::string FormatLogMessage(const Entry& entry);
|
||||
/// Formats and prints a log entry to stderr.
|
||||
void PrintMessage(const Entry& entry);
|
||||
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
|
||||
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
|
||||
void PrintColoredMessage(const Entry& entry);
|
||||
} // namespace Log
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_util.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
// Windows.h needs to be included before psapi.h
|
||||
#include <psapi.h>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/string_util.h"
|
||||
#else
|
||||
#include <cstdlib>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
#include <unistd.h>
|
||||
#define PAGE_MASK (getpagesize() - 1)
|
||||
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
|
||||
#endif
|
||||
|
||||
// This is purposely not a full wrapper for virtualalloc/mmap, but it
|
||||
// provides exactly the primitive operations that Dolphin needs.
|
||||
|
||||
void* AllocateExecutableMemory(size_t size, bool low) {
|
||||
#if defined(_WIN32)
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
||||
#else
|
||||
static char* map_hint = nullptr;
|
||||
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
// This OS has no flag to enforce allocation below the 4 GB boundary,
|
||||
// but if we hint that we want a low address it is very likely we will
|
||||
// get one.
|
||||
// An older version of this code used MAP_FIXED, but that has the side
|
||||
// effect of discarding already mapped pages that happen to be in the
|
||||
// requested virtual memory range (such as the emulated RAM, sometimes).
|
||||
if (low && (!map_hint))
|
||||
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
,
|
||||
-1, 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
#ifdef _WIN32
|
||||
if (ptr == nullptr) {
|
||||
#else
|
||||
if (ptr == MAP_FAILED) {
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
|
||||
}
|
||||
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
|
||||
else {
|
||||
if (low) {
|
||||
map_hint += size;
|
||||
map_hint = (char*)round_page(map_hint); /* round up to the next page */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if EMU_ARCH_BITS == 64
|
||||
if ((u64)ptr >= 0x80000000 && low == true)
|
||||
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
|
||||
#endif
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateMemoryPages(size_t size) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
#else
|
||||
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
ptr = nullptr;
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* AllocateAlignedMemory(size_t size, size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
void* ptr = _aligned_malloc(size, alignment);
|
||||
#else
|
||||
void* ptr = nullptr;
|
||||
#ifdef ANDROID
|
||||
ptr = memalign(alignment, size);
|
||||
#else
|
||||
if (posix_memalign(&ptr, alignment, size) != 0)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (ptr == nullptr)
|
||||
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* ptr, size_t size) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
if (!VirtualFree(ptr, 0, MEM_RELEASE))
|
||||
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
munmap(ptr, size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void FreeAlignedMemory(void* ptr) {
|
||||
if (ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
|
||||
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) {
|
||||
#ifdef _WIN32
|
||||
DWORD oldValue;
|
||||
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
|
||||
&oldValue))
|
||||
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
|
||||
#else
|
||||
mprotect(ptr, size,
|
||||
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string MemUsage() {
|
||||
#ifdef _WIN32
|
||||
#pragma comment(lib, "psapi")
|
||||
DWORD processID = GetCurrentProcessId();
|
||||
HANDLE hProcess;
|
||||
PROCESS_MEMORY_COUNTERS pmc;
|
||||
std::string Ret;
|
||||
|
||||
// Print information about the memory usage of the process.
|
||||
|
||||
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
|
||||
if (nullptr == hProcess)
|
||||
return "MemUsage Error";
|
||||
|
||||
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
|
||||
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
|
||||
|
||||
CloseHandle(hProcess);
|
||||
return Ret;
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
void* AllocateExecutableMemory(size_t size, bool low = true);
|
||||
void* AllocateMemoryPages(size_t size);
|
||||
void FreeMemoryPages(void* ptr, size_t size);
|
||||
void* AllocateAlignedMemory(size_t size, size_t alignment);
|
||||
void FreeAlignedMemory(void* ptr);
|
||||
void WriteProtectMemory(void* ptr, size_t size, bool executable = false);
|
||||
void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false);
|
||||
std::string MemUsage();
|
||||
|
||||
inline int GetPageSize() {
|
||||
return 4096;
|
||||
}
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
@@ -16,7 +16,7 @@
|
||||
// Call directly after the command or use the error num.
|
||||
// This function might change the error code.
|
||||
std::string GetLastErrorMsg() {
|
||||
static const std::size_t buff_size = 255;
|
||||
static const size_t buff_size = 255;
|
||||
char err_str[buff_size];
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -20,15 +20,7 @@ constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
|
||||
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
|
||||
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
|
||||
|
||||
/// A placeholder for empty param packages to avoid empty strings
|
||||
/// (they may be recognized as "not set" by some frontend libraries like qt)
|
||||
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
|
||||
|
||||
ParamPackage::ParamPackage(const std::string& serialized) {
|
||||
if (serialized == EMPTY_PLACEHOLDER) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> pairs;
|
||||
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
|
||||
|
||||
@@ -54,7 +46,7 @@ ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : d
|
||||
|
||||
std::string ParamPackage::Serialize() const {
|
||||
if (data.empty())
|
||||
return EMPTY_PLACEHOLDER;
|
||||
return "";
|
||||
|
||||
std::string result;
|
||||
|
||||
@@ -128,12 +120,4 @@ bool ParamPackage::Has(const std::string& key) const {
|
||||
return data.find(key) != data.end();
|
||||
}
|
||||
|
||||
void ParamPackage::Erase(const std::string& key) {
|
||||
data.erase(key);
|
||||
}
|
||||
|
||||
void ParamPackage::Clear() {
|
||||
data.clear();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -32,8 +32,6 @@ public:
|
||||
void Set(const std::string& key, int value);
|
||||
void Set(const std::string& key, float value);
|
||||
bool Has(const std::string& key) const;
|
||||
void Erase(const std::string& key);
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
DataType data;
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
/// SPSC ring buffer
|
||||
/// @tparam T Element type
|
||||
/// @tparam capacity Number of slots in ring buffer
|
||||
/// @tparam granularity Slot size in terms of number of elements
|
||||
template <typename T, std::size_t capacity, std::size_t granularity = 1>
|
||||
class RingBuffer {
|
||||
/// A "slot" is made of `granularity` elements of `T`.
|
||||
static constexpr std::size_t slot_size = granularity * sizeof(T);
|
||||
// T must be safely memcpy-able and have a trivial default constructor.
|
||||
static_assert(std::is_trivial_v<T>);
|
||||
// Ensure capacity is sensible.
|
||||
static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity);
|
||||
static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two");
|
||||
// Ensure lock-free.
|
||||
static_assert(std::atomic_size_t::is_always_lock_free);
|
||||
|
||||
public:
|
||||
/// Pushes slots into the ring buffer
|
||||
/// @param new_slots Pointer to the slots to push
|
||||
/// @param slot_count Number of slots to push
|
||||
/// @returns The number of slots actually pushed
|
||||
std::size_t Push(const void* new_slots, std::size_t slot_count) {
|
||||
const std::size_t write_index = m_write_index.load();
|
||||
const std::size_t slots_free = capacity + m_read_index.load() - write_index;
|
||||
const std::size_t push_count = std::min(slot_count, slots_free);
|
||||
|
||||
const std::size_t pos = write_index % capacity;
|
||||
const std::size_t first_copy = std::min(capacity - pos, push_count);
|
||||
const std::size_t second_copy = push_count - first_copy;
|
||||
|
||||
const char* in = static_cast<const char*>(new_slots);
|
||||
std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size);
|
||||
in += first_copy * slot_size;
|
||||
std::memcpy(m_data.data(), in, second_copy * slot_size);
|
||||
|
||||
m_write_index.store(write_index + push_count);
|
||||
|
||||
return push_count;
|
||||
}
|
||||
|
||||
std::size_t Push(const std::vector<T>& input) {
|
||||
return Push(input.data(), input.size());
|
||||
}
|
||||
|
||||
/// Pops slots from the ring buffer
|
||||
/// @param output Where to store the popped slots
|
||||
/// @param max_slots Maximum number of slots to pop
|
||||
/// @returns The number of slots actually popped
|
||||
std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) {
|
||||
const std::size_t read_index = m_read_index.load();
|
||||
const std::size_t slots_filled = m_write_index.load() - read_index;
|
||||
const std::size_t pop_count = std::min(slots_filled, max_slots);
|
||||
|
||||
const std::size_t pos = read_index % capacity;
|
||||
const std::size_t first_copy = std::min(capacity - pos, pop_count);
|
||||
const std::size_t second_copy = pop_count - first_copy;
|
||||
|
||||
char* out = static_cast<char*>(output);
|
||||
std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size);
|
||||
out += first_copy * slot_size;
|
||||
std::memcpy(out, m_data.data(), second_copy * slot_size);
|
||||
|
||||
m_read_index.store(read_index + pop_count);
|
||||
|
||||
return pop_count;
|
||||
}
|
||||
|
||||
std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) {
|
||||
std::vector<T> out(std::min(max_slots, capacity) * granularity);
|
||||
const std::size_t count = Pop(out.data(), out.size() / granularity);
|
||||
out.resize(count * granularity);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// @returns Number of slots used
|
||||
std::size_t Size() const {
|
||||
return m_write_index.load() - m_read_index.load();
|
||||
}
|
||||
|
||||
/// @returns Maximum size of ring buffer
|
||||
constexpr std::size_t Capacity() const {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
private:
|
||||
// It is important to align the below variables for performance reasons:
|
||||
// Having them on the same cache-line would result in false-sharing between them.
|
||||
// TODO: Remove this ifdef whenever clang and GCC support
|
||||
// std::hardware_destructive_interference_size.
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1911
|
||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0};
|
||||
alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0};
|
||||
#else
|
||||
alignas(128) std::atomic_size_t m_read_index{0};
|
||||
alignas(128) std::atomic_size_t m_write_index{0};
|
||||
#endif
|
||||
|
||||
std::array<T, granularity * capacity> m_data;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user