Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98ae39f751 | ||
|
|
b7f920486f | ||
|
|
66e62bf849 |
No files matched your search
@@ -3,12 +3,14 @@
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
cmake .. -GNinja -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
cmake .. -DDISPLAY_VERSION=$1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/clang -DCMAKE_CXX_COMPILER=/usr/lib/ccache/clang++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_INSTALL_PREFIX="/usr"
|
||||
|
||||
ninja
|
||||
make -j$(nproc)
|
||||
|
||||
ccache -s
|
||||
|
||||
|
||||
@@ -4,5 +4,5 @@ mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/clang/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh "$1"
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/clang/docker.sh $1
|
||||
sudo chown -R $UID ./
|
||||
Executable → Regular
File mode changed.
@@ -4,10 +4,8 @@
|
||||
cp license.txt "$DIR_NAME"
|
||||
cp README.md "$DIR_NAME"
|
||||
|
||||
if [[ -z "${NO_SOURCE_PACK}" ]]; then
|
||||
tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md license.txt
|
||||
cp -v "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
fi
|
||||
tar -cJvf "${REV_NAME}-source.tar.xz" src externals CMakeLists.txt README.md license.txt
|
||||
cp "${REV_NAME}-source.tar.xz" "$DIR_NAME"
|
||||
|
||||
tar $COMPRESSION_FLAGS "$ARCHIVE_NAME" "$DIR_NAME"
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
chmod a+x ./.ci/scripts/format/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.ci/scripts/format/docker.sh
|
||||
docker run -v $(pwd):/yuzu yuzuemu/build-environments:linux-clang-format /bin/bash -ex /yuzu/.ci/scripts/format/docker.sh
|
||||
sudo chown -R $UID ./
|
||||
@@ -3,6 +3,8 @@
|
||||
# Exit on error, rather than continuing with the rest of the script.
|
||||
set -e
|
||||
|
||||
cd /yuzu
|
||||
|
||||
ccache -s
|
||||
|
||||
mkdir build || true && cd build
|
||||
@@ -17,16 +19,15 @@ cmake .. \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||
-GNinja
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON
|
||||
|
||||
ninja
|
||||
make -j$(nproc)
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
DESTDIR="$PWD/AppDir" ninja install
|
||||
make install DESTDIR=AppDir
|
||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||
|
||||
# Download tools needed to build an AppImage
|
||||
|
||||
@@ -4,5 +4,5 @@ mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/linux/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh "$1"
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh $1
|
||||
sudo chown -R $UID ./
|
||||
Executable → Regular
-5
@@ -24,11 +24,6 @@ cd build
|
||||
wget -nc https://github.com/yuzu-emu/ext-linux-bin/raw/main/appimage/appimagetool-x86_64.AppImage
|
||||
chmod 755 appimagetool-x86_64.AppImage
|
||||
|
||||
# if FUSE is not available, then fallback to extract and run
|
||||
if ! ./appimagetool-x86_64.AppImage --version; then
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
fi
|
||||
|
||||
if [ "${RELEASE_NAME}" = "mainline" ]; then
|
||||
# Generate update information if releasing to mainline
|
||||
./appimagetool-x86_64.AppImage -u "gh-releases-zsync|yuzu-emu|yuzu-${RELEASE_NAME}|latest|yuzu-*.AppImage.zsync" AppDir "${APPIMAGE_NAME}"
|
||||
|
||||
@@ -1,33 +1,14 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
set -e
|
||||
cd /yuzu
|
||||
|
||||
#cd /yuzu
|
||||
ccache -s
|
||||
|
||||
ccache -sv
|
||||
mkdir build || true && cd build
|
||||
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON
|
||||
ninja
|
||||
|
||||
mkdir -p "$HOME/.conan/profiles"
|
||||
wget -c "https://github.com/yuzu-emu/build-environments/raw/master/linux-mingw/default" -O "$HOME/.conan/profiles/default"
|
||||
wget -c "https://github.com/yuzu-emu/build-environments/raw/master/linux-mingw/settings.yml" -O "$HOME/.conan/settings.yml"
|
||||
|
||||
mkdir -p build && cd build
|
||||
export LDFLAGS="-fuse-ld=lld"
|
||||
# -femulated-tls required due to an incompatibility between GCC and Clang
|
||||
# TODO(lat9nq): If this is widespread, we probably need to add this to CMakeLists where appropriate
|
||||
export CXXFLAGS="-femulated-tls"
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${PWD}/../CMakeModules/MinGWClangCross.cmake" \
|
||||
-DDISPLAY_VERSION="$1" \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_CCACHE=ON \
|
||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||
-GNinja
|
||||
ninja yuzu yuzu-cmd
|
||||
|
||||
ccache -sv
|
||||
ccache -s
|
||||
|
||||
echo "Tests skipped"
|
||||
#ctest -VV -C Release
|
||||
|
||||
@@ -4,5 +4,5 @@ mkdir -p "ccache" || true
|
||||
chmod a+x ./.ci/scripts/windows/docker.sh
|
||||
# the UID for the container yuzu user is 1027
|
||||
sudo chown -R 1027 ./
|
||||
docker run -e CCACHE_DIR=/yuzu/ccache -v "$(pwd):/yuzu" -w /yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh "$1"
|
||||
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh $1
|
||||
sudo chown -R $UID ./
|
||||
Executable → Regular
File mode changed.
@@ -1,115 +0,0 @@
|
||||
name: 'yuzu verify'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
format:
|
||||
name: 'verify format'
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: yuzuemu/build-environments:linux-clang-format
|
||||
options: -u 1001
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: false
|
||||
- name: 'Verify Formatting'
|
||||
run: bash -ex ./.ci/scripts/format/script.sh
|
||||
build:
|
||||
name: 'test build'
|
||||
needs: format
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- type: clang
|
||||
image: linux-fresh
|
||||
- type: linux
|
||||
image: linux-fresh
|
||||
- type: windows
|
||||
image: linux-mingw
|
||||
container:
|
||||
image: yuzuemu/build-environments:${{ matrix.image }}
|
||||
options: -u 1001
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v2
|
||||
id: ccache-restore
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.type }}-
|
||||
- name: Create ccache directory
|
||||
if: steps.ccache-restore.outputs.cache-hit != 'true'
|
||||
run: mkdir -p ~/.ccache
|
||||
- name: Build
|
||||
run: ./.ci/scripts/${{ matrix.type }}/docker.sh
|
||||
env:
|
||||
ENABLE_COMPATIBILITY_REPORTING: "ON"
|
||||
- name: Pack
|
||||
run: ./.ci/scripts/${{ matrix.type }}/upload.sh
|
||||
env:
|
||||
NO_SOURCE_PACK: "YES"
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ matrix.type }}
|
||||
path: artifacts/
|
||||
build-msvc:
|
||||
name: 'test build (windows, msvc)'
|
||||
needs: format
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- name: Set up cache
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.buildcache
|
||||
key: ${{ runner.os }}-msvc-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-msvc-
|
||||
- name: Install dependencies
|
||||
# due to how chocolatey works, only cmd.exe is supported here
|
||||
shell: cmd
|
||||
run: |
|
||||
choco install vulkan-sdk wget
|
||||
python -m pip install --upgrade pip conan
|
||||
call refreshenv
|
||||
wget https://github.com/mbitsnbites/buildcache/releases/download/v0.27.6/buildcache-windows.zip
|
||||
7z x buildcache-windows.zip
|
||||
copy buildcache\bin\buildcache.exe C:\ProgramData\chocolatey\bin
|
||||
rmdir buildcache
|
||||
echo %PATH% >> %GITHUB_PATH%
|
||||
- name: Set up MSVC
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- name: Configure
|
||||
env:
|
||||
CC: cl.exe
|
||||
CXX: cl.exe
|
||||
run: |
|
||||
glslangValidator --version
|
||||
mkdir build
|
||||
cmake . -B build -GNinja -DCMAKE_TOOLCHAIN_FILE="CMakeModules/MSVCCache.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release
|
||||
- name: Build
|
||||
run: cmake --build build
|
||||
- name: Cache Summary
|
||||
run: buildcache -s
|
||||
- name: Pack
|
||||
shell: pwsh
|
||||
run: .\.ci\scripts\windows\upload.ps1
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: msvc
|
||||
path: artifacts/
|
||||
+4
-1
@@ -3,10 +3,13 @@
|
||||
url = https://github.com/benhoyt/inih.git
|
||||
[submodule "cubeb"]
|
||||
path = externals/cubeb
|
||||
url = https://github.com/mozilla/cubeb.git
|
||||
url = https://github.com/kinetiknz/cubeb.git
|
||||
[submodule "dynarmic"]
|
||||
path = externals/dynarmic
|
||||
url = https://github.com/MerryMage/dynarmic.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
|
||||
|
||||
+34
-31
@@ -21,6 +21,8 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" "${MSV
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_BOOST "Download bundled Boost" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_LIBUSB "Compile bundled libusb" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_FFMPEG "Download/Build bundled FFmpeg" "${WIN32}")
|
||||
@@ -206,7 +208,9 @@ macro(yuzu_find_packages)
|
||||
unset(FN_FORCE_REQUIRED)
|
||||
endmacro()
|
||||
|
||||
find_package(Boost 1.73.0 COMPONENTS context headers)
|
||||
if (NOT YUZU_USE_BUNDLED_BOOST)
|
||||
find_package(Boost 1.73.0 CONFIG COMPONENTS context headers QUIET)
|
||||
endif()
|
||||
if (Boost_FOUND)
|
||||
set(Boost_LIBRARIES Boost::boost)
|
||||
# Conditionally add Boost::context only if the active version of the Conan or system Boost package provides it
|
||||
@@ -217,14 +221,23 @@ if (Boost_FOUND)
|
||||
if (TARGET Boost::context)
|
||||
list(APPEND Boost_LIBRARIES Boost::context)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Boost 1.79.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.79.0")
|
||||
endif()
|
||||
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" OR YUZU_USE_BUNDLED_BOOST)
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to externals")
|
||||
set(YUZU_USE_BUNDLED_BOOST ON CACHE BOOL "Download bundled Boost" FORCE)
|
||||
|
||||
# boost:asio has functions that require AcceptEx et al
|
||||
if (MINGW)
|
||||
find_library(MSWSOCK_LIBRARY mswsock REQUIRED)
|
||||
# Use yuzu Boost binaries
|
||||
set(Boost_EXT_NAME "boost_1_75_0")
|
||||
set(Boost_PATH "${CMAKE_BINARY_DIR}/externals/${Boost_EXT_NAME}")
|
||||
download_bundled_external("boost/" ${Boost_EXT_NAME} "")
|
||||
set(Boost_USE_DEBUG_RUNTIME FALSE)
|
||||
set(Boost_USE_STATIC_LIBS ON)
|
||||
find_package(Boost 1.75.0 CONFIG REQUIRED COMPONENTS context headers PATHS ${Boost_PATH} NO_DEFAULT_PATH)
|
||||
# Manually set the include dirs since the find_package sets it incorrectly
|
||||
set(Boost_INCLUDE_DIRS ${Boost_PATH}/include CACHE PATH "Path to Boost headers" FORCE)
|
||||
include_directories(SYSTEM "${Boost_INCLUDE_DIRS}")
|
||||
else()
|
||||
message(STATUS "Boost 1.73.0 or newer not found, falling back to Conan")
|
||||
list(APPEND CONAN_REQUIRED_LIBS "boost/1.78.0")
|
||||
endif()
|
||||
|
||||
# Attempt to locate any packages that are required and report the missing ones in CONAN_REQUIRED_LIBS
|
||||
@@ -410,11 +423,13 @@ if (CONAN_REQUIRED_LIBS)
|
||||
# Download conan.cmake automatically, you can also just copy the conan.cmake file
|
||||
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/release/0.18/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
# TODO: Use a tagged release. The latest tagged release does not support VS2022 as of this writing.
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/43e385830ee35377dbd2dcbe8d5a9e750301ea00/conan.cmake"
|
||||
"${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
endif()
|
||||
include(${CMAKE_BINARY_DIR}/conan.cmake)
|
||||
|
||||
conan_check(VERSION 1.45.0 REQUIRED)
|
||||
conan_check(VERSION 1.41.0 REQUIRED)
|
||||
|
||||
# Manually add iconv to fix a dep conflict between qt and sdl2
|
||||
# We don't need to add it through find_package or anything since the other two can find it just fine
|
||||
@@ -627,14 +642,6 @@ add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY
|
||||
-DBOOST_DATE_TIME_NO_LIB
|
||||
-DBOOST_REGEX_NO_LIB
|
||||
)
|
||||
# Adjustments for MSVC + Ninja
|
||||
if (MSVC AND CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
add_compile_options(
|
||||
/wd4711 # function 'function' selected for automatic inline expansion
|
||||
/wd4464 # relative include path contains '..'
|
||||
/wd4820 # 'identifier1': '4' bytes padding added after data member 'identifier2'
|
||||
)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(externals)
|
||||
@@ -652,18 +659,14 @@ endif()
|
||||
# =========================
|
||||
|
||||
# Install freedesktop.org metadata files, following those specifications:
|
||||
# https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
|
||||
# https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
|
||||
# https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
|
||||
# https://www.freedesktop.org/software/appstream/docs/
|
||||
# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
|
||||
# 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 "dist/org.yuzu_emu.yuzu.desktop"
|
||||
DESTINATION "share/applications")
|
||||
install(FILES "dist/yuzu.svg"
|
||||
DESTINATION "share/icons/hicolor/scalable/apps"
|
||||
RENAME "org.yuzu_emu.yuzu.svg")
|
||||
install(FILES "dist/org.yuzu_emu.yuzu.xml"
|
||||
DESTINATION "share/mime/packages")
|
||||
install(FILES "dist/org.yuzu_emu.yuzu.metainfo.xml"
|
||||
DESTINATION "share/metainfo")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
|
||||
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
|
||||
endif()
|
||||
@@ -2,6 +2,5 @@ function(copy_yuzu_FFmpeg_deps target_dir)
|
||||
include(WindowsCopyFiles)
|
||||
set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
|
||||
file(READ "${FFmpeg_PATH}/requirements.txt" FFmpeg_REQUIRED_DLLS)
|
||||
string(STRIP "${FFmpeg_REQUIRED_DLLS}" FFmpeg_REQUIRED_DLLS)
|
||||
windows_copy_files(${target_dir} ${FFmpeg_DLL_DIR} ${DLL_DEST} ${FFmpeg_REQUIRED_DLLS})
|
||||
endfunction(copy_yuzu_FFmpeg_deps)
|
||||
@@ -1,12 +0,0 @@
|
||||
# buildcache wrapper
|
||||
OPTION(USE_CCACHE "Use buildcache for compilation" OFF)
|
||||
IF(USE_CCACHE)
|
||||
FIND_PROGRAM(CCACHE buildcache)
|
||||
IF (CCACHE)
|
||||
MESSAGE(STATUS "Using buildcache 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 buildcache executable found")
|
||||
ENDIF(CCACHE)
|
||||
ENDIF(USE_CCACHE)
|
||||
@@ -1,55 +0,0 @@
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
|
||||
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}clang)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}clang++)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
set(CMAKE_C_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_CXX_COMPILER_AR ${MINGW_TOOL_PREFIX}ar)
|
||||
set(CMAKE_C_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
set(CMAKE_CXX_COMPILER_RANLIB ${MINGW_TOOL_PREFIX}ranlib)
|
||||
|
||||
# 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()
|
||||
@@ -79,4 +79,4 @@ If you wish to support us a different way, please join our [Discord](https://dis
|
||||
|
||||
yuzu is licensed under the GPLv3 (or any later version). Refer to the [license.txt](https://github.com/yuzu-emu/yuzu/blob/master/license.txt) file.
|
||||
|
||||
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) may choose to use the code from these contributors under the GPL-3.0-or-later OR MPL-2.0: [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq)
|
||||
The [Skyline-Emulator Team](https://github.com/skyline-emu/skyline) is exempt from GPLv3 for the contributions from all these contributors [FernandoS27](https://github.com/FernandoS27), [lioncash](https://github.com/lioncash), [bunnei](https://github.com/bunnei), [ReinUsesLisp](https://github.com/ReinUsesLisp), [Morph1984](https://github.com/Morph1984), [ogniK5377](https://github.com/ogniK5377), [german77](https://github.com/german77), [ameerj](https://github.com/ameerj), [Kelebek1](https://github.com/Kelebek1) and [lat9nq](https://github.com/lat9nq). They may only use the code from these contributors under Mozilla Public License, version 2.0.
|
||||
Vendored
+716
-1069
File diff suppressed because it is too large.
Load diff
Vendored
+715
-1067
File diff suppressed because it is too large.
Load diff
Vendored
+699
-1042
File diff suppressed because it is too large.
Load diff
Vendored
+763
-1114
File diff suppressed because it is too large.
Load diff
Vendored
-6559
File diff suppressed because it is too large.
Load diff
Vendored
+725
-1087
File diff suppressed because it is too large.
Load diff
Vendored
+523
-568
File diff suppressed because it is too large.
Load diff
Vendored
+756
-1102
File diff suppressed because it is too large.
Load diff
Vendored
+714
-1060
File diff suppressed because it is too large.
Load diff
Vendored
+706
-1049
File diff suppressed because it is too large.
Load diff
Vendored
+842
-1191
File diff suppressed because it is too large.
Load diff
Vendored
+711
-1077
File diff suppressed because it is too large.
Load diff
Vendored
+1023
-1415
File diff suppressed because it is too large.
Load diff
Vendored
+701
-1044
File diff suppressed because it is too large.
Load diff
Vendored
+702
-1045
File diff suppressed because it is too large.
Load diff
Vendored
+702
-1064
File diff suppressed because it is too large.
Load diff
Vendored
+705
-1064
File diff suppressed because it is too large.
Load diff
Vendored
+978
-1337
File diff suppressed because it is too large.
Load diff
Vendored
+698
-1041
File diff suppressed because it is too large.
Load diff
Vendored
+714
-1057
File diff suppressed because it is too large.
Load diff
Vendored
+698
-1041
File diff suppressed because it is too large.
Load diff
Vendored
+698
-1041
File diff suppressed because it is too large.
Load diff
Vendored
+698
-1057
File diff suppressed because it is too large.
Load diff
Vendored
+696
-1055
File diff suppressed because it is too large.
Load diff
Vendored
-56
@@ -1,56 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>org.yuzu_emu.yuzu</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<name>yuzu</name>
|
||||
<summary>Nintendo Switch emulator</summary>
|
||||
<description>
|
||||
<p>yuzu is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of Citra.</p>
|
||||
<p>The emulator is capable of running most commercial games at full speed, provided you meet the necessary hardware requirements.</p>
|
||||
<p>For a full list of games yuzu support, please visit our Compatibility page.</p>
|
||||
<p>Check out our website for the latest news on exciting features, monthly progress reports, and more!</p>
|
||||
</description>
|
||||
<categories>
|
||||
<category>Game</category>
|
||||
<category>Emulator</category>
|
||||
</categories>
|
||||
<keywords>
|
||||
<keyword>switch</keyword>
|
||||
<keyword>emulator</keyword>
|
||||
</keywords>
|
||||
<url type="homepage">https://yuzu-emu.org/</url>
|
||||
<url type="bugtracker">https://github.com/yuzu-emu/yuzu/issues</url>
|
||||
<url type="faq">https://yuzu-emu.org/wiki/faq/</url>
|
||||
<url type="help">https://yuzu-emu.org/wiki/home/</url>
|
||||
<url type="donation">https://yuzu-emu.org/donate/</url>
|
||||
<url type="translate">https://www.transifex.com/projects/p/yuzu</url>
|
||||
<url type="contact">https://community.citra-emu.org/</url>
|
||||
<url type="vcs-browser">https://github.com/yuzu-emu/yuzu</url>
|
||||
<url type="contribute">https://yuzu-emu.org/wiki/contributing/</url>
|
||||
<launchable type="desktop-id">org.yuzu_emu.yuzu.desktop</launchable>
|
||||
<provides>
|
||||
<binary>yuzu</binary>
|
||||
<binary>yuzu-cmd</binary>
|
||||
</provides>
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>gamepad</control>
|
||||
</supports>
|
||||
<requires>
|
||||
<memory>8192</memory>
|
||||
</requires>
|
||||
<recommends>
|
||||
<memory>16384</memory>
|
||||
</recommends>
|
||||
<project_license>GPL-3.0-or-later</project_license>
|
||||
<developer_name>yuzu Emulator Team</developer_name>
|
||||
<content_rating type="oars-1.0"/>
|
||||
<screenshots>
|
||||
<screenshot type="default">https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/001-Super%20Mario%20Odyssey%20.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/004-The%20Legend%20of%20Zelda%20Skyward%20Sword%20HD.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/007-Pokemon%20Sword.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/010-Hyrule%20Warriors%20Age%20of%20Calamity.png</screenshot>
|
||||
<screenshot>https://raw.githubusercontent.com/yuzu-emu/yuzu-emu.github.io/master/images/screenshots/039-Pok%C3%A9mon%20Mystery%20Dungeon%20Rescue%20Team%20DX.png.png.png</screenshot>
|
||||
</screenshots>
|
||||
</component>
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 10 KiB |
Vendored
-13
@@ -58,19 +58,6 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #109010;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #000000;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 21px;
|
||||
min-height: 21px;
|
||||
|
||||
Vendored
-13
@@ -1304,19 +1304,6 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 23px;
|
||||
min-height: 23px;
|
||||
|
||||
@@ -2207,19 +2207,6 @@ QPushButton#GPUStatusBarButton:!checked {
|
||||
color: #40dd40;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton {
|
||||
min-width: 0px;
|
||||
color: #ffffff;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
padding: 0px 3px 0px 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
QPushButton#DockingStatusBarButton:hover {
|
||||
border: 1px solid #76797C;
|
||||
}
|
||||
|
||||
QPushButton#buttonRefreshDevices {
|
||||
min-width: 19px;
|
||||
min-height: 19px;
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@ Type=Application
|
||||
Name=yuzu
|
||||
GenericName=Switch Emulator
|
||||
Comment=Nintendo Switch video game console emulator
|
||||
Icon=org.yuzu_emu.yuzu
|
||||
Icon=yuzu
|
||||
TryExec=yuzu
|
||||
Exec=yuzu %f
|
||||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Nintendo;Switch;
|
||||
Keywords=Switch;Nintendo;
|
||||
+5
-5
@@ -3,7 +3,7 @@
|
||||
<mime-type type="application/x-nx-nro">
|
||||
<comment>Nintendo Switch homebrew executable</comment>
|
||||
<acronym>NRO</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nro"/>
|
||||
<magic><match value="NRO" type="string" offset="16"/></magic>
|
||||
</mime-type>
|
||||
@@ -11,7 +11,7 @@
|
||||
<mime-type type="application/x-nx-nso">
|
||||
<comment>Nintendo Switch homebrew executable</comment>
|
||||
<acronym>NSO</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nso"/>
|
||||
<magic><match value="NSO" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
@@ -19,7 +19,7 @@
|
||||
<mime-type type="application/x-nx-nsp">
|
||||
<comment>Nintendo Switch Package</comment>
|
||||
<acronym>NSP</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.nsp"/>
|
||||
<magic><match value="PFS" type="string" offset="0"/></magic>
|
||||
</mime-type>
|
||||
@@ -27,7 +27,7 @@
|
||||
<mime-type type="application/x-nx-xci">
|
||||
<comment>Nintendo Switch Card Image</comment>
|
||||
<acronym>XCI</acronym>
|
||||
<icon name="org.yuzu_emu.yuzu"/>
|
||||
<icon name="yuzu"/>
|
||||
<glob pattern="*.xci"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
</mime-info>
|
||||
Vendored
+3
-5
@@ -40,11 +40,6 @@ target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
|
||||
# GCC bugs
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "12" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND MINGW)
|
||||
target_compile_options(microprofile INTERFACE "-Wno-array-bounds")
|
||||
endif()
|
||||
|
||||
# libusb
|
||||
if (NOT LIBUSB_FOUND OR YUZU_USE_BUNDLED_LIBUSB)
|
||||
add_subdirectory(libusb)
|
||||
@@ -73,6 +68,9 @@ if (YUZU_USE_EXTERNAL_SDL2)
|
||||
add_library(SDL2 ALIAS SDL2-static)
|
||||
endif()
|
||||
|
||||
# SoundTouch
|
||||
add_subdirectory(soundtouch)
|
||||
|
||||
# Cubeb
|
||||
if(ENABLE_CUBEB)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
|
||||
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: 33d4dd987f...e005e1f817.
Vendored
+1
-1
Submodule externals/cpp-httplib updated: 305a7abcb9...9648f950f5.
Vendored
+1
-1
Submodule externals/cubeb updated: 6f79fd7c0d...75d9d125ee.
Vendored
+1
-1
Submodule externals/dynarmic updated: 9ebf6a8384...af2d50288f.
Vendored
+1
-1
Submodule externals/libressl updated: 8929f818fd...8289d0d07d.
+1
-1
@@ -1246,7 +1246,7 @@ struct MicroProfileScopeLock
|
||||
{
|
||||
bool bUseLock;
|
||||
std::recursive_mutex& m;
|
||||
MicroProfileScopeLock(std::recursive_mutex& m_) : bUseLock(g_bUseLock), m(m_)
|
||||
MicroProfileScopeLock(std::recursive_mutex& m) : bUseLock(g_bUseLock), m(m)
|
||||
{
|
||||
if(bUseLock)
|
||||
m.lock();
|
||||
|
||||
+38
-37
@@ -213,8 +213,8 @@ struct MicroProfileCustom
|
||||
|
||||
struct SOptionDesc
|
||||
{
|
||||
SOptionDesc()=default;
|
||||
SOptionDesc(uint8_t nSubType_, uint8_t nIndex_, const char* fmt, ...):nSubType(nSubType_), nIndex(nIndex_)
|
||||
SOptionDesc(){}
|
||||
SOptionDesc(uint8_t nSubType, uint8_t nIndex, const char* fmt, ...):nSubType(nSubType), nIndex(nIndex)
|
||||
{
|
||||
va_list args;
|
||||
va_start (args, fmt);
|
||||
@@ -573,10 +573,10 @@ inline void MicroProfileToolTipMeta(MicroProfileStringArray* pToolTip)
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int k = 0; k < MICROPROFILE_META_MAX; ++k)
|
||||
for(int i = 0; i < MICROPROFILE_META_MAX; ++i)
|
||||
{
|
||||
nMetaSumInclusive[k] += nMetaSum[k];
|
||||
nMetaSum[k] = 0;
|
||||
nMetaSumInclusive[i] += nMetaSum[i];
|
||||
nMetaSum[i] = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -708,10 +708,10 @@ inline void MicroProfileDrawFloatTooltip(uint32_t nX, uint32_t nY, uint32_t nTok
|
||||
|
||||
if(UI.nMouseLeftMod)
|
||||
{
|
||||
int nToolTipIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
|
||||
g_MicroProfileUI.nLockedToolTipColor[nToolTipIndex] = S.TimerInfo[nTimerId].nColor;
|
||||
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nToolTipIndex], &ToolTip);
|
||||
g_MicroProfileUI.LockedToolTipFront = nToolTipIndex;
|
||||
int nIndex = (g_MicroProfileUI.LockedToolTipFront + MICROPROFILE_TOOLTIP_MAX_LOCKED - 1) % MICROPROFILE_TOOLTIP_MAX_LOCKED;
|
||||
g_MicroProfileUI.nLockedToolTipColor[nIndex] = S.TimerInfo[nTimerId].nColor;
|
||||
MicroProfileStringArrayCopy(&g_MicroProfileUI.LockedToolTips[nIndex], &ToolTip);
|
||||
g_MicroProfileUI.LockedToolTipFront = nIndex;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -917,8 +917,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
float fStart = floor(fMsBase*fRcpStep) * fStep;
|
||||
for(float f = fStart; f < fMsEnd; )
|
||||
{
|
||||
float fStart = f;
|
||||
float fNext = f + fStep;
|
||||
MicroProfileDrawBox(((f-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
|
||||
MicroProfileDrawBox(((fStart-fMsBase) * fMsToScreen), nBaseY, (fNext-fMsBase) * fMsToScreen+1, nBaseY + nHeight, UI.nOpacityBackground | g_nMicroProfileBackColors[nColorIndex++ & 1]);
|
||||
f = fNext;
|
||||
}
|
||||
}
|
||||
@@ -1115,9 +1116,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
|
||||
nMaxStackDepth = MicroProfileMax(nMaxStackDepth, nStackPos);
|
||||
float fMsStart = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickStart);
|
||||
float fMsEnd2 = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
|
||||
float fMsEnd = fToMs * MicroProfileLogTickDifference(nBaseTicks, nTickEnd);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd2 * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
float fYStart = (float)(nY + nStackPos * nYDelta);
|
||||
float fYEnd = fYStart + (MICROPROFILE_DETAILED_BAR_HEIGHT);
|
||||
float fXDist = MicroProfileMax(fXStart - fMouseX, fMouseX - fXEnd);
|
||||
@@ -1268,22 +1269,22 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
if(UI.nRangeBegin != UI.nRangeEnd)
|
||||
{
|
||||
float fMsStart = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeBegin);
|
||||
float fMsEnd3 = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
|
||||
float fMsEnd = fToMsCpu * MicroProfileLogTickDifference(nBaseTicksCpu, UI.nRangeEnd);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd3 * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
|
||||
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT | 0x44000000);
|
||||
|
||||
fMsStart += fDetailedOffset;
|
||||
fMsEnd3 += fDetailedOffset;
|
||||
fMsEnd += fDetailedOffset;
|
||||
char sBuffer[32];
|
||||
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
|
||||
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
|
||||
float fStartTextX = fXStart - fStartTextWidth - 2;
|
||||
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd3);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
|
||||
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
|
||||
|
||||
@@ -1296,9 +1297,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
if(UI.nRangeBeginGpu != UI.nRangeEndGpu)
|
||||
{
|
||||
float fMsStart = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeBeginGpu);
|
||||
float fMsEnd4 = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
|
||||
float fMsEnd = fToMsGpu * MicroProfileLogTickDifference(nBaseTicksGpu, UI.nRangeEndGpu);
|
||||
float fXStart = fMsStart * fMsToScreen;
|
||||
float fXEnd = fMsEnd4 * fMsToScreen;
|
||||
float fXEnd = fMsEnd * fMsToScreen;
|
||||
MicroProfileDrawBox(fXStart, nBaseY, fXEnd, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawLineVertical(fXStart, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
|
||||
MicroProfileDrawLineVertical(fXEnd, nBaseY, nHeight, MICROPROFILE_FRAME_COLOR_HIGHTLIGHT_GPU | 0x44000000);
|
||||
@@ -1306,14 +1307,14 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
|
||||
nBaseY += MICROPROFILE_TEXT_HEIGHT+1;
|
||||
|
||||
fMsStart += fDetailedOffset;
|
||||
fMsEnd4 += fDetailedOffset;
|
||||
fMsEnd += fDetailedOffset;
|
||||
char sBuffer[32];
|
||||
uint32_t nLenStart = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsStart);
|
||||
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
|
||||
float fStartTextX = fXStart - fStartTextWidth - 2;
|
||||
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd4);
|
||||
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
|
||||
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
|
||||
MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
|
||||
}
|
||||
@@ -1715,8 +1716,8 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
uint32_t nTextCount = 0;
|
||||
uint32_t nGraphIndex = (S.nGraphPut + MICROPROFILE_GRAPH_HISTORY - int(MICROPROFILE_GRAPH_HISTORY*(1.f - fMouseXPrc))) % MICROPROFILE_GRAPH_HISTORY;
|
||||
|
||||
uint32_t nMouseX = UI.nMouseX;
|
||||
uint32_t nMouseY = UI.nMouseY + 20;
|
||||
uint32_t nX = UI.nMouseX;
|
||||
uint32_t nY = UI.nMouseY + 20;
|
||||
|
||||
for(uint32_t i = 0; i < MICROPROFILE_MAX_GRAPHS; ++i)
|
||||
{
|
||||
@@ -1735,7 +1736,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
}
|
||||
if(nTextCount)
|
||||
{
|
||||
MicroProfileDrawFloatWindow(nMouseX, nMouseY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
|
||||
MicroProfileDrawFloatWindow(nX, nY, Strings.ppStrings, Strings.nNumStrings, 0, pColors);
|
||||
}
|
||||
|
||||
if(UI.nMouseRight)
|
||||
@@ -2320,8 +2321,8 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
uint32_t nMenuX[MICROPROFILE_MENU_MAX] = {0};
|
||||
uint32_t nNumMenuItems = 0;
|
||||
|
||||
int nMPTextLen = snprintf(buffer, 127, "MicroProfile");
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nMPTextLen);
|
||||
int nLen = snprintf(buffer, 127, "MicroProfile");
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
|
||||
nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
pMenuText[nNumMenuItems++] = "Mode";
|
||||
pMenuText[nNumMenuItems++] = "Groups";
|
||||
@@ -2437,16 +2438,16 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
int nNumLines = 0;
|
||||
bool bSelected = false;
|
||||
const char* pString = CB(nNumLines, &bSelected);
|
||||
uint32_t nTextWidth = 0, nTextHeight = 0;
|
||||
uint32_t nWidth = 0, nHeight = 0;
|
||||
while(pString)
|
||||
{
|
||||
nTextWidth = MicroProfileMax<int>(nTextWidth, (int)strlen(pString));
|
||||
nWidth = MicroProfileMax<int>(nWidth, (int)strlen(pString));
|
||||
nNumLines++;
|
||||
pString = CB(nNumLines, &bSelected);
|
||||
}
|
||||
nTextWidth = (2+nTextWidth) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
nTextHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
|
||||
if(UI.nMouseY <= nY + nTextHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nTextWidth + 0 && UI.nMouseX >= nX - 0)
|
||||
nWidth = (2+nWidth) * (MICROPROFILE_TEXT_WIDTH+1);
|
||||
nHeight = nNumLines * (MICROPROFILE_TEXT_HEIGHT+1);
|
||||
if(UI.nMouseY <= nY + nHeight+0 && UI.nMouseY >= nY-0 && UI.nMouseX <= nX + nWidth + 0 && UI.nMouseX >= nX - 0)
|
||||
{
|
||||
UI.nActiveMenu = nMenu;
|
||||
}
|
||||
@@ -2454,21 +2455,21 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
|
||||
{
|
||||
UI.nActiveMenu = UINT32_MAX;
|
||||
}
|
||||
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + nTextHeight, 0xff000000|g_nMicroProfileBackColors[1]);
|
||||
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
|
||||
for(int i = 0; i < nNumLines; ++i)
|
||||
{
|
||||
bool bSelected2 = false;
|
||||
const char* pString2 = CB(i, &bSelected2);
|
||||
bool bSelected = false;
|
||||
const char* pString = CB(i, &bSelected);
|
||||
if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
|
||||
{
|
||||
if(UI.nMouseLeft || UI.nMouseRight)
|
||||
{
|
||||
CBClick[nMenu](i);
|
||||
}
|
||||
MicroProfileDrawBox(nX, nY, nX + nTextWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
|
||||
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
|
||||
}
|
||||
int nTextLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected2 ? '*' : ' ' ,pString2);
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nTextLen);
|
||||
int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
|
||||
MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
|
||||
nY += MICROPROFILE_TEXT_HEIGHT+1;
|
||||
}
|
||||
}
|
||||
@@ -2604,7 +2605,7 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
|
||||
for(uint32_t i = 0; i < nCount; ++i)
|
||||
{
|
||||
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
|
||||
nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
|
||||
uint32_t nWidth = MicroProfileMin(nMaxWidth, (uint32_t)(nMaxWidth * pMs[i] * fRcpReference));
|
||||
MicroProfileDrawBox(nMaxOffsetX, nOffsetY, nMaxOffsetX+nWidth, nOffsetY+MICROPROFILE_TEXT_HEIGHT, pColors[i]|0xff000000);
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
Submodule externals/sirit updated: aa292d5665...a39596358a.
+1
Submodule externals/soundtouch added at 060181eaf2.
+7
-14
@@ -36,6 +36,7 @@ if (MSVC)
|
||||
# /GT - Supports fiber safety for data allocated using static thread-local storage
|
||||
add_compile_options(
|
||||
/MP
|
||||
/Zi
|
||||
/Zm200
|
||||
/Zo
|
||||
/permissive-
|
||||
@@ -64,27 +65,14 @@ if (MSVC)
|
||||
/we4305 # 'context': truncation from 'type1' to 'type2'
|
||||
/we4388 # 'expression': signed/unsigned mismatch
|
||||
/we4389 # 'operator': signed/unsigned mismatch
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
/we4505 # 'function': unreferenced local function has been removed
|
||||
/we4547 # 'operator': operator before comma has no effect; expected operator with side-effect
|
||||
/we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
|
||||
/we4555 # Expression has no effect; expected expression with side-effect
|
||||
/we4715 # 'function': not all control paths return a value
|
||||
/we4834 # Discarding return value of function with 'nodiscard' attribute
|
||||
/we5038 # data member 'member1' will be initialized after data member 'member2'
|
||||
/we5245 # 'function': unreferenced function with internal linkage has been removed
|
||||
)
|
||||
|
||||
if (USE_CCACHE)
|
||||
# when caching, we need to use /Z7 to downgrade debug info to use an older but more cachable format
|
||||
add_compile_options(/Z7)
|
||||
else()
|
||||
add_compile_options(/Zi)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options(/QIntel-jcc-erratum)
|
||||
endif()
|
||||
@@ -102,7 +90,6 @@ else()
|
||||
-Werror=missing-declarations
|
||||
-Werror=missing-field-initializers
|
||||
-Werror=reorder
|
||||
-Werror=shadow
|
||||
-Werror=sign-compare
|
||||
-Werror=switch
|
||||
-Werror=uninitialized
|
||||
@@ -116,6 +103,12 @@ else()
|
||||
-Wno-unused-parameter
|
||||
)
|
||||
|
||||
# TODO: Remove when we update to a GCC compiler that enables this
|
||||
# by default (i.e. GCC 10 or newer).
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU)
|
||||
add_compile_options(-fconcepts)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options("-mcx16")
|
||||
endif()
|
||||
|
||||
+47
-208
@@ -1,217 +1,58 @@
|
||||
add_library(audio_core STATIC
|
||||
audio_core.cpp
|
||||
audio_core.h
|
||||
audio_event.h
|
||||
audio_event.cpp
|
||||
audio_render_manager.cpp
|
||||
audio_render_manager.h
|
||||
audio_in_manager.cpp
|
||||
audio_in_manager.h
|
||||
audio_out_manager.cpp
|
||||
audio_out_manager.h
|
||||
audio_manager.cpp
|
||||
audio_manager.h
|
||||
common/audio_renderer_parameter.h
|
||||
common/common.h
|
||||
common/feature_support.h
|
||||
common/wave_buffer.h
|
||||
common/workbuffer_allocator.h
|
||||
device/audio_buffer.h
|
||||
device/audio_buffers.h
|
||||
device/device_session.cpp
|
||||
device/device_session.h
|
||||
in/audio_in.cpp
|
||||
in/audio_in.h
|
||||
in/audio_in_system.cpp
|
||||
in/audio_in_system.h
|
||||
out/audio_out.cpp
|
||||
out/audio_out.h
|
||||
out/audio_out_system.cpp
|
||||
out/audio_out_system.h
|
||||
renderer/adsp/adsp.cpp
|
||||
renderer/adsp/adsp.h
|
||||
renderer/adsp/audio_renderer.cpp
|
||||
renderer/adsp/audio_renderer.h
|
||||
renderer/adsp/command_buffer.h
|
||||
renderer/adsp/command_list_processor.cpp
|
||||
renderer/adsp/command_list_processor.h
|
||||
renderer/audio_device.cpp
|
||||
renderer/audio_device.h
|
||||
renderer/audio_renderer.h
|
||||
renderer/audio_renderer.cpp
|
||||
renderer/behavior/behavior_info.cpp
|
||||
renderer/behavior/behavior_info.h
|
||||
renderer/behavior/info_updater.cpp
|
||||
renderer/behavior/info_updater.h
|
||||
renderer/command/data_source/adpcm.cpp
|
||||
renderer/command/data_source/adpcm.h
|
||||
renderer/command/data_source/decode.cpp
|
||||
renderer/command/data_source/decode.h
|
||||
renderer/command/data_source/pcm_float.cpp
|
||||
renderer/command/data_source/pcm_float.h
|
||||
renderer/command/data_source/pcm_int16.cpp
|
||||
renderer/command/data_source/pcm_int16.h
|
||||
renderer/command/effect/aux_.cpp
|
||||
renderer/command/effect/aux_.h
|
||||
renderer/command/effect/biquad_filter.cpp
|
||||
renderer/command/effect/biquad_filter.h
|
||||
renderer/command/effect/capture.cpp
|
||||
renderer/command/effect/capture.h
|
||||
renderer/command/effect/compressor.cpp
|
||||
renderer/command/effect/compressor.h
|
||||
renderer/command/effect/delay.cpp
|
||||
renderer/command/effect/delay.h
|
||||
renderer/command/effect/i3dl2_reverb.cpp
|
||||
renderer/command/effect/i3dl2_reverb.h
|
||||
renderer/command/effect/light_limiter.cpp
|
||||
renderer/command/effect/light_limiter.h
|
||||
renderer/command/effect/multi_tap_biquad_filter.cpp
|
||||
renderer/command/effect/multi_tap_biquad_filter.h
|
||||
renderer/command/effect/reverb.cpp
|
||||
renderer/command/effect/reverb.h
|
||||
renderer/command/mix/clear_mix.cpp
|
||||
renderer/command/mix/clear_mix.h
|
||||
renderer/command/mix/copy_mix.cpp
|
||||
renderer/command/mix/copy_mix.h
|
||||
renderer/command/mix/depop_for_mix_buffers.cpp
|
||||
renderer/command/mix/depop_for_mix_buffers.h
|
||||
renderer/command/mix/depop_prepare.cpp
|
||||
renderer/command/mix/depop_prepare.h
|
||||
renderer/command/mix/mix.cpp
|
||||
renderer/command/mix/mix.h
|
||||
renderer/command/mix/mix_ramp.cpp
|
||||
renderer/command/mix/mix_ramp.h
|
||||
renderer/command/mix/mix_ramp_grouped.cpp
|
||||
renderer/command/mix/mix_ramp_grouped.h
|
||||
renderer/command/mix/volume.cpp
|
||||
renderer/command/mix/volume.h
|
||||
renderer/command/mix/volume_ramp.cpp
|
||||
renderer/command/mix/volume_ramp.h
|
||||
renderer/command/performance/performance.cpp
|
||||
renderer/command/performance/performance.h
|
||||
renderer/command/resample/downmix_6ch_to_2ch.cpp
|
||||
renderer/command/resample/downmix_6ch_to_2ch.h
|
||||
renderer/command/resample/resample.h
|
||||
renderer/command/resample/resample.cpp
|
||||
renderer/command/resample/upsample.cpp
|
||||
renderer/command/resample/upsample.h
|
||||
renderer/command/sink/device.cpp
|
||||
renderer/command/sink/device.h
|
||||
renderer/command/sink/circular_buffer.cpp
|
||||
renderer/command/sink/circular_buffer.h
|
||||
renderer/command/command_buffer.cpp
|
||||
renderer/command/command_buffer.h
|
||||
renderer/command/command_generator.cpp
|
||||
renderer/command/command_generator.h
|
||||
renderer/command/command_list_header.h
|
||||
renderer/command/command_processing_time_estimator.cpp
|
||||
renderer/command/command_processing_time_estimator.h
|
||||
renderer/command/commands.h
|
||||
renderer/command/icommand.h
|
||||
renderer/effect/aux_.cpp
|
||||
renderer/effect/aux_.h
|
||||
renderer/effect/biquad_filter.cpp
|
||||
renderer/effect/biquad_filter.h
|
||||
renderer/effect/buffer_mixer.cpp
|
||||
renderer/effect/buffer_mixer.h
|
||||
renderer/effect/capture.cpp
|
||||
renderer/effect/capture.h
|
||||
renderer/effect/compressor.cpp
|
||||
renderer/effect/compressor.h
|
||||
renderer/effect/delay.cpp
|
||||
renderer/effect/delay.h
|
||||
renderer/effect/effect_context.cpp
|
||||
renderer/effect/effect_context.h
|
||||
renderer/effect/effect_info_base.h
|
||||
renderer/effect/effect_reset.h
|
||||
renderer/effect/effect_result_state.h
|
||||
renderer/effect/i3dl2.cpp
|
||||
renderer/effect/i3dl2.h
|
||||
renderer/effect/light_limiter.cpp
|
||||
renderer/effect/light_limiter.h
|
||||
renderer/effect/reverb.h
|
||||
renderer/effect/reverb.cpp
|
||||
renderer/mix/mix_context.cpp
|
||||
renderer/mix/mix_context.h
|
||||
renderer/mix/mix_info.cpp
|
||||
renderer/mix/mix_info.h
|
||||
renderer/memory/address_info.h
|
||||
renderer/memory/memory_pool_info.cpp
|
||||
renderer/memory/memory_pool_info.h
|
||||
renderer/memory/pool_mapper.cpp
|
||||
renderer/memory/pool_mapper.h
|
||||
renderer/nodes/bit_array.h
|
||||
renderer/nodes/edge_matrix.cpp
|
||||
renderer/nodes/edge_matrix.h
|
||||
renderer/nodes/node_states.cpp
|
||||
renderer/nodes/node_states.h
|
||||
renderer/performance/detail_aspect.cpp
|
||||
renderer/performance/detail_aspect.h
|
||||
renderer/performance/entry_aspect.cpp
|
||||
renderer/performance/entry_aspect.h
|
||||
renderer/performance/performance_detail.h
|
||||
renderer/performance/performance_entry.h
|
||||
renderer/performance/performance_entry_addresses.h
|
||||
renderer/performance/performance_frame_header.h
|
||||
renderer/performance/performance_manager.cpp
|
||||
renderer/performance/performance_manager.h
|
||||
renderer/sink/circular_buffer_sink_info.cpp
|
||||
renderer/sink/circular_buffer_sink_info.h
|
||||
renderer/sink/device_sink_info.cpp
|
||||
renderer/sink/device_sink_info.h
|
||||
renderer/sink/sink_context.cpp
|
||||
renderer/sink/sink_context.h
|
||||
renderer/sink/sink_info_base.cpp
|
||||
renderer/sink/sink_info_base.h
|
||||
renderer/splitter/splitter_context.cpp
|
||||
renderer/splitter/splitter_context.h
|
||||
renderer/splitter/splitter_destinations_data.cpp
|
||||
renderer/splitter/splitter_destinations_data.h
|
||||
renderer/splitter/splitter_info.cpp
|
||||
renderer/splitter/splitter_info.h
|
||||
renderer/system.cpp
|
||||
renderer/system.h
|
||||
renderer/system_manager.cpp
|
||||
renderer/system_manager.h
|
||||
renderer/upsampler/upsampler_info.h
|
||||
renderer/upsampler/upsampler_manager.cpp
|
||||
renderer/upsampler/upsampler_manager.h
|
||||
renderer/upsampler/upsampler_state.h
|
||||
renderer/voice/voice_channel_resource.h
|
||||
renderer/voice/voice_context.cpp
|
||||
renderer/voice/voice_context.h
|
||||
renderer/voice/voice_info.cpp
|
||||
renderer/voice/voice_info.h
|
||||
renderer/voice/voice_state.h
|
||||
sink/cubeb_sink.cpp
|
||||
sink/cubeb_sink.h
|
||||
sink/null_sink.h
|
||||
sink/sdl2_sink.cpp
|
||||
sink/sdl2_sink.h
|
||||
sink/sink.h
|
||||
sink/sink_details.cpp
|
||||
sink/sink_details.h
|
||||
sink/sink_stream.h
|
||||
algorithm/filter.cpp
|
||||
algorithm/filter.h
|
||||
algorithm/interpolate.cpp
|
||||
algorithm/interpolate.h
|
||||
audio_out.cpp
|
||||
audio_out.h
|
||||
audio_renderer.cpp
|
||||
audio_renderer.h
|
||||
behavior_info.cpp
|
||||
behavior_info.h
|
||||
buffer.h
|
||||
codec.cpp
|
||||
codec.h
|
||||
command_generator.cpp
|
||||
command_generator.h
|
||||
common.h
|
||||
delay_line.cpp
|
||||
delay_line.h
|
||||
effect_context.cpp
|
||||
effect_context.h
|
||||
info_updater.cpp
|
||||
info_updater.h
|
||||
memory_pool.cpp
|
||||
memory_pool.h
|
||||
mix_context.cpp
|
||||
mix_context.h
|
||||
null_sink.h
|
||||
sink.h
|
||||
sink_context.cpp
|
||||
sink_context.h
|
||||
sink_details.cpp
|
||||
sink_details.h
|
||||
sink_stream.h
|
||||
splitter_context.cpp
|
||||
splitter_context.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
time_stretch.cpp
|
||||
time_stretch.h
|
||||
voice_context.cpp
|
||||
voice_context.h
|
||||
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||
$<$<BOOL:${ENABLE_SDL2}>:sdl2_sink.cpp sdl2_sink.h>
|
||||
)
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
if (MSVC)
|
||||
target_compile_options(audio_core PRIVATE
|
||||
/we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4244 # 'conversion': conversion from 'type1' to 'type2', possible loss of data
|
||||
/we4245 # 'conversion': conversion from 'type1' to 'type2', signed/unsigned mismatch
|
||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||
/we4456 # Declaration of 'identifier' hides previous local declaration
|
||||
/we4457 # Declaration of 'identifier' hides function parameter
|
||||
/we4458 # Declaration of 'identifier' hides class member
|
||||
/we4459 # Declaration of 'identifier' hides global declaration
|
||||
)
|
||||
else()
|
||||
if (NOT MSVC)
|
||||
target_compile_options(audio_core PRIVATE
|
||||
-Werror=conversion
|
||||
-Werror=ignored-qualifiers
|
||||
-Werror=shadow
|
||||
-Werror=unused-parameter
|
||||
-Werror=unused-variable
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
|
||||
@@ -222,9 +63,7 @@ else()
|
||||
endif()
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_link_libraries(audio_core PRIVATE dynarmic)
|
||||
endif()
|
||||
target_link_libraries(audio_core PRIVATE SoundTouch)
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 auto pole =
|
||||
M_PI * static_cast<double>(2 * index + 1) / (4.0 * static_cast<double>(total_count));
|
||||
return 1.0 / (2.0 * std::cos(pole));
|
||||
}
|
||||
|
||||
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
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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
|
||||
@@ -0,0 +1,233 @@
|
||||
// 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 <climits>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut0{
|
||||
6600, 19426, 6722, 3, 6479, 19424, 6845, 9, 6359, 19419, 6968, 15, 6239,
|
||||
19412, 7093, 22, 6121, 19403, 7219, 28, 6004, 19391, 7345, 34, 5888, 19377,
|
||||
7472, 41, 5773, 19361, 7600, 48, 5659, 19342, 7728, 55, 5546, 19321, 7857,
|
||||
62, 5434, 19298, 7987, 69, 5323, 19273, 8118, 77, 5213, 19245, 8249, 84,
|
||||
5104, 19215, 8381, 92, 4997, 19183, 8513, 101, 4890, 19148, 8646, 109, 4785,
|
||||
19112, 8780, 118, 4681, 19073, 8914, 127, 4579, 19031, 9048, 137, 4477, 18988,
|
||||
9183, 147, 4377, 18942, 9318, 157, 4277, 18895, 9454, 168, 4179, 18845, 9590,
|
||||
179, 4083, 18793, 9726, 190, 3987, 18738, 9863, 202, 3893, 18682, 10000, 215,
|
||||
3800, 18624, 10137, 228, 3709, 18563, 10274, 241, 3618, 18500, 10411, 255, 3529,
|
||||
18436, 10549, 270, 3441, 18369, 10687, 285, 3355, 18300, 10824, 300, 3269, 18230,
|
||||
10962, 317, 3186, 18157, 11100, 334, 3103, 18082, 11238, 351, 3022, 18006, 11375,
|
||||
369, 2942, 17927, 11513, 388, 2863, 17847, 11650, 408, 2785, 17765, 11788, 428,
|
||||
2709, 17681, 11925, 449, 2635, 17595, 12062, 471, 2561, 17507, 12198, 494, 2489,
|
||||
17418, 12334, 517, 2418, 17327, 12470, 541, 2348, 17234, 12606, 566, 2280, 17140,
|
||||
12741, 592, 2213, 17044, 12876, 619, 2147, 16946, 13010, 647, 2083, 16846, 13144,
|
||||
675, 2020, 16745, 13277, 704, 1958, 16643, 13409, 735, 1897, 16539, 13541, 766,
|
||||
1838, 16434, 13673, 798, 1780, 16327, 13803, 832, 1723, 16218, 13933, 866, 1667,
|
||||
16109, 14062, 901, 1613, 15998, 14191, 937, 1560, 15885, 14318, 975, 1508, 15772,
|
||||
14445, 1013, 1457, 15657, 14571, 1052, 1407, 15540, 14695, 1093, 1359, 15423, 14819,
|
||||
1134, 1312, 15304, 14942, 1177, 1266, 15185, 15064, 1221, 1221, 15064, 15185, 1266,
|
||||
1177, 14942, 15304, 1312, 1134, 14819, 15423, 1359, 1093, 14695, 15540, 1407, 1052,
|
||||
14571, 15657, 1457, 1013, 14445, 15772, 1508, 975, 14318, 15885, 1560, 937, 14191,
|
||||
15998, 1613, 901, 14062, 16109, 1667, 866, 13933, 16218, 1723, 832, 13803, 16327,
|
||||
1780, 798, 13673, 16434, 1838, 766, 13541, 16539, 1897, 735, 13409, 16643, 1958,
|
||||
704, 13277, 16745, 2020, 675, 13144, 16846, 2083, 647, 13010, 16946, 2147, 619,
|
||||
12876, 17044, 2213, 592, 12741, 17140, 2280, 566, 12606, 17234, 2348, 541, 12470,
|
||||
17327, 2418, 517, 12334, 17418, 2489, 494, 12198, 17507, 2561, 471, 12062, 17595,
|
||||
2635, 449, 11925, 17681, 2709, 428, 11788, 17765, 2785, 408, 11650, 17847, 2863,
|
||||
388, 11513, 17927, 2942, 369, 11375, 18006, 3022, 351, 11238, 18082, 3103, 334,
|
||||
11100, 18157, 3186, 317, 10962, 18230, 3269, 300, 10824, 18300, 3355, 285, 10687,
|
||||
18369, 3441, 270, 10549, 18436, 3529, 255, 10411, 18500, 3618, 241, 10274, 18563,
|
||||
3709, 228, 10137, 18624, 3800, 215, 10000, 18682, 3893, 202, 9863, 18738, 3987,
|
||||
190, 9726, 18793, 4083, 179, 9590, 18845, 4179, 168, 9454, 18895, 4277, 157,
|
||||
9318, 18942, 4377, 147, 9183, 18988, 4477, 137, 9048, 19031, 4579, 127, 8914,
|
||||
19073, 4681, 118, 8780, 19112, 4785, 109, 8646, 19148, 4890, 101, 8513, 19183,
|
||||
4997, 92, 8381, 19215, 5104, 84, 8249, 19245, 5213, 77, 8118, 19273, 5323,
|
||||
69, 7987, 19298, 5434, 62, 7857, 19321, 5546, 55, 7728, 19342, 5659, 48,
|
||||
7600, 19361, 5773, 41, 7472, 19377, 5888, 34, 7345, 19391, 6004, 28, 7219,
|
||||
19403, 6121, 22, 7093, 19412, 6239, 15, 6968, 19419, 6359, 9, 6845, 19424,
|
||||
6479, 3, 6722, 19426, 6600};
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut1{
|
||||
-68, 32639, 69, -5, -200, 32630, 212, -15, -328, 32613, 359, -26, -450,
|
||||
32586, 512, -36, -568, 32551, 669, -47, -680, 32507, 832, -58, -788, 32454,
|
||||
1000, -69, -891, 32393, 1174, -80, -990, 32323, 1352, -92, -1084, 32244, 1536,
|
||||
-103, -1173, 32157, 1724, -115, -1258, 32061, 1919, -128, -1338, 31956, 2118, -140,
|
||||
-1414, 31844, 2322, -153, -1486, 31723, 2532, -167, -1554, 31593, 2747, -180, -1617,
|
||||
31456, 2967, -194, -1676, 31310, 3192, -209, -1732, 31157, 3422, -224, -1783, 30995,
|
||||
3657, -240, -1830, 30826, 3897, -256, -1874, 30649, 4143, -272, -1914, 30464, 4393,
|
||||
-289, -1951, 30272, 4648, -307, -1984, 30072, 4908, -325, -2014, 29866, 5172, -343,
|
||||
-2040, 29652, 5442, -362, -2063, 29431, 5716, -382, -2083, 29203, 5994, -403, -2100,
|
||||
28968, 6277, -424, -2114, 28727, 6565, -445, -2125, 28480, 6857, -468, -2133, 28226,
|
||||
7153, -490, -2139, 27966, 7453, -514, -2142, 27700, 7758, -538, -2142, 27428, 8066,
|
||||
-563, -2141, 27151, 8378, -588, -2136, 26867, 8694, -614, -2130, 26579, 9013, -641,
|
||||
-2121, 26285, 9336, -668, -2111, 25987, 9663, -696, -2098, 25683, 9993, -724, -2084,
|
||||
25375, 10326, -753, -2067, 25063, 10662, -783, -2049, 24746, 11000, -813, -2030, 24425,
|
||||
11342, -844, -2009, 24100, 11686, -875, -1986, 23771, 12033, -907, -1962, 23438, 12382,
|
||||
-939, -1937, 23103, 12733, -972, -1911, 22764, 13086, -1005, -1883, 22422, 13441, -1039,
|
||||
-1855, 22077, 13798, -1072, -1825, 21729, 14156, -1107, -1795, 21380, 14516, -1141, -1764,
|
||||
21027, 14877, -1176, -1732, 20673, 15239, -1211, -1700, 20317, 15602, -1246, -1667, 19959,
|
||||
15965, -1282, -1633, 19600, 16329, -1317, -1599, 19239, 16694, -1353, -1564, 18878, 17058,
|
||||
-1388, -1530, 18515, 17423, -1424, -1495, 18151, 17787, -1459, -1459, 17787, 18151, -1495,
|
||||
-1424, 17423, 18515, -1530, -1388, 17058, 18878, -1564, -1353, 16694, 19239, -1599, -1317,
|
||||
16329, 19600, -1633, -1282, 15965, 19959, -1667, -1246, 15602, 20317, -1700, -1211, 15239,
|
||||
20673, -1732, -1176, 14877, 21027, -1764, -1141, 14516, 21380, -1795, -1107, 14156, 21729,
|
||||
-1825, -1072, 13798, 22077, -1855, -1039, 13441, 22422, -1883, -1005, 13086, 22764, -1911,
|
||||
-972, 12733, 23103, -1937, -939, 12382, 23438, -1962, -907, 12033, 23771, -1986, -875,
|
||||
11686, 24100, -2009, -844, 11342, 24425, -2030, -813, 11000, 24746, -2049, -783, 10662,
|
||||
25063, -2067, -753, 10326, 25375, -2084, -724, 9993, 25683, -2098, -696, 9663, 25987,
|
||||
-2111, -668, 9336, 26285, -2121, -641, 9013, 26579, -2130, -614, 8694, 26867, -2136,
|
||||
-588, 8378, 27151, -2141, -563, 8066, 27428, -2142, -538, 7758, 27700, -2142, -514,
|
||||
7453, 27966, -2139, -490, 7153, 28226, -2133, -468, 6857, 28480, -2125, -445, 6565,
|
||||
28727, -2114, -424, 6277, 28968, -2100, -403, 5994, 29203, -2083, -382, 5716, 29431,
|
||||
-2063, -362, 5442, 29652, -2040, -343, 5172, 29866, -2014, -325, 4908, 30072, -1984,
|
||||
-307, 4648, 30272, -1951, -289, 4393, 30464, -1914, -272, 4143, 30649, -1874, -256,
|
||||
3897, 30826, -1830, -240, 3657, 30995, -1783, -224, 3422, 31157, -1732, -209, 3192,
|
||||
31310, -1676, -194, 2967, 31456, -1617, -180, 2747, 31593, -1554, -167, 2532, 31723,
|
||||
-1486, -153, 2322, 31844, -1414, -140, 2118, 31956, -1338, -128, 1919, 32061, -1258,
|
||||
-115, 1724, 32157, -1173, -103, 1536, 32244, -1084, -92, 1352, 32323, -990, -80,
|
||||
1174, 32393, -891, -69, 1000, 32454, -788, -58, 832, 32507, -680, -47, 669,
|
||||
32551, -568, -36, 512, 32586, -450, -26, 359, 32613, -328, -15, 212, 32630,
|
||||
-200, -5, 69, 32639, -68};
|
||||
|
||||
constexpr std::array<s16, 512> curve_lut2{
|
||||
3195, 26287, 3329, -32, 3064, 26281, 3467, -34, 2936, 26270, 3608, -38, 2811,
|
||||
26253, 3751, -42, 2688, 26230, 3897, -46, 2568, 26202, 4046, -50, 2451, 26169,
|
||||
4199, -54, 2338, 26130, 4354, -58, 2227, 26085, 4512, -63, 2120, 26035, 4673,
|
||||
-67, 2015, 25980, 4837, -72, 1912, 25919, 5004, -76, 1813, 25852, 5174, -81,
|
||||
1716, 25780, 5347, -87, 1622, 25704, 5522, -92, 1531, 25621, 5701, -98, 1442,
|
||||
25533, 5882, -103, 1357, 25440, 6066, -109, 1274, 25342, 6253, -115, 1193, 25239,
|
||||
6442, -121, 1115, 25131, 6635, -127, 1040, 25018, 6830, -133, 967, 24899, 7027,
|
||||
-140, 897, 24776, 7227, -146, 829, 24648, 7430, -153, 764, 24516, 7635, -159,
|
||||
701, 24379, 7842, -166, 641, 24237, 8052, -174, 583, 24091, 8264, -181, 526,
|
||||
23940, 8478, -187, 472, 23785, 8695, -194, 420, 23626, 8914, -202, 371, 23462,
|
||||
9135, -209, 324, 23295, 9358, -215, 279, 23123, 9583, -222, 236, 22948, 9809,
|
||||
-230, 194, 22769, 10038, -237, 154, 22586, 10269, -243, 117, 22399, 10501, -250,
|
||||
81, 22208, 10735, -258, 47, 22015, 10970, -265, 15, 21818, 11206, -271, -16,
|
||||
21618, 11444, -277, -44, 21415, 11684, -283, -71, 21208, 11924, -290, -97, 20999,
|
||||
12166, -296, -121, 20786, 12409, -302, -143, 20571, 12653, -306, -163, 20354, 12898,
|
||||
-311, -183, 20134, 13143, -316, -201, 19911, 13389, -321, -218, 19686, 13635, -325,
|
||||
-234, 19459, 13882, -328, -248, 19230, 14130, -332, -261, 18998, 14377, -335, -273,
|
||||
18765, 14625, -337, -284, 18531, 14873, -339, -294, 18295, 15121, -341, -302, 18057,
|
||||
15369, -341, -310, 17817, 15617, -341, -317, 17577, 15864, -340, -323, 17335, 16111,
|
||||
-340, -328, 17092, 16357, -338, -332, 16848, 16603, -336, -336, 16603, 16848, -332,
|
||||
-338, 16357, 17092, -328, -340, 16111, 17335, -323, -340, 15864, 17577, -317, -341,
|
||||
15617, 17817, -310, -341, 15369, 18057, -302, -341, 15121, 18295, -294, -339, 14873,
|
||||
18531, -284, -337, 14625, 18765, -273, -335, 14377, 18998, -261, -332, 14130, 19230,
|
||||
-248, -328, 13882, 19459, -234, -325, 13635, 19686, -218, -321, 13389, 19911, -201,
|
||||
-316, 13143, 20134, -183, -311, 12898, 20354, -163, -306, 12653, 20571, -143, -302,
|
||||
12409, 20786, -121, -296, 12166, 20999, -97, -290, 11924, 21208, -71, -283, 11684,
|
||||
21415, -44, -277, 11444, 21618, -16, -271, 11206, 21818, 15, -265, 10970, 22015,
|
||||
47, -258, 10735, 22208, 81, -250, 10501, 22399, 117, -243, 10269, 22586, 154,
|
||||
-237, 10038, 22769, 194, -230, 9809, 22948, 236, -222, 9583, 23123, 279, -215,
|
||||
9358, 23295, 324, -209, 9135, 23462, 371, -202, 8914, 23626, 420, -194, 8695,
|
||||
23785, 472, -187, 8478, 23940, 526, -181, 8264, 24091, 583, -174, 8052, 24237,
|
||||
641, -166, 7842, 24379, 701, -159, 7635, 24516, 764, -153, 7430, 24648, 829,
|
||||
-146, 7227, 24776, 897, -140, 7027, 24899, 967, -133, 6830, 25018, 1040, -127,
|
||||
6635, 25131, 1115, -121, 6442, 25239, 1193, -115, 6253, 25342, 1274, -109, 6066,
|
||||
25440, 1357, -103, 5882, 25533, 1442, -98, 5701, 25621, 1531, -92, 5522, 25704,
|
||||
1622, -87, 5347, 25780, 1716, -81, 5174, 25852, 1813, -76, 5004, 25919, 1912,
|
||||
-72, 4837, 25980, 2015, -67, 4673, 26035, 2120, -63, 4512, 26085, 2227, -58,
|
||||
4354, 26130, 2338, -54, 4199, 26169, 2451, -50, 4046, 26202, 2568, -46, 3897,
|
||||
26230, 2688, -42, 3751, 26253, 2811, -38, 3608, 26270, 2936, -34, 3467, 26281,
|
||||
3064, -32, 3329, 26287, 3195};
|
||||
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
|
||||
if (input.size() < 2)
|
||||
return {};
|
||||
|
||||
if (ratio <= 0) {
|
||||
LOG_ERROR(Audio, "Nonsensical interpolation ratio {}", ratio);
|
||||
return input;
|
||||
}
|
||||
|
||||
const s32 step{static_cast<s32>(ratio * 0x8000)};
|
||||
const std::array<s16, 512>& lut = [step] {
|
||||
if (step > 0xaaaa) {
|
||||
return curve_lut0;
|
||||
}
|
||||
if (step <= 0x8000) {
|
||||
return curve_lut1;
|
||||
}
|
||||
return curve_lut2;
|
||||
}();
|
||||
|
||||
const std::size_t num_frames{input.size() / 2};
|
||||
|
||||
std::vector<s16> output;
|
||||
output.reserve(static_cast<std::size_t>(static_cast<double>(input.size()) / ratio +
|
||||
InterpolationState::taps));
|
||||
|
||||
for (std::size_t frame{}; frame < num_frames; ++frame) {
|
||||
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
|
||||
|
||||
std::rotate(state.history.begin(), state.history.end() - 1, state.history.end());
|
||||
state.history[0][0] = input[frame * 2 + 0];
|
||||
state.history[0][1] = input[frame * 2 + 1];
|
||||
|
||||
while (state.position <= 1.0) {
|
||||
const s32 left{state.history[0][0] * lut[lut_index + 0] +
|
||||
state.history[1][0] * lut[lut_index + 1] +
|
||||
state.history[2][0] * lut[lut_index + 2] +
|
||||
state.history[3][0] * lut[lut_index + 3]};
|
||||
const s32 right{state.history[0][1] * lut[lut_index + 0] +
|
||||
state.history[1][1] * lut[lut_index + 1] +
|
||||
state.history[2][1] * lut[lut_index + 2] +
|
||||
state.history[3][1] * lut[lut_index + 3]};
|
||||
const s32 new_offset{state.fraction + step};
|
||||
|
||||
state.fraction = new_offset & 0x7fff;
|
||||
|
||||
output.emplace_back(static_cast<s16>(std::clamp(left >> 15, SHRT_MIN, SHRT_MAX)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(right >> 15, SHRT_MIN, SHRT_MAX)));
|
||||
|
||||
state.position += ratio;
|
||||
}
|
||||
state.position -= 1.0;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count) {
|
||||
const std::array<s16, 512>& lut = [pitch] {
|
||||
if (pitch > 0xaaaa) {
|
||||
return curve_lut0;
|
||||
}
|
||||
if (pitch <= 0x8000) {
|
||||
return curve_lut1;
|
||||
}
|
||||
return curve_lut2;
|
||||
}();
|
||||
|
||||
std::size_t index{};
|
||||
|
||||
for (std::size_t i = 0; i < sample_count; i++) {
|
||||
const std::size_t lut_index{(static_cast<std::size_t>(fraction) >> 8) * 4};
|
||||
const auto l0 = lut[lut_index + 0];
|
||||
const auto l1 = lut[lut_index + 1];
|
||||
const auto l2 = lut[lut_index + 2];
|
||||
const auto l3 = lut[lut_index + 3];
|
||||
|
||||
const auto s0 = static_cast<s32>(input[index + 0]);
|
||||
const auto s1 = static_cast<s32>(input[index + 1]);
|
||||
const auto s2 = static_cast<s32>(input[index + 2]);
|
||||
const auto s3 = static_cast<s32>(input[index + 3]);
|
||||
|
||||
output[i] = (l0 * s0 + l1 * s1 + l2 * s2 + l3 * s3) >> 15;
|
||||
fraction += pitch;
|
||||
index += (fraction >> 15);
|
||||
fraction &= 0x7fff;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 {
|
||||
|
||||
struct InterpolationState {
|
||||
static constexpr std::size_t taps{4};
|
||||
static constexpr std::size_t history_size{taps * 2 - 1};
|
||||
std::array<std::array<s16, 2>, history_size> history{};
|
||||
double position{};
|
||||
s32 fraction{};
|
||||
};
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
/// Nintendo Switchs DSP resampling algorithm. Based on a single channel
|
||||
void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count);
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,68 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/sink/sink_details.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioCore::AudioCore(Core::System& system) : audio_manager{std::make_unique<AudioManager>(system)} {
|
||||
CreateSinks();
|
||||
// Must be created after the sinks
|
||||
adsp = std::make_unique<AudioRenderer::ADSP::ADSP>(system, *output_sink);
|
||||
}
|
||||
|
||||
AudioCore ::~AudioCore() {
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void AudioCore::CreateSinks() {
|
||||
const auto& sink_id{Settings::values.sink_id};
|
||||
const auto& audio_output_device_id{Settings::values.audio_output_device_id};
|
||||
const auto& audio_input_device_id{Settings::values.audio_input_device_id};
|
||||
|
||||
output_sink = Sink::CreateSinkFromID(sink_id.GetValue(), audio_output_device_id.GetValue());
|
||||
input_sink = Sink::CreateSinkFromID(sink_id.GetValue(), audio_input_device_id.GetValue());
|
||||
}
|
||||
|
||||
void AudioCore::Shutdown() {
|
||||
audio_manager->Shutdown();
|
||||
}
|
||||
|
||||
AudioManager& AudioCore::GetAudioManager() {
|
||||
return *audio_manager;
|
||||
}
|
||||
|
||||
Sink::Sink& AudioCore::GetOutputSink() {
|
||||
return *output_sink;
|
||||
}
|
||||
|
||||
Sink::Sink& AudioCore::GetInputSink() {
|
||||
return *input_sink;
|
||||
}
|
||||
|
||||
AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() {
|
||||
return *adsp;
|
||||
}
|
||||
|
||||
void AudioCore::PauseSinks(const bool pausing) const {
|
||||
if (pausing) {
|
||||
output_sink->PauseStreams();
|
||||
input_sink->PauseStreams();
|
||||
} else {
|
||||
output_sink->UnpauseStreams();
|
||||
input_sink->UnpauseStreams();
|
||||
}
|
||||
}
|
||||
|
||||
u32 AudioCore::GetStreamQueue() const {
|
||||
return estimated_queue.load();
|
||||
}
|
||||
|
||||
void AudioCore::SetStreamQueue(u32 size) {
|
||||
estimated_queue.store(size);
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,100 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "audio_core/renderer/adsp/adsp.h"
|
||||
#include "audio_core/sink/sink.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioManager;
|
||||
/**
|
||||
* Main audio class, sotred inside the core, and holding the audio manager, all sinks, and the ADSP.
|
||||
*/
|
||||
class AudioCore {
|
||||
public:
|
||||
explicit AudioCore(Core::System& system);
|
||||
~AudioCore();
|
||||
|
||||
/**
|
||||
* Shutdown the audio core.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Get a reference to the audio manager.
|
||||
*
|
||||
* @return Ref to the audio manager.
|
||||
*/
|
||||
AudioManager& GetAudioManager();
|
||||
|
||||
/**
|
||||
* Get the audio output sink currently in use.
|
||||
*
|
||||
* @return Ref to the sink.
|
||||
*/
|
||||
Sink::Sink& GetOutputSink();
|
||||
|
||||
/**
|
||||
* Get the audio input sink currently in use.
|
||||
*
|
||||
* @return Ref to the sink.
|
||||
*/
|
||||
Sink::Sink& GetInputSink();
|
||||
|
||||
/**
|
||||
* Get the ADSP.
|
||||
*
|
||||
* @return Ref to the ADSP.
|
||||
*/
|
||||
AudioRenderer::ADSP::ADSP& GetADSP();
|
||||
|
||||
/**
|
||||
* Pause the sink. Called from the core.
|
||||
*
|
||||
* @param pausing - Is this pause due to an actual pause, or shutdown?
|
||||
* Unfortunately, shutdown also pauses streams, which can cause issues.
|
||||
*/
|
||||
void PauseSinks(bool pausing) const;
|
||||
|
||||
/**
|
||||
* Get the size of the current stream queue.
|
||||
*
|
||||
* @return Current stream queue size.
|
||||
*/
|
||||
u32 GetStreamQueue() const;
|
||||
|
||||
/**
|
||||
* Get the size of the current stream queue.
|
||||
*
|
||||
* @param size - New stream size.
|
||||
*/
|
||||
void SetStreamQueue(u32 size);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create the sinks on startup.
|
||||
*/
|
||||
void CreateSinks();
|
||||
|
||||
/// Main audio manager for audio in/out
|
||||
std::unique_ptr<AudioManager> audio_manager;
|
||||
/// Sink used for audio renderer and audio out
|
||||
std::unique_ptr<Sink::Sink> output_sink;
|
||||
/// Sink used for audio input
|
||||
std::unique_ptr<Sink::Sink> input_sink;
|
||||
/// The ADSP in the sysmodule
|
||||
std::unique_ptr<AudioRenderer::ADSP::ADSP> adsp;
|
||||
/// Current size of the stream queue
|
||||
std::atomic<u32> estimated_queue{0};
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,61 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_event.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
size_t Event::GetManagerIndex(const Type type) const {
|
||||
switch (type) {
|
||||
case Type::AudioInManager:
|
||||
return 0;
|
||||
case Type::AudioOutManager:
|
||||
return 1;
|
||||
case Type::FinalOutputRecorderManager:
|
||||
return 2;
|
||||
case Type::Max:
|
||||
return 3;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
void Event::SetAudioEvent(const Type type, const bool signalled) {
|
||||
events_signalled[GetManagerIndex(type)] = signalled;
|
||||
if (signalled) {
|
||||
manager_event.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool Event::CheckAudioEventSet(const Type type) const {
|
||||
return events_signalled[GetManagerIndex(type)];
|
||||
}
|
||||
|
||||
std::mutex& Event::GetAudioEventLock() {
|
||||
return event_lock;
|
||||
}
|
||||
|
||||
std::condition_variable_any& Event::GetAudioEvent() {
|
||||
return manager_event;
|
||||
}
|
||||
|
||||
bool Event::Wait(std::unique_lock<std::mutex>& l, const std::chrono::seconds timeout) {
|
||||
bool timed_out{false};
|
||||
if (!manager_event.wait_for(l, timeout, [&]() {
|
||||
return std::ranges::any_of(events_signalled, [](bool x) { return x; });
|
||||
})) {
|
||||
timed_out = true;
|
||||
}
|
||||
return timed_out;
|
||||
}
|
||||
|
||||
void Event::ClearEvents() {
|
||||
events_signalled[0] = false;
|
||||
events_signalled[1] = false;
|
||||
events_signalled[2] = false;
|
||||
events_signalled[3] = false;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,92 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace AudioCore {
|
||||
/**
|
||||
* Responsible for the input/output events, set by the stream backend when buffers are consumed, and
|
||||
* waited on by the audio manager. These callbacks signal the game's events to keep the audio buffer
|
||||
* recycling going.
|
||||
* In a real Switch this is not a seprate class, and exists entirely within the audio manager.
|
||||
* On the Switch it's implemented more simply through a MultiWaitEventHolder, where it can
|
||||
* wait on multiple events at once, and the events are not needed by the backend.
|
||||
*/
|
||||
class Event {
|
||||
public:
|
||||
enum class Type {
|
||||
AudioInManager,
|
||||
AudioOutManager,
|
||||
FinalOutputRecorderManager,
|
||||
Max,
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a manager type to an index.
|
||||
*
|
||||
* @param type - The manager type to convert
|
||||
* @return The index of the type.
|
||||
*/
|
||||
size_t GetManagerIndex(Type type) const;
|
||||
|
||||
/**
|
||||
* Set an audio event to true or false.
|
||||
*
|
||||
* @param type - The manager type to signal.
|
||||
* @param signalled - Its signal state.
|
||||
*/
|
||||
void SetAudioEvent(Type type, bool signalled);
|
||||
|
||||
/**
|
||||
* Check if the given manager type is signalled.
|
||||
*
|
||||
* @param type - The manager type to check.
|
||||
* @return True if the event is signalled, otherwise false.
|
||||
*/
|
||||
bool CheckAudioEventSet(Type type) const;
|
||||
|
||||
/**
|
||||
* Get the lock for audio events.
|
||||
*
|
||||
* @return Reference to the lock.
|
||||
*/
|
||||
std::mutex& GetAudioEventLock();
|
||||
|
||||
/**
|
||||
* Get the manager event, this signals the audio manager to release buffers and signal the game
|
||||
* for more.
|
||||
*
|
||||
* @return Reference to the condition variable.
|
||||
*/
|
||||
std::condition_variable_any& GetAudioEvent();
|
||||
|
||||
/**
|
||||
* Wait on the manager_event.
|
||||
*
|
||||
* @param l - Lock held by the wait.
|
||||
* @param timeout - Timeout for the wait. This is 2 seconds by default.
|
||||
* @return True if the wait timed out, otherwise false if signalled.
|
||||
*/
|
||||
bool Wait(std::unique_lock<std::mutex>& l, std::chrono::seconds timeout);
|
||||
|
||||
/**
|
||||
* Reset all manager events.
|
||||
*/
|
||||
void ClearEvents();
|
||||
|
||||
private:
|
||||
/// Lock, used bythe audio manager
|
||||
std::mutex event_lock;
|
||||
/// Array of events, one per system type (see Type), last event is used to terminate
|
||||
std::array<std::atomic<bool>, 4> events_signalled;
|
||||
/// Event to signal the audio manager
|
||||
std::condition_variable_any manager_event;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,91 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/audio_in_manager.h"
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "audio_core/in/audio_in.h"
|
||||
#include "audio_core/sink/sink_details.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore::AudioIn {
|
||||
|
||||
Manager::Manager(Core::System& system_) : system{system_} {
|
||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||
num_free_sessions = MaxInSessions;
|
||||
}
|
||||
|
||||
Result Manager::AcquireSessionId(size_t& session_id) {
|
||||
if (num_free_sessions == 0) {
|
||||
LOG_ERROR(Service_Audio, "All 4 AudioIn sessions are in use, cannot create any more");
|
||||
return Service::Audio::ERR_MAXIMUM_SESSIONS_REACHED;
|
||||
}
|
||||
session_id = session_ids[next_session_id];
|
||||
next_session_id = (next_session_id + 1) % MaxInSessions;
|
||||
num_free_sessions--;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
std::scoped_lock l{mutex};
|
||||
LOG_DEBUG(Service_Audio, "Freeing AudioIn session {}", session_id);
|
||||
session_ids[free_session_id] = session_id;
|
||||
num_free_sessions++;
|
||||
free_session_id = (free_session_id + 1) % MaxInSessions;
|
||||
sessions[session_id].reset();
|
||||
applet_resource_user_ids[session_id] = 0;
|
||||
}
|
||||
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetInManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Manager::Start() {
|
||||
if (sessions_started) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock l{mutex};
|
||||
for (auto& session : sessions) {
|
||||
if (session) {
|
||||
session->StartSession();
|
||||
}
|
||||
}
|
||||
|
||||
sessions_started = true;
|
||||
}
|
||||
|
||||
void Manager::BufferReleaseAndRegister() {
|
||||
std::scoped_lock l{mutex};
|
||||
for (auto& session : sessions) {
|
||||
if (session != nullptr) {
|
||||
session->ReleaseAndRegisterBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 Manager::GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names,
|
||||
[[maybe_unused]] const u32 max_count,
|
||||
[[maybe_unused]] const bool filter) {
|
||||
std::scoped_lock l{mutex};
|
||||
|
||||
LinkToManager();
|
||||
|
||||
auto input_devices{Sink::GetDeviceListForSink(Settings::values.sink_id.GetValue(), true)};
|
||||
if (input_devices.size() > 1) {
|
||||
names.push_back(AudioRenderer::AudioDevice::AudioDeviceName("Uac"));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioIn
|
||||
@@ -1,92 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore::AudioIn {
|
||||
class In;
|
||||
|
||||
constexpr size_t MaxInSessions = 4;
|
||||
/**
|
||||
* Manages all audio in sessions.
|
||||
*/
|
||||
class Manager {
|
||||
public:
|
||||
explicit Manager(Core::System& system);
|
||||
|
||||
/**
|
||||
* Acquire a free session id for opening a new audio in.
|
||||
*
|
||||
* @param session_id - Output session_id.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result AcquireSessionId(size_t& session_id);
|
||||
|
||||
/**
|
||||
* Release a session id on close.
|
||||
*
|
||||
* @param session_id - Session id to free.
|
||||
*/
|
||||
void ReleaseSessionId(size_t session_id);
|
||||
|
||||
/**
|
||||
* Link the audio in manager to the main audio manager.
|
||||
*
|
||||
* @return Result code.
|
||||
*/
|
||||
Result LinkToManager();
|
||||
|
||||
/**
|
||||
* Start the audio in manager.
|
||||
*/
|
||||
void Start();
|
||||
|
||||
/**
|
||||
* Callback function, called by the audio manager when the audio in event is signalled.
|
||||
*/
|
||||
void BufferReleaseAndRegister();
|
||||
|
||||
/**
|
||||
* Get a list of audio in device names.
|
||||
*
|
||||
* @oaram names - Output container to write names to.
|
||||
* @param max_count - Maximum numebr of deivce names to write. Unused
|
||||
* @param filter - Should the list be filtered? Unused.
|
||||
* @return Number of names written.
|
||||
*/
|
||||
u32 GetDeviceNames(std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names,
|
||||
u32 max_count, bool filter);
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// Array of session ids
|
||||
std::array<size_t, MaxInSessions> session_ids{};
|
||||
/// Array of resource user ids
|
||||
std::array<size_t, MaxInSessions> applet_resource_user_ids{};
|
||||
/// Pointer to each open session
|
||||
std::array<std::shared_ptr<In>, MaxInSessions> sessions{};
|
||||
/// The number of free sessions
|
||||
size_t num_free_sessions{};
|
||||
/// The next session id to be taken
|
||||
size_t next_session_id{};
|
||||
/// The next session id to be freed
|
||||
size_t free_session_id{};
|
||||
/// Whether this is linked to the audio manager
|
||||
bool linked_to_manager{};
|
||||
/// Whether the sessions have been started
|
||||
bool sessions_started{};
|
||||
/// Protect state due to audio manager callback
|
||||
std::recursive_mutex mutex{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioIn
|
||||
@@ -1,80 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_in_manager.h"
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
AudioManager::AudioManager(Core::System& system_) : system{system_} {
|
||||
thread = std::jthread([this]() { ThreadFunc(); });
|
||||
}
|
||||
|
||||
void AudioManager::Shutdown() {
|
||||
running = false;
|
||||
events.SetAudioEvent(Event::Type::Max, true);
|
||||
thread.join();
|
||||
}
|
||||
|
||||
Result AudioManager::SetOutManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioOutManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = buffer_func;
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioOutManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result AudioManager::SetInManager(BufferEventFunc buffer_func) {
|
||||
if (!running) {
|
||||
return Service::Audio::ERR_OPERATION_FAILED;
|
||||
}
|
||||
|
||||
std::scoped_lock l{lock};
|
||||
|
||||
const auto index{events.GetManagerIndex(Event::Type::AudioInManager)};
|
||||
if (buffer_events[index] == nullptr) {
|
||||
buffer_events[index] = buffer_func;
|
||||
needs_update = true;
|
||||
events.SetAudioEvent(Event::Type::AudioInManager, true);
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void AudioManager::SetEvent(const Event::Type type, const bool signalled) {
|
||||
events.SetAudioEvent(type, signalled);
|
||||
}
|
||||
|
||||
void AudioManager::ThreadFunc() {
|
||||
std::unique_lock l{events.GetAudioEventLock()};
|
||||
events.ClearEvents();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
auto timed_out{events.Wait(l, std::chrono::seconds(2))};
|
||||
|
||||
if (events.CheckAudioEventSet(Event::Type::Max)) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < buffer_events.size(); i++) {
|
||||
if (events.CheckAudioEventSet(Event::Type(i)) || timed_out) {
|
||||
if (buffer_events[i]) {
|
||||
buffer_events[i]();
|
||||
}
|
||||
}
|
||||
events.SetAudioEvent(Event::Type(i), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,101 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "audio_core/audio_event.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
namespace AudioOut {
|
||||
class Manager;
|
||||
}
|
||||
|
||||
namespace AudioIn {
|
||||
class Manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* The AudioManager's main purpose is to wait for buffer events for the audio in and out managers,
|
||||
* and call an associated callback to release buffers.
|
||||
*
|
||||
* Execution pattern is:
|
||||
* Buffers appended ->
|
||||
* Buffers queued and played by the backend stream ->
|
||||
* When consumed, set the corresponding manager event and signal the audio manager ->
|
||||
* Consumed buffers are released, game is signalled ->
|
||||
* Game appends more buffers.
|
||||
*
|
||||
* This is only used by audio in and audio out.
|
||||
*/
|
||||
class AudioManager {
|
||||
using BufferEventFunc = std::function<void()>;
|
||||
|
||||
public:
|
||||
explicit AudioManager(Core::System& system);
|
||||
|
||||
/**
|
||||
* Shutdown the audio manager.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Register the out manager, keeping a function to be called when the out event is signalled.
|
||||
*
|
||||
* @param buffer_func - Function to be called on signal.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result SetOutManager(BufferEventFunc buffer_func);
|
||||
|
||||
/**
|
||||
* Register the in manager, keeping a function to be called when the in event is signalled.
|
||||
*
|
||||
* @param buffer_func - Function to be called on signal.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result SetInManager(BufferEventFunc buffer_func);
|
||||
|
||||
/**
|
||||
* Set an event to signalled, and signal the thread.
|
||||
*
|
||||
* @param type - Manager type to set.
|
||||
* @param signalled - Set the event to true or false?
|
||||
*/
|
||||
void SetEvent(Event::Type type, bool signalled);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Main thread, waiting on a manager signal and calling the registered fucntion.
|
||||
*/
|
||||
void ThreadFunc();
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// Have sessions started palying?
|
||||
bool sessions_started{};
|
||||
/// Is the main thread running?
|
||||
std::atomic<bool> running{};
|
||||
/// Unused
|
||||
bool needs_update{};
|
||||
/// Events to be set and signalled
|
||||
Event events{};
|
||||
/// Callbacks for each manager
|
||||
std::array<BufferEventFunc, 3> buffer_events{};
|
||||
/// General lock
|
||||
std::mutex lock{};
|
||||
/// Main thread for waiting and callbacks
|
||||
std::jthread thread;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "audio_core/audio_out.h"
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// Returns the stream format from the specified number of channels
|
||||
static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
switch (num_channels) {
|
||||
case 1:
|
||||
return Stream::Format::Mono16;
|
||||
case 2:
|
||||
return Stream::Format::Stereo16;
|
||||
case 6:
|
||||
return Stream::Format::Multi51Channel16;
|
||||
}
|
||||
|
||||
UNIMPLEMENTED_MSG("Unimplemented num_channels={}", num_channels);
|
||||
return {};
|
||||
}
|
||||
|
||||
StreamPtr AudioOut::OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate,
|
||||
u32 num_channels, std::string&& name,
|
||||
Stream::ReleaseCallback&& release_callback) {
|
||||
if (!sink) {
|
||||
sink = CreateSinkFromID(Settings::values.sink_id.GetValue(),
|
||||
Settings::values.audio_device_id.GetValue());
|
||||
}
|
||||
|
||||
return std::make_shared<Stream>(
|
||||
core_timing, sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
|
||||
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||
std::size_t max_count) {
|
||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream) {
|
||||
return stream->GetTagsAndReleaseBuffers();
|
||||
}
|
||||
|
||||
void AudioOut::StartStream(StreamPtr stream) {
|
||||
stream->Play();
|
||||
}
|
||||
|
||||
void AudioOut::StopStream(StreamPtr stream) {
|
||||
stream->Stop();
|
||||
}
|
||||
|
||||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
|
||||
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/buffer.h"
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/**
|
||||
* Represents an audio playback interface, used to open and play audio streams
|
||||
*/
|
||||
class AudioOut {
|
||||
public:
|
||||
/// Opens a new audio stream
|
||||
StreamPtr OpenStream(Core::Timing::CoreTiming& core_timing, u32 sample_rate, u32 num_channels,
|
||||
std::string&& name, 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);
|
||||
|
||||
/// Returns a vector of all recently released buffers specified by tag for the specified stream
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream);
|
||||
|
||||
/// Starts an audio stream for playback
|
||||
void StartStream(StreamPtr stream);
|
||||
|
||||
/// Stops an audio stream that is currently playing
|
||||
void StopStream(StreamPtr stream);
|
||||
|
||||
/// Queues a buffer into the specified audio stream, returns true on success
|
||||
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
|
||||
|
||||
private:
|
||||
SinkPtr sink;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,81 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
#include "audio_core/audio_manager.h"
|
||||
#include "audio_core/audio_out_manager.h"
|
||||
#include "audio_core/out/audio_out.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
|
||||
Manager::Manager(Core::System& system_) : system{system_} {
|
||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||
num_free_sessions = MaxOutSessions;
|
||||
}
|
||||
|
||||
Result Manager::AcquireSessionId(size_t& session_id) {
|
||||
if (num_free_sessions == 0) {
|
||||
LOG_ERROR(Service_Audio, "All 12 Audio Out sessions are in use, cannot create any more");
|
||||
return Service::Audio::ERR_MAXIMUM_SESSIONS_REACHED;
|
||||
}
|
||||
session_id = session_ids[next_session_id];
|
||||
next_session_id = (next_session_id + 1) % MaxOutSessions;
|
||||
num_free_sessions--;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Manager::ReleaseSessionId(const size_t session_id) {
|
||||
std::scoped_lock l{mutex};
|
||||
LOG_DEBUG(Service_Audio, "Freeing AudioOut session {}", session_id);
|
||||
session_ids[free_session_id] = session_id;
|
||||
num_free_sessions++;
|
||||
free_session_id = (free_session_id + 1) % MaxOutSessions;
|
||||
sessions[session_id].reset();
|
||||
applet_resource_user_ids[session_id] = 0;
|
||||
}
|
||||
|
||||
Result Manager::LinkToManager() {
|
||||
std::scoped_lock l{mutex};
|
||||
if (!linked_to_manager) {
|
||||
AudioManager& manager{system.AudioCore().GetAudioManager()};
|
||||
manager.SetOutManager(std::bind(&Manager::BufferReleaseAndRegister, this));
|
||||
linked_to_manager = true;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void Manager::Start() {
|
||||
if (sessions_started) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock l{mutex};
|
||||
for (auto& session : sessions) {
|
||||
if (session) {
|
||||
session->StartSession();
|
||||
}
|
||||
}
|
||||
|
||||
sessions_started = true;
|
||||
}
|
||||
|
||||
void Manager::BufferReleaseAndRegister() {
|
||||
std::scoped_lock l{mutex};
|
||||
for (auto& session : sessions) {
|
||||
if (session != nullptr) {
|
||||
session->ReleaseAndRegisterBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 Manager::GetAudioOutDeviceNames(
|
||||
std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const {
|
||||
names.push_back({"DeviceOut"});
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioOut
|
||||
@@ -1,89 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
|
||||
#include "audio_core/renderer/audio_device.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore::AudioOut {
|
||||
class Out;
|
||||
|
||||
constexpr size_t MaxOutSessions = 12;
|
||||
/**
|
||||
* Manages all audio out sessions.
|
||||
*/
|
||||
class Manager {
|
||||
public:
|
||||
explicit Manager(Core::System& system);
|
||||
|
||||
/**
|
||||
* Acquire a free session id for opening a new audio out.
|
||||
*
|
||||
* @param session_id - Output session_id.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result AcquireSessionId(size_t& session_id);
|
||||
|
||||
/**
|
||||
* Release a session id on close.
|
||||
*
|
||||
* @param session_id - Session id to free.
|
||||
*/
|
||||
void ReleaseSessionId(size_t session_id);
|
||||
|
||||
/**
|
||||
* Link this manager to the main audio manager.
|
||||
*
|
||||
* @return Result code.
|
||||
*/
|
||||
Result LinkToManager();
|
||||
|
||||
/**
|
||||
* Start the audio out manager.
|
||||
*/
|
||||
void Start();
|
||||
|
||||
/**
|
||||
* Callback function, called by the audio manager when the audio out event is signalled.
|
||||
*/
|
||||
void BufferReleaseAndRegister();
|
||||
|
||||
/**
|
||||
* Get a list of audio out device names.
|
||||
*
|
||||
* @oaram names - Output container to write names to.
|
||||
* @return Number of names written.
|
||||
*/
|
||||
u32 GetAudioOutDeviceNames(
|
||||
std::vector<AudioRenderer::AudioDevice::AudioDeviceName>& names) const;
|
||||
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// Array of session ids
|
||||
std::array<size_t, MaxOutSessions> session_ids{};
|
||||
/// Array of resource user ids
|
||||
std::array<size_t, MaxOutSessions> applet_resource_user_ids{};
|
||||
/// Pointer to each open session
|
||||
std::array<std::shared_ptr<Out>, MaxOutSessions> sessions{};
|
||||
/// The number of free sessions
|
||||
size_t num_free_sessions{};
|
||||
/// The next session id to be taken
|
||||
size_t next_session_id{};
|
||||
/// The next session id to be freed
|
||||
size_t free_session_id{};
|
||||
/// Whether this is linked to the audio manager
|
||||
bool linked_to_manager{};
|
||||
/// Whether the sessions have been started
|
||||
bool sessions_started{};
|
||||
/// Protect state due to audio manager callback
|
||||
std::recursive_mutex mutex{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioOut
|
||||
@@ -1,70 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "audio_core/audio_render_manager.h"
|
||||
#include "audio_core/common/audio_renderer_parameter.h"
|
||||
#include "audio_core/common/feature_support.h"
|
||||
#include "core/core.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
Manager::Manager(Core::System& system_)
|
||||
: system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
|
||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||
}
|
||||
|
||||
Manager::~Manager() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Manager::Stop() {
|
||||
system_manager->Stop();
|
||||
}
|
||||
|
||||
SystemManager& Manager::GetSystemManager() {
|
||||
return *system_manager;
|
||||
}
|
||||
|
||||
auto Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count)
|
||||
-> Result {
|
||||
if (!CheckValidRevision(params.revision)) {
|
||||
return Service::Audio::ERR_INVALID_REVISION;
|
||||
}
|
||||
|
||||
out_count = System::GetWorkBufferSize(params);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
s32 Manager::GetSessionId() {
|
||||
std::scoped_lock l{session_lock};
|
||||
auto session_id{session_ids[session_count]};
|
||||
|
||||
if (session_id == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
session_ids[session_count] = -1;
|
||||
session_count++;
|
||||
return session_id;
|
||||
}
|
||||
|
||||
void Manager::ReleaseSessionId(const s32 session_id) {
|
||||
std::scoped_lock l{session_lock};
|
||||
session_ids[--session_count] = session_id;
|
||||
}
|
||||
|
||||
u32 Manager::GetSessionCount() {
|
||||
std::scoped_lock l{session_lock};
|
||||
return session_count;
|
||||
}
|
||||
|
||||
bool Manager::AddSystem(System& system_) {
|
||||
return system_manager->Add(system_);
|
||||
}
|
||||
|
||||
bool Manager::RemoveSystem(System& system_) {
|
||||
return system_manager->Remove(system_);
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
||||
@@ -1,103 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "audio_core/common/common.h"
|
||||
#include "audio_core/renderer/system_manager.h"
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
struct AudioRendererParameterInternal;
|
||||
|
||||
namespace AudioRenderer {
|
||||
/**
|
||||
* Wrapper for the audio system manager, handles service calls.
|
||||
*/
|
||||
class Manager {
|
||||
public:
|
||||
explicit Manager(Core::System& system);
|
||||
~Manager();
|
||||
|
||||
/**
|
||||
* Stop the manager.
|
||||
*/
|
||||
void Stop();
|
||||
|
||||
/**
|
||||
* Get the system manager.
|
||||
*
|
||||
* @return The system manager.
|
||||
*/
|
||||
SystemManager& GetSystemManager();
|
||||
|
||||
/**
|
||||
* Get required size for the audio renderer workbuffer.
|
||||
*
|
||||
* @param params - Input parameters with the numbers of voices/mixes/sinks etc.
|
||||
* @param out_count - Output size of the required workbuffer.
|
||||
* @return Result code.
|
||||
*/
|
||||
Result GetWorkBufferSize(const AudioRendererParameterInternal& params, u64& out_count);
|
||||
|
||||
/**
|
||||
* Get a new session id.
|
||||
*
|
||||
* @return The new session id. -1 if invalid, otherwise 0-MaxRendererSessions.
|
||||
*/
|
||||
s32 GetSessionId();
|
||||
|
||||
/**
|
||||
* Get the number of currently active sessions.
|
||||
*
|
||||
* @return The number of active sessions.
|
||||
*/
|
||||
u32 GetSessionCount();
|
||||
|
||||
/**
|
||||
* Add a renderer system to the manager.
|
||||
* The system will be reguarly called to generate commands for the AudioRenderer.
|
||||
*
|
||||
* @param system - The system to add.
|
||||
* @return True if the system was sucessfully added, otherwise false.
|
||||
*/
|
||||
bool AddSystem(System& system);
|
||||
|
||||
/**
|
||||
* Remove a renderer system from the manager.
|
||||
*
|
||||
* @param system - The system to remove.
|
||||
* @return True if the system was sucessfully removed, otherwise false.
|
||||
*/
|
||||
bool RemoveSystem(System& system);
|
||||
|
||||
/**
|
||||
* Free a session id when the system wants to shut down.
|
||||
*
|
||||
* @param session_id - The session id to free.
|
||||
*/
|
||||
void ReleaseSessionId(s32 session_id);
|
||||
|
||||
private:
|
||||
/// Core system
|
||||
Core::System& system;
|
||||
/// Session ids, -1 when in use
|
||||
std::array<s32, MaxRendererSessions> session_ids{};
|
||||
/// Number of active renderers
|
||||
u32 session_count{};
|
||||
/// Lock for interacting with the sessions
|
||||
std::mutex session_lock{};
|
||||
/// Regularly generates commands from the registered systems for the AudioRenderer
|
||||
std::unique_ptr<SystemManager> system_manager{};
|
||||
};
|
||||
|
||||
} // namespace AudioRenderer
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,340 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/audio_out.h"
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/info_updater.h"
|
||||
#include "audio_core/voice_context.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace {
|
||||
[[nodiscard]] static constexpr s16 ClampToS16(s32 value) {
|
||||
return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()},
|
||||
s32{std::numeric_limits<s16>::max()}));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) {
|
||||
// Mix 50% from left and 50% from right channel
|
||||
constexpr float l_mix_amount = 50.0f / 100.0f;
|
||||
constexpr float r_mix_amount = 50.0f / 100.0f;
|
||||
return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) +
|
||||
(static_cast<float>(r_channel) * r_mix_amount)));
|
||||
}
|
||||
|
||||
[[maybe_unused, nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(
|
||||
s16 fl_channel, s16 fr_channel, s16 fc_channel, [[maybe_unused]] s16 lf_channel, s16 bl_channel,
|
||||
s16 br_channel) {
|
||||
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
||||
// are mixed to be 36.94%
|
||||
|
||||
constexpr float front_mix_amount = 36.94f / 100.0f;
|
||||
constexpr float center_mix_amount = 26.12f / 100.0f;
|
||||
constexpr float back_mix_amount = 36.94f / 100.0f;
|
||||
|
||||
// Mix 50% from left and 50% from right channel
|
||||
const auto left = front_mix_amount * static_cast<float>(fl_channel) +
|
||||
center_mix_amount * static_cast<float>(fc_channel) +
|
||||
back_mix_amount * static_cast<float>(bl_channel);
|
||||
|
||||
const auto right = front_mix_amount * static_cast<float>(fr_channel) +
|
||||
center_mix_amount * static_cast<float>(fc_channel) +
|
||||
back_mix_amount * static_cast<float>(br_channel);
|
||||
|
||||
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients(
|
||||
s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel,
|
||||
const std::array<float_le, 4>& coeff) {
|
||||
const auto left =
|
||||
static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[3];
|
||||
|
||||
const auto right =
|
||||
static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
||||
static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[3];
|
||||
|
||||
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace AudioCore {
|
||||
constexpr s32 NUM_BUFFERS = 2;
|
||||
|
||||
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_,
|
||||
AudioCommon::AudioRendererParameter params,
|
||||
Stream::ReleaseCallback&& release_callback,
|
||||
std::size_t instance_number)
|
||||
: worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4),
|
||||
voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
|
||||
sink_context(params.sink_count), splitter_context(),
|
||||
voices(params.voice_count), memory{memory_},
|
||||
command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
|
||||
memory),
|
||||
core_timing{core_timing_} {
|
||||
behavior_info.SetUserRevision(params.revision);
|
||||
splitter_context.Initialize(behavior_info, params.splitter_count,
|
||||
params.num_splitter_send_channels);
|
||||
mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_out->OpenStream(
|
||||
core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
|
||||
fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
|
||||
process_event = Core::Timing::CreateEvent(
|
||||
fmt::format("AudioRenderer-Instance{}-Process", instance_number),
|
||||
[this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); });
|
||||
for (s32 i = 0; i < NUM_BUFFERS; ++i) {
|
||||
QueueMixedBuffer(i);
|
||||
}
|
||||
}
|
||||
|
||||
AudioRenderer::~AudioRenderer() = default;
|
||||
|
||||
ResultCode AudioRenderer::Start() {
|
||||
audio_out->StartStream(stream);
|
||||
ReleaseAndQueueBuffers();
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
ResultCode AudioRenderer::Stop() {
|
||||
audio_out->StopStream(stream);
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params) {
|
||||
std::scoped_lock lock{mutex};
|
||||
InfoUpdater info_updater{input_params, output_params, behavior_info};
|
||||
|
||||
if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
|
||||
LOG_ERROR(Audio, "Failed to update behavior info input parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!info_updater.UpdateMemoryPools(memory_pool_info)) {
|
||||
LOG_ERROR(Audio, "Failed to update memory pool parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!info_updater.UpdateVoiceChannelResources(voice_context)) {
|
||||
LOG_ERROR(Audio, "Failed to update voice channel resource parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) {
|
||||
LOG_ERROR(Audio, "Failed to update voice parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
// TODO(ogniK): Deal with stopped audio renderer but updates still taking place
|
||||
if (!info_updater.UpdateEffects(effect_context, true)) {
|
||||
LOG_ERROR(Audio, "Failed to update effect parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (behavior_info.IsSplitterSupported()) {
|
||||
if (!info_updater.UpdateSplitterInfo(splitter_context)) {
|
||||
LOG_ERROR(Audio, "Failed to update splitter parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
}
|
||||
|
||||
const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
|
||||
splitter_context, effect_context);
|
||||
|
||||
if (mix_result.IsError()) {
|
||||
LOG_ERROR(Audio, "Failed to update mix parameters");
|
||||
return mix_result;
|
||||
}
|
||||
|
||||
// TODO(ogniK): Sinks
|
||||
if (!info_updater.UpdateSinks(sink_context)) {
|
||||
LOG_ERROR(Audio, "Failed to update sink parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
// TODO(ogniK): Performance buffer
|
||||
if (!info_updater.UpdatePerformanceBuffer()) {
|
||||
LOG_ERROR(Audio, "Failed to update performance buffer parameters");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (!info_updater.UpdateErrorInfo(behavior_info)) {
|
||||
LOG_ERROR(Audio, "Failed to update error info");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
if (behavior_info.IsElapsedFrameCountSupported()) {
|
||||
if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) {
|
||||
LOG_ERROR(Audio, "Failed to update renderer info");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
}
|
||||
// TODO(ogniK): Statistics
|
||||
|
||||
if (!info_updater.WriteOutputHeader()) {
|
||||
LOG_ERROR(Audio, "Failed to write output header");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
|
||||
// TODO(ogniK): Check when all sections are implemented
|
||||
|
||||
if (!info_updater.CheckConsumedSize()) {
|
||||
LOG_ERROR(Audio, "Audio buffers were not consumed!");
|
||||
return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
|
||||
}
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
command_generator.PreCommand();
|
||||
// Clear mix buffers before our next operation
|
||||
command_generator.ClearMixBuffers();
|
||||
|
||||
// If the splitter is not in use, sort our mixes
|
||||
if (!splitter_context.UsingSplitter()) {
|
||||
mix_context.SortInfo();
|
||||
}
|
||||
// Sort our voices
|
||||
voice_context.SortInfo();
|
||||
|
||||
// Handle samples
|
||||
command_generator.GenerateVoiceCommands();
|
||||
command_generator.GenerateSubMixCommands();
|
||||
command_generator.GenerateFinalMixCommands();
|
||||
|
||||
command_generator.PostCommand();
|
||||
// Base sample size
|
||||
std::size_t BUFFER_SIZE{worker_params.sample_count};
|
||||
// Samples, making sure to clear
|
||||
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0);
|
||||
|
||||
if (sink_context.InUse()) {
|
||||
const auto stream_channel_count = stream->GetNumChannels();
|
||||
const auto buffer_offsets = sink_context.OutputBuffers();
|
||||
const auto channel_count = buffer_offsets.size();
|
||||
const auto& final_mix = mix_context.GetFinalMixInfo();
|
||||
const auto& in_params = final_mix.GetInParams();
|
||||
std::vector<std::span<s32>> mix_buffers(channel_count);
|
||||
for (std::size_t i = 0; i < channel_count; i++) {
|
||||
mix_buffers[i] =
|
||||
command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (channel_count == 1) {
|
||||
const auto sample = ClampToS16(mix_buffers[0][i]);
|
||||
|
||||
// Place sample in all channels
|
||||
for (u32 channel = 0; channel < stream_channel_count; channel++) {
|
||||
buffer[i * stream_channel_count + channel] = sample;
|
||||
}
|
||||
|
||||
if (stream_channel_count == 6) {
|
||||
// Output stream has a LF channel, mute it!
|
||||
buffer[i * stream_channel_count + 3] = 0;
|
||||
}
|
||||
|
||||
} else if (channel_count == 2) {
|
||||
const auto l_sample = ClampToS16(mix_buffers[0][i]);
|
||||
const auto r_sample = ClampToS16(mix_buffers[1][i]);
|
||||
if (stream_channel_count == 1) {
|
||||
buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample);
|
||||
} else if (stream_channel_count == 2) {
|
||||
buffer[i * stream_channel_count + 0] = l_sample;
|
||||
buffer[i * stream_channel_count + 1] = r_sample;
|
||||
} else if (stream_channel_count == 6) {
|
||||
buffer[i * stream_channel_count + 0] = l_sample;
|
||||
buffer[i * stream_channel_count + 1] = r_sample;
|
||||
|
||||
// Combine both left and right channels to the center channel
|
||||
buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample);
|
||||
|
||||
buffer[i * stream_channel_count + 4] = l_sample;
|
||||
buffer[i * stream_channel_count + 5] = r_sample;
|
||||
}
|
||||
|
||||
} else if (channel_count == 6) {
|
||||
const auto fl_sample = ClampToS16(mix_buffers[0][i]);
|
||||
const auto fr_sample = ClampToS16(mix_buffers[1][i]);
|
||||
const auto fc_sample = ClampToS16(mix_buffers[2][i]);
|
||||
const auto lf_sample = ClampToS16(mix_buffers[3][i]);
|
||||
const auto bl_sample = ClampToS16(mix_buffers[4][i]);
|
||||
const auto br_sample = ClampToS16(mix_buffers[5][i]);
|
||||
|
||||
if (stream_channel_count == 1) {
|
||||
// Games seem to ignore the center channel half the time, we use the front left
|
||||
// and right channel for mixing as that's where majority of the audio goes
|
||||
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
||||
} else if (stream_channel_count == 2) {
|
||||
// Mix all channels into 2 channels
|
||||
const auto [left, right] = Mix6To2WithCoefficients(
|
||||
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
||||
sink_context.GetDownmixCoefficients());
|
||||
buffer[i * stream_channel_count + 0] = left;
|
||||
buffer[i * stream_channel_count + 1] = right;
|
||||
} else if (stream_channel_count == 6) {
|
||||
// Pass through
|
||||
buffer[i * stream_channel_count + 0] = fl_sample;
|
||||
buffer[i * stream_channel_count + 1] = fr_sample;
|
||||
buffer[i * stream_channel_count + 2] = fc_sample;
|
||||
buffer[i * stream_channel_count + 3] = lf_sample;
|
||||
buffer[i * stream_channel_count + 4] = bl_sample;
|
||||
buffer[i * stream_channel_count + 5] = br_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
audio_out->QueueBuffer(stream, tag, std::move(buffer));
|
||||
elapsed_frame_count++;
|
||||
voice_context.UpdateStateByDspShared();
|
||||
}
|
||||
|
||||
void AudioRenderer::ReleaseAndQueueBuffers() {
|
||||
if (!stream->IsPlaying()) {
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lock{mutex};
|
||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)};
|
||||
for (const auto& tag : released_buffers) {
|
||||
QueueMixedBuffer(tag);
|
||||
}
|
||||
}
|
||||
|
||||
const f32 sample_rate = static_cast<f32>(GetSampleRate());
|
||||
const f32 sample_count = static_cast<f32>(GetSampleCount());
|
||||
const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240));
|
||||
const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1;
|
||||
const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1));
|
||||
core_timing.ScheduleEvent(next_event_time, process_event, {});
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/behavior_info.h"
|
||||
#include "audio_core/command_generator.h"
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/effect_context.h"
|
||||
#include "audio_core/memory_pool.h"
|
||||
#include "audio_core/mix_context.h"
|
||||
#include "audio_core/sink_context.h"
|
||||
#include "audio_core/splitter_context.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/voice_context.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
}
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
using DSPStateHolder = std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>;
|
||||
|
||||
class AudioOut;
|
||||
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
|
||||
AudioCommon::AudioRendererParameter params,
|
||||
Stream::ReleaseCallback&& release_callback, std::size_t instance_number);
|
||||
~AudioRenderer();
|
||||
|
||||
[[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
|
||||
std::vector<u8>& output_params);
|
||||
[[nodiscard]] ResultCode Start();
|
||||
[[nodiscard]] ResultCode Stop();
|
||||
void QueueMixedBuffer(Buffer::Tag tag);
|
||||
void ReleaseAndQueueBuffers();
|
||||
[[nodiscard]] u32 GetSampleRate() const;
|
||||
[[nodiscard]] u32 GetSampleCount() const;
|
||||
[[nodiscard]] u32 GetMixBufferCount() const;
|
||||
[[nodiscard]] Stream::State GetStreamState() const;
|
||||
|
||||
private:
|
||||
BehaviorInfo behavior_info{};
|
||||
|
||||
AudioCommon::AudioRendererParameter worker_params;
|
||||
std::vector<ServerMemoryPoolInfo> memory_pool_info;
|
||||
VoiceContext voice_context;
|
||||
EffectContext effect_context;
|
||||
MixContext mix_context;
|
||||
SinkContext sink_context;
|
||||
SplitterContext splitter_context;
|
||||
std::vector<VoiceState> voices;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
StreamPtr stream;
|
||||
Core::Memory::Memory& memory;
|
||||
CommandGenerator command_generator;
|
||||
std::size_t elapsed_frame_count{};
|
||||
Core::Timing::CoreTiming& core_timing;
|
||||
std::shared_ptr<Core::Timing::EventType> process_event;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "audio_core/behavior_info.h"
|
||||
#include "audio_core/common.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
BehaviorInfo::BehaviorInfo() : process_revision(AudioCommon::CURRENT_PROCESS_REVISION) {}
|
||||
BehaviorInfo::~BehaviorInfo() = default;
|
||||
|
||||
bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) {
|
||||
if (!AudioCommon::CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) {
|
||||
LOG_ERROR(Audio, "Buffer is an invalid size!");
|
||||
return false;
|
||||
}
|
||||
|
||||
OutParams params{};
|
||||
std::memcpy(params.errors.data(), errors.data(), sizeof(ErrorInfo) * errors.size());
|
||||
params.error_count = static_cast<u32_le>(error_count);
|
||||
std::memcpy(buffer.data() + offset, ¶ms, sizeof(OutParams));
|
||||
return true;
|
||||
}
|
||||
|
||||
void BehaviorInfo::ClearError() {
|
||||
error_count = 0;
|
||||
}
|
||||
|
||||
void BehaviorInfo::UpdateFlags(u64_le dest_flags) {
|
||||
flags = dest_flags;
|
||||
}
|
||||
|
||||
void BehaviorInfo::SetUserRevision(u32_le revision) {
|
||||
user_revision = revision;
|
||||
}
|
||||
|
||||
u32_le BehaviorInfo::GetUserRevision() const {
|
||||
return user_revision;
|
||||
}
|
||||
|
||||
u32_le BehaviorInfo::GetProcessRevision() const {
|
||||
return process_revision;
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const {
|
||||
return AudioCommon::IsRevisionSupported(2, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsSplitterSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(2, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsLongSizePreDelaySupported() const {
|
||||
return AudioCommon::IsRevisionSupported(3, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(4, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(1, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsElapsedFrameCountSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const {
|
||||
return (flags & 1) != 0;
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsFlushVoiceWaveBuffersSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsVoicePitchAndSrcSkippedSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() const {
|
||||
return AudioCommon::IsRevisionSupported(7, user_revision);
|
||||
}
|
||||
|
||||
bool BehaviorInfo::IsSplitterBugFixed() const {
|
||||
return AudioCommon::IsRevisionSupported(5, user_revision);
|
||||
}
|
||||
|
||||
void BehaviorInfo::CopyErrorInfo(BehaviorInfo::OutParams& dst) {
|
||||
dst.error_count = static_cast<u32>(error_count);
|
||||
std::copy(errors.begin(), errors.begin() + error_count, dst.errors.begin());
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,72 @@
|
||||
// Copyright 2020 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_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace AudioCore {
|
||||
class BehaviorInfo {
|
||||
public:
|
||||
struct ErrorInfo {
|
||||
u32_le result{};
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64_le result_info{};
|
||||
};
|
||||
static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
|
||||
|
||||
struct InParams {
|
||||
u32_le revision{};
|
||||
u32_le padding{};
|
||||
u64_le flags{};
|
||||
};
|
||||
static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
|
||||
|
||||
struct OutParams {
|
||||
std::array<ErrorInfo, 10> errors{};
|
||||
u32_le error_count{};
|
||||
INSERT_PADDING_BYTES(12);
|
||||
};
|
||||
static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
|
||||
|
||||
explicit BehaviorInfo();
|
||||
~BehaviorInfo();
|
||||
|
||||
bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset);
|
||||
|
||||
void ClearError();
|
||||
void UpdateFlags(u64_le dest_flags);
|
||||
void SetUserRevision(u32_le revision);
|
||||
[[nodiscard]] u32_le GetUserRevision() const;
|
||||
[[nodiscard]] u32_le GetProcessRevision() const;
|
||||
|
||||
[[nodiscard]] bool IsAdpcmLoopContextBugFixed() const;
|
||||
[[nodiscard]] bool IsSplitterSupported() const;
|
||||
[[nodiscard]] bool IsLongSizePreDelaySupported() const;
|
||||
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
|
||||
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
|
||||
[[nodiscard]] bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
|
||||
[[nodiscard]] bool IsElapsedFrameCountSupported() const;
|
||||
[[nodiscard]] bool IsMemoryPoolForceMappingEnabled() const;
|
||||
[[nodiscard]] bool IsFlushVoiceWaveBuffersSupported() const;
|
||||
[[nodiscard]] bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
|
||||
[[nodiscard]] bool IsVoicePitchAndSrcSkippedSupported() const;
|
||||
[[nodiscard]] bool IsMixInParameterDirtyOnlyUpdateSupported() const;
|
||||
[[nodiscard]] bool IsSplitterBugFixed() const;
|
||||
void CopyErrorInfo(OutParams& dst);
|
||||
|
||||
private:
|
||||
u32_le process_revision{};
|
||||
u32_le user_revision{};
|
||||
u64_le flags{};
|
||||
std::array<ErrorInfo, 10> errors{};
|
||||
std::size_t error_count{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/**
|
||||
* Represents a buffer of audio samples to be played in an audio stream
|
||||
*/
|
||||
class Buffer {
|
||||
public:
|
||||
using Tag = u64;
|
||||
|
||||
Buffer(Tag tag_, std::vector<s16>&& samples_) : tag{tag_}, samples{std::move(samples_)} {}
|
||||
|
||||
/// Returns the raw audio data for the buffer
|
||||
std::vector<s16>& GetSamples() {
|
||||
return samples;
|
||||
}
|
||||
|
||||
/// Returns the raw audio data for the buffer
|
||||
const std::vector<s16>& GetSamples() const {
|
||||
return samples;
|
||||
}
|
||||
|
||||
/// Returns the buffer tag, this is provided by the game to the audout service
|
||||
Tag GetTag() const {
|
||||
return tag;
|
||||
}
|
||||
|
||||
private:
|
||||
Tag tag;
|
||||
std::vector<s16> samples;
|
||||
};
|
||||
|
||||
using BufferPtr = std::shared_ptr<Buffer>;
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "audio_core/codec.h"
|
||||
|
||||
namespace AudioCore::Codec {
|
||||
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, std::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;
|
||||
static 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 =
|
||||
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 =
|
||||
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
|
||||
for (std::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;
|
||||
|
||||
// Coefficients are fixed point with 11 bits fractional part.
|
||||
const int coef1 = coeff[idx * 2 + 0];
|
||||
const int coef2 = coeff[idx * 2 + 1];
|
||||
|
||||
// Decodes an audio sample. One nibble produces one sample.
|
||||
const auto decode_sample = [&](const int nibble) -> s16 {
|
||||
const int xn = nibble * scale;
|
||||
// We first transform everything into 11 bit fixed point, perform the second order
|
||||
// digital filter, then transform back.
|
||||
// 0x400 == 0.5 in 11 bit fixed point.
|
||||
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
|
||||
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
|
||||
// Clamp to output range.
|
||||
val = std::clamp<s32>(val, -32768, 32767);
|
||||
// Advance output feedback.
|
||||
yn2 = yn1;
|
||||
yn1 = val;
|
||||
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) {
|
||||
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
|
||||
ret[outputi] = sample1;
|
||||
outputi++;
|
||||
|
||||
const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
|
||||
ret[outputi] = sample2;
|
||||
outputi++;
|
||||
|
||||
datai++;
|
||||
}
|
||||
}
|
||||
|
||||
state.yn1 = static_cast<s16>(yn1);
|
||||
state.yn2 = static_cast<s16>(yn2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Codec
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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::Codec {
|
||||
|
||||
enum class PcmFormat : u32 {
|
||||
Invalid = 0,
|
||||
Int8 = 1,
|
||||
Int16 = 2,
|
||||
Int24 = 3,
|
||||
Int32 = 4,
|
||||
PcmFloat = 5,
|
||||
Adpcm = 6,
|
||||
};
|
||||
|
||||
/// See: Codec::DecodeADPCM
|
||||
struct ADPCMState {
|
||||
// Two historical samples from previous processed buffer,
|
||||
// required for ADPCM decoding
|
||||
s16 yn1; ///< y[n-1]
|
||||
s16 yn2; ///< y[n-2]
|
||||
};
|
||||
|
||||
using ADPCM_Coeff = std::array<s16, 16>;
|
||||
|
||||
/**
|
||||
* @param data Pointer to buffer that contains ADPCM data to decode
|
||||
* @param size Size of buffer in bytes
|
||||
* @param coeff ADPCM coefficients
|
||||
* @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* data, std::size_t size, const ADPCM_Coeff& coeff,
|
||||
ADPCMState& state);
|
||||
|
||||
}; // namespace AudioCore::Codec
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,111 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
#include "audio_core/common.h"
|
||||
#include "audio_core/voice_context.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
class MixContext;
|
||||
class SplitterContext;
|
||||
class ServerSplitterDestinationData;
|
||||
class ServerMixInfo;
|
||||
class EffectContext;
|
||||
class EffectBase;
|
||||
struct AuxInfoDSP;
|
||||
struct I3dl2ReverbParams;
|
||||
struct I3dl2ReverbState;
|
||||
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
|
||||
|
||||
class CommandGenerator {
|
||||
public:
|
||||
explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
|
||||
VoiceContext& voice_context_, MixContext& mix_context_,
|
||||
SplitterContext& splitter_context_, EffectContext& effect_context_,
|
||||
Core::Memory::Memory& memory_);
|
||||
~CommandGenerator();
|
||||
|
||||
void ClearMixBuffers();
|
||||
void GenerateVoiceCommands();
|
||||
void GenerateVoiceCommand(ServerVoiceInfo& voice_info);
|
||||
void GenerateSubMixCommands();
|
||||
void GenerateFinalMixCommands();
|
||||
void PreCommand();
|
||||
void PostCommand();
|
||||
|
||||
[[nodiscard]] std::span<s32> GetChannelMixBuffer(s32 channel);
|
||||
[[nodiscard]] std::span<const s32> GetChannelMixBuffer(s32 channel) const;
|
||||
[[nodiscard]] std::span<s32> GetMixBuffer(std::size_t index);
|
||||
[[nodiscard]] std::span<const s32> GetMixBuffer(std::size_t index) const;
|
||||
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||
|
||||
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
||||
|
||||
private:
|
||||
void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
|
||||
void GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
|
||||
s32 mix_buffer_count, s32 channel);
|
||||
void GenerateVolumeRampCommand(float last_volume, float current_volume, s32 channel,
|
||||
s32 node_id);
|
||||
void GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
|
||||
const MixVolumeBuffer& last_mix_volumes, VoiceState& dsp_state,
|
||||
s32 mix_buffer_offset, s32 mix_buffer_count, s32 voice_index,
|
||||
s32 node_id);
|
||||
void GenerateSubMixCommand(ServerMixInfo& mix_info);
|
||||
void GenerateMixCommands(ServerMixInfo& mix_info);
|
||||
void GenerateMixCommand(std::size_t output_offset, std::size_t input_offset, float volume,
|
||||
s32 node_id);
|
||||
void GenerateFinalMixCommand();
|
||||
void GenerateBiquadFilterCommand(s32 mix_buffer, const BiquadFilterParameter& params,
|
||||
std::array<s64, 2>& state, std::size_t input_offset,
|
||||
std::size_t output_offset, s32 sample_count, s32 node_id);
|
||||
void GenerateDepopPrepareCommand(VoiceState& dsp_state, std::size_t mix_buffer_count,
|
||||
std::size_t mix_buffer_offset);
|
||||
void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
|
||||
std::size_t mix_buffer_offset, s32 sample_rate);
|
||||
void GenerateEffectCommand(ServerMixInfo& mix_info);
|
||||
void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||
void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||
|
||||
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
|
||||
std::span<const s32> data, u32 sample_count, u32 write_offset,
|
||||
u32 write_count);
|
||||
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
|
||||
std::span<s32> out_data, u32 sample_count, u32 read_offset, u32 read_count);
|
||||
|
||||
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||
std::vector<u8>& work_buffer);
|
||||
void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
|
||||
// DSP Code
|
||||
template <typename T>
|
||||
s32 DecodePcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset,
|
||||
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
|
||||
s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset,
|
||||
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
|
||||
void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output,
|
||||
VoiceState& dsp_state, s32 channel, s32 target_sample_rate,
|
||||
s32 sample_count, s32 node_id);
|
||||
|
||||
AudioCommon::AudioRendererParameter& worker_params;
|
||||
VoiceContext& voice_context;
|
||||
MixContext& mix_context;
|
||||
SplitterContext& splitter_context;
|
||||
EffectContext& effect_context;
|
||||
Core::Memory::Memory& memory;
|
||||
std::vector<s32> mix_buffer{};
|
||||
std::vector<s32> sample_buffer{};
|
||||
std::vector<s32> depop_buffer{};
|
||||
bool dumping_frame{false};
|
||||
};
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace AudioCommon {
|
||||
namespace Audren {
|
||||
constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
|
||||
constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
|
||||
} // namespace Audren
|
||||
|
||||
constexpr u8 BASE_REVISION = '0';
|
||||
constexpr u32_le CURRENT_PROCESS_REVISION =
|
||||
Common::MakeMagic('R', 'E', 'V', static_cast<u8>(BASE_REVISION + 0xA));
|
||||
constexpr std::size_t MAX_MIX_BUFFERS = 24;
|
||||
constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
|
||||
constexpr std::size_t MAX_CHANNEL_COUNT = 6;
|
||||
constexpr std::size_t MAX_WAVE_BUFFERS = 4;
|
||||
constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
|
||||
constexpr u32 STREAM_SAMPLE_RATE = 48000;
|
||||
constexpr u32 STREAM_NUM_CHANNELS = 2;
|
||||
constexpr s32 NO_SPLITTER = -1;
|
||||
constexpr s32 NO_MIX = 0x7fffffff;
|
||||
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
|
||||
constexpr s32 FINAL_MIX = 0;
|
||||
constexpr s32 NO_EFFECT_ORDER = -1;
|
||||
constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
|
||||
// Any size checks seem to take the sample history into account
|
||||
// and our const ends up being 0x3f04, the 4 bytes are most
|
||||
// likely the sample history
|
||||
constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
|
||||
constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
|
||||
constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
|
||||
constexpr std::size_t I3DL2REVERB_TAPS = 20;
|
||||
constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
|
||||
using Fractional = s32;
|
||||
|
||||
template <typename T>
|
||||
constexpr Fractional ToFractional(T x) {
|
||||
return static_cast<Fractional>(x * static_cast<T>(0x4000));
|
||||
}
|
||||
|
||||
constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
|
||||
return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
|
||||
}
|
||||
|
||||
constexpr s32 FractionalToFixed(Fractional x) {
|
||||
const auto s = x & (1 << 13);
|
||||
return static_cast<s32>(x >> 14) + s;
|
||||
}
|
||||
|
||||
constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
|
||||
return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
|
||||
}
|
||||
|
||||
static constexpr u32 VersionFromRevision(u32_le rev) {
|
||||
// "REV7" -> 7
|
||||
return ((rev >> 24) & 0xff) - 0x30;
|
||||
}
|
||||
|
||||
static constexpr bool IsRevisionSupported(u32 required, u32_le user_revision) {
|
||||
const auto base = VersionFromRevision(user_revision);
|
||||
return required <= base;
|
||||
}
|
||||
|
||||
static constexpr bool IsValidRevision(u32_le revision) {
|
||||
const auto base = VersionFromRevision(revision);
|
||||
constexpr auto max_rev = VersionFromRevision(CURRENT_PROCESS_REVISION);
|
||||
return base <= max_rev;
|
||||
}
|
||||
|
||||
static constexpr bool CanConsumeBuffer(std::size_t size, std::size_t offset, std::size_t required) {
|
||||
if (offset > size) {
|
||||
return false;
|
||||
}
|
||||
if (size < required) {
|
||||
return false;
|
||||
}
|
||||
if ((size - offset) < required) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct UpdateDataSizes {
|
||||
u32_le behavior{};
|
||||
u32_le memory_pool{};
|
||||
u32_le voice{};
|
||||
u32_le voice_channel_resource{};
|
||||
u32_le effect{};
|
||||
u32_le mixer{};
|
||||
u32_le sink{};
|
||||
u32_le performance{};
|
||||
u32_le splitter{};
|
||||
u32_le render_info{};
|
||||
INSERT_PADDING_WORDS(4);
|
||||
};
|
||||
static_assert(sizeof(UpdateDataSizes) == 0x38, "UpdateDataSizes is an invalid size");
|
||||
|
||||
struct UpdateDataHeader {
|
||||
u32_le revision{};
|
||||
UpdateDataSizes size{};
|
||||
u32_le total_size{};
|
||||
};
|
||||
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader is an invalid size");
|
||||
|
||||
struct AudioRendererParameter {
|
||||
u32_le sample_rate;
|
||||
u32_le sample_count;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le submix_count;
|
||||
u32_le voice_count;
|
||||
u32_le sink_count;
|
||||
u32_le effect_count;
|
||||
u32_le performance_frame_count;
|
||||
u8 is_voice_drop_enabled;
|
||||
u8 unknown_21;
|
||||
u8 unknown_22;
|
||||
u8 execution_mode;
|
||||
u32_le splitter_count;
|
||||
u32_le num_splitter_send_channels;
|
||||
u32_le unknown_30;
|
||||
u32_le revision;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
|
||||
|
||||
} // namespace AudioCommon
|
||||
@@ -1,60 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "audio_core/renderer/behavior/behavior_info.h"
|
||||
#include "audio_core/renderer/memory/memory_pool_info.h"
|
||||
#include "audio_core/renderer/upsampler/upsampler_manager.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
/**
|
||||
* Execution mode of the audio renderer.
|
||||
* Only Auto is currently supported.
|
||||
*/
|
||||
enum class ExecutionMode : u8 {
|
||||
Auto,
|
||||
Manual,
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters from the game, passed to the audio renderer for initialisation.
|
||||
*/
|
||||
struct AudioRendererParameterInternal {
|
||||
/* 0x00 */ u32 sample_rate;
|
||||
/* 0x04 */ u32 sample_count;
|
||||
/* 0x08 */ u32 mixes;
|
||||
/* 0x0C */ u32 sub_mixes;
|
||||
/* 0x10 */ u32 voices;
|
||||
/* 0x14 */ u32 sinks;
|
||||
/* 0x18 */ u32 effects;
|
||||
/* 0x1C */ u32 perf_frames;
|
||||
/* 0x20 */ u16 voice_drop_enabled;
|
||||
/* 0x22 */ u8 rendering_device;
|
||||
/* 0x23 */ ExecutionMode execution_mode;
|
||||
/* 0x24 */ u32 splitter_infos;
|
||||
/* 0x28 */ s32 splitter_destinations;
|
||||
/* 0x2C */ u32 external_context_size;
|
||||
/* 0x30 */ u32 revision;
|
||||
/* 0x34 */ char unk34[0x4];
|
||||
};
|
||||
static_assert(sizeof(AudioRendererParameterInternal) == 0x38,
|
||||
"AudioRendererParameterInternal has the wrong size!");
|
||||
|
||||
/**
|
||||
* Context for rendering, contains a bunch of useful fields for the command generator.
|
||||
*/
|
||||
struct AudioRendererSystemContext {
|
||||
s32 session_id;
|
||||
s8 channels;
|
||||
s16 mix_buffer_count;
|
||||
AudioRenderer::BehaviorInfo* behavior;
|
||||
std::span<s32> depop_buffer;
|
||||
AudioRenderer::UpsamplerManager* upsampler_manager;
|
||||
AudioRenderer::MemoryPoolInfo* memory_pool_info;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,138 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
using CpuAddr = std::uintptr_t;
|
||||
|
||||
enum class PlayState : u8 {
|
||||
Started,
|
||||
Stopped,
|
||||
Paused,
|
||||
};
|
||||
|
||||
enum class SrcQuality : u8 {
|
||||
Medium,
|
||||
High,
|
||||
Low,
|
||||
};
|
||||
|
||||
enum class SampleFormat : u8 {
|
||||
Invalid,
|
||||
PcmInt8,
|
||||
PcmInt16,
|
||||
PcmInt24,
|
||||
PcmInt32,
|
||||
PcmFloat,
|
||||
Adpcm,
|
||||
};
|
||||
|
||||
enum class SessionTypes {
|
||||
AudioIn,
|
||||
AudioOut,
|
||||
FinalOutputRecorder,
|
||||
};
|
||||
|
||||
enum class Channels : u32 {
|
||||
FrontLeft,
|
||||
FrontRight,
|
||||
Center,
|
||||
LFE,
|
||||
BackLeft,
|
||||
BackRight,
|
||||
};
|
||||
|
||||
// These are used by Delay, Reverb and I3dl2Reverb prior to Revision 11.
|
||||
enum class OldChannels : u32 {
|
||||
FrontLeft,
|
||||
FrontRight,
|
||||
BackLeft,
|
||||
BackRight,
|
||||
Center,
|
||||
LFE,
|
||||
};
|
||||
|
||||
constexpr u32 BufferCount = 32;
|
||||
|
||||
constexpr u32 MaxRendererSessions = 2;
|
||||
constexpr u32 TargetSampleCount = 240;
|
||||
constexpr u32 TargetSampleRate = 48'000;
|
||||
constexpr u32 MaxChannels = 6;
|
||||
constexpr u32 MaxMixBuffers = 24;
|
||||
constexpr u32 MaxWaveBuffers = 4;
|
||||
constexpr s32 LowestVoicePriority = 0xFF;
|
||||
constexpr s32 HighestVoicePriority = 0;
|
||||
constexpr u32 BufferAlignment = 0x40;
|
||||
constexpr u32 WorkbufferAlignment = 0x1000;
|
||||
constexpr s32 FinalMixId = 0;
|
||||
constexpr s32 InvalidDistanceFromFinalMix = std::numeric_limits<s32>::min();
|
||||
constexpr s32 UnusedSplitterId = -1;
|
||||
constexpr s32 UnusedMixId = std::numeric_limits<s32>::max();
|
||||
constexpr u32 InvalidNodeId = 0xF0000000;
|
||||
constexpr s32 InvalidProcessOrder = -1;
|
||||
constexpr u32 MaxBiquadFilters = 2;
|
||||
constexpr u32 MaxEffects = 256;
|
||||
|
||||
constexpr bool IsChannelCountValid(u16 channel_count) {
|
||||
return channel_count <= 6 &&
|
||||
(channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6);
|
||||
}
|
||||
|
||||
constexpr void UseOldChannelMapping(std::span<s16> inputs, std::span<s16> outputs) {
|
||||
constexpr auto old_center{static_cast<u32>(OldChannels::Center)};
|
||||
constexpr auto new_center{static_cast<u32>(Channels::Center)};
|
||||
constexpr auto old_lfe{static_cast<u32>(OldChannels::LFE)};
|
||||
constexpr auto new_lfe{static_cast<u32>(Channels::LFE)};
|
||||
|
||||
auto center{inputs[old_center]};
|
||||
auto lfe{inputs[old_lfe]};
|
||||
inputs[old_center] = inputs[new_center];
|
||||
inputs[old_lfe] = inputs[new_lfe];
|
||||
inputs[new_center] = center;
|
||||
inputs[new_lfe] = lfe;
|
||||
|
||||
center = outputs[old_center];
|
||||
lfe = outputs[old_lfe];
|
||||
outputs[old_center] = outputs[new_center];
|
||||
outputs[old_lfe] = outputs[new_lfe];
|
||||
outputs[new_center] = center;
|
||||
outputs[new_lfe] = lfe;
|
||||
}
|
||||
|
||||
constexpr u32 GetSplitterInParamHeaderMagic() {
|
||||
return Common::MakeMagic('S', 'N', 'D', 'H');
|
||||
}
|
||||
|
||||
constexpr u32 GetSplitterInfoMagic() {
|
||||
return Common::MakeMagic('S', 'N', 'D', 'I');
|
||||
}
|
||||
|
||||
constexpr u32 GetSplitterSendDataMagic() {
|
||||
return Common::MakeMagic('S', 'N', 'D', 'D');
|
||||
}
|
||||
|
||||
constexpr size_t GetSampleFormatByteSize(SampleFormat format) {
|
||||
switch (format) {
|
||||
case SampleFormat::PcmInt8:
|
||||
return 1;
|
||||
case SampleFormat::PcmInt16:
|
||||
return 2;
|
||||
case SampleFormat::PcmInt24:
|
||||
return 3;
|
||||
case SampleFormat::PcmInt32:
|
||||
case SampleFormat::PcmFloat:
|
||||
return 4;
|
||||
default:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,105 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
constexpr u32 CurrentRevision = 11;
|
||||
|
||||
enum class SupportTags {
|
||||
CommandProcessingTimeEstimatorVersion4,
|
||||
CommandProcessingTimeEstimatorVersion3,
|
||||
CommandProcessingTimeEstimatorVersion2,
|
||||
MultiTapBiquadFilterProcessing,
|
||||
EffectInfoVer2,
|
||||
WaveBufferVer2,
|
||||
BiquadFilterFloatProcessing,
|
||||
VolumeMixParameterPrecisionQ23,
|
||||
MixInParameterDirtyOnlyUpdate,
|
||||
BiquadFilterEffectStateClearBugFix,
|
||||
VoicePlayedSampleCountResetAtLoopPoint,
|
||||
VoicePitchAndSrcSkipped,
|
||||
SplitterBugFix,
|
||||
FlushVoiceWaveBuffers,
|
||||
ElapsedFrameCount,
|
||||
AudioRendererVariadicCommandBufferSize,
|
||||
PerformanceMetricsDataFormatVersion2,
|
||||
AudioRendererProcessingTimeLimit80Percent,
|
||||
AudioRendererProcessingTimeLimit75Percent,
|
||||
AudioRendererProcessingTimeLimit70Percent,
|
||||
AdpcmLoopContextBugFix,
|
||||
Splitter,
|
||||
LongSizePreDelay,
|
||||
AudioUsbDeviceOutput,
|
||||
DeviceApiVersion2,
|
||||
DelayChannelMappingChange,
|
||||
ReverbChannelMappingChange,
|
||||
I3dl2ReverbChannelMappingChange,
|
||||
|
||||
// Not a real tag, just here to get the count.
|
||||
Size
|
||||
};
|
||||
|
||||
constexpr u32 GetRevisionNum(u32 user_revision) {
|
||||
if (user_revision >= 0x100) {
|
||||
user_revision -= Common::MakeMagic('R', 'E', 'V', '0');
|
||||
user_revision >>= 24;
|
||||
}
|
||||
return user_revision;
|
||||
};
|
||||
|
||||
constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) {
|
||||
constexpr std::array<std::pair<SupportTags, u32>, static_cast<u32>(SupportTags::Size)> features{
|
||||
{
|
||||
{SupportTags::AudioRendererProcessingTimeLimit70Percent, 1},
|
||||
{SupportTags::Splitter, 2},
|
||||
{SupportTags::AdpcmLoopContextBugFix, 2},
|
||||
{SupportTags::LongSizePreDelay, 3},
|
||||
{SupportTags::AudioUsbDeviceOutput, 4},
|
||||
{SupportTags::AudioRendererProcessingTimeLimit75Percent, 4},
|
||||
{SupportTags::VoicePlayedSampleCountResetAtLoopPoint, 5},
|
||||
{SupportTags::VoicePitchAndSrcSkipped, 5},
|
||||
{SupportTags::SplitterBugFix, 5},
|
||||
{SupportTags::FlushVoiceWaveBuffers, 5},
|
||||
{SupportTags::ElapsedFrameCount, 5},
|
||||
{SupportTags::AudioRendererProcessingTimeLimit80Percent, 5},
|
||||
{SupportTags::AudioRendererVariadicCommandBufferSize, 5},
|
||||
{SupportTags::PerformanceMetricsDataFormatVersion2, 5},
|
||||
{SupportTags::CommandProcessingTimeEstimatorVersion2, 5},
|
||||
{SupportTags::BiquadFilterEffectStateClearBugFix, 6},
|
||||
{SupportTags::BiquadFilterFloatProcessing, 7},
|
||||
{SupportTags::VolumeMixParameterPrecisionQ23, 7},
|
||||
{SupportTags::MixInParameterDirtyOnlyUpdate, 7},
|
||||
{SupportTags::WaveBufferVer2, 8},
|
||||
{SupportTags::CommandProcessingTimeEstimatorVersion3, 8},
|
||||
{SupportTags::EffectInfoVer2, 9},
|
||||
{SupportTags::CommandProcessingTimeEstimatorVersion4, 10},
|
||||
{SupportTags::MultiTapBiquadFilterProcessing, 10},
|
||||
{SupportTags::DelayChannelMappingChange, 11},
|
||||
{SupportTags::ReverbChannelMappingChange, 11},
|
||||
{SupportTags::I3dl2ReverbChannelMappingChange, 11},
|
||||
}};
|
||||
|
||||
const auto& feature =
|
||||
std::ranges::find_if(features, [tag](const auto& entry) { return entry.first == tag; });
|
||||
if (feature == features.cend()) {
|
||||
LOG_ERROR(Service_Audio, "Invalid SupportTag {}!", static_cast<u32>(tag));
|
||||
return false;
|
||||
}
|
||||
user_revision = GetRevisionNum(user_revision);
|
||||
return (*feature).second <= user_revision;
|
||||
}
|
||||
|
||||
constexpr bool CheckValidRevision(u32 user_revision) {
|
||||
return GetRevisionNum(user_revision) <= CurrentRevision;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,35 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct WaveBufferVersion1 {
|
||||
CpuAddr buffer;
|
||||
u64 buffer_size;
|
||||
u32 start_offset;
|
||||
u32 end_offset;
|
||||
bool loop;
|
||||
bool stream_ended;
|
||||
CpuAddr context;
|
||||
u64 context_size;
|
||||
};
|
||||
|
||||
struct WaveBufferVersion2 {
|
||||
CpuAddr buffer;
|
||||
CpuAddr context;
|
||||
u64 buffer_size;
|
||||
u64 context_size;
|
||||
u32 start_offset;
|
||||
u32 end_offset;
|
||||
u32 loop_start_offset;
|
||||
u32 loop_end_offset;
|
||||
s32 loop_count;
|
||||
bool loop;
|
||||
bool stream_ended;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,100 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
/**
|
||||
* Responsible for allocating up a workbuffer into multiple pieces.
|
||||
* Takes in a buffer and size (it does not own them), and allocates up the buffer via Allocate.
|
||||
*/
|
||||
class WorkbufferAllocator {
|
||||
public:
|
||||
explicit WorkbufferAllocator(std::span<u8> buffer_, u64 size_)
|
||||
: buffer{reinterpret_cast<u64>(buffer_.data())}, size{size_} {}
|
||||
|
||||
/**
|
||||
* Allocate the given count of T elements, aligned to alignment.
|
||||
*
|
||||
* @param count - The number of elements to allocate.
|
||||
* @param alignment - The required starting alignment.
|
||||
* @return Non-owning container of allocated elements.
|
||||
*/
|
||||
template <typename T>
|
||||
std::span<T> Allocate(u64 count, u64 alignment) {
|
||||
u64 out{0};
|
||||
u64 byte_size{count * sizeof(T)};
|
||||
|
||||
if (byte_size > 0) {
|
||||
auto current{buffer + offset};
|
||||
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
||||
if (aligned_buffer + byte_size <= buffer + size) {
|
||||
out = aligned_buffer;
|
||||
offset = byte_size - buffer + aligned_buffer;
|
||||
} else {
|
||||
LOG_ERROR(
|
||||
Service_Audio,
|
||||
"Allocated buffer was too small to hold new alloc.\nAllocator size={:08X}, "
|
||||
"offset={:08X}.\nAttempting to allocate {:08X} with alignment={:02X}",
|
||||
size, offset, byte_size, alignment);
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return std::span<T>(reinterpret_cast<T*>(out), count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the current offset to the given alignment.
|
||||
*
|
||||
* @param alignment - The required starting alignment.
|
||||
*/
|
||||
void Align(u64 alignment) {
|
||||
auto current{buffer + offset};
|
||||
auto aligned_buffer{Common::AlignUp(current, alignment)};
|
||||
offset = 0 - buffer + aligned_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current buffer offset.
|
||||
*
|
||||
* @return The current allocating offset.
|
||||
*/
|
||||
u64 GetCurrentOffset() const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current buffer size.
|
||||
*
|
||||
* @return The size of the current buffer.
|
||||
*/
|
||||
u64 GetSize() const {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the remaining size that can be allocated.
|
||||
*
|
||||
* @return The remaining size left in the buffer.
|
||||
*/
|
||||
u64 GetRemainingSize() const {
|
||||
return size - offset;
|
||||
}
|
||||
|
||||
private:
|
||||
/// The buffer into which we are allocating.
|
||||
u64 buffer;
|
||||
/// Size of the buffer we're allocating to.
|
||||
u64 size;
|
||||
/// Current offset into the buffer, an error will be thrown if it exceeds size.
|
||||
u64 offset{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,271 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// 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/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/ring_buffer.h"
|
||||
#include "common/settings.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class CubebSinkStream 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_, 6u)}, time_stretch{sample_rate,
|
||||
num_channels} {
|
||||
|
||||
cubeb_stream_params params{};
|
||||
params.rate = sample_rate;
|
||||
params.channels = num_channels;
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
params.prefs = CUBEB_STREAM_PREF_PERSIST;
|
||||
switch (num_channels) {
|
||||
case 1:
|
||||
params.layout = CUBEB_LAYOUT_MONO;
|
||||
break;
|
||||
case 2:
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
break;
|
||||
case 6:
|
||||
params.layout = CUBEB_LAYOUT_3F2_LFE;
|
||||
break;
|
||||
}
|
||||
|
||||
u32 minimum_latency{};
|
||||
if (cubeb_get_min_latency(ctx, ¶ms, &minimum_latency) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
|
||||
}
|
||||
|
||||
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
|
||||
¶ms, std::max(512u, minimum_latency),
|
||||
&CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback,
|
||||
this) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cubeb_stream_start(stream_backend) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error starting cubeb stream");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
~CubebSinkStream() override {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cubeb_stream_stop(stream_backend) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
||||
}
|
||||
|
||||
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
|
||||
ASSERT_MSG(source_num_channels == 6, "Channel count must be 6");
|
||||
|
||||
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) {
|
||||
// Downmixing implementation taken from the ATSC standard
|
||||
const s16 left{samples[i + 0]};
|
||||
const s16 right{samples[i + 1]};
|
||||
const s16 center{samples[i + 2]};
|
||||
const s16 surround_left{samples[i + 4]};
|
||||
const s16 surround_right{samples[i + 5]};
|
||||
// Not used in the ATSC reference implementation
|
||||
[[maybe_unused]] const s16 low_frequency_effects{samples[i + 3]};
|
||||
|
||||
constexpr s32 clev{707}; // center mixing level coefficient
|
||||
constexpr s32 slev{707}; // surround mixing level coefficient
|
||||
|
||||
buf.push_back(static_cast<s16>(left + (clev * center / 1000) +
|
||||
(slev * surround_left / 1000)));
|
||||
buf.push_back(static_cast<s16>(right + (clev * center / 1000) +
|
||||
(slev * surround_right / 1000)));
|
||||
}
|
||||
queue.Push(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
queue.Push(samples);
|
||||
}
|
||||
|
||||
std::size_t SamplesInQueue(u32 channel_count) const override {
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
return queue.Size() / channel_count;
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
should_flush = true;
|
||||
}
|
||||
|
||||
u32 GetNumChannels() const {
|
||||
return num_channels;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> device_list;
|
||||
|
||||
cubeb* ctx{};
|
||||
cubeb_stream* stream_backend{};
|
||||
u32 num_channels{};
|
||||
|
||||
Common::RingBuffer<s16, 0x10000> queue;
|
||||
std::array<s16, 2> last_frame{};
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames);
|
||||
static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
|
||||
};
|
||||
|
||||
CubebSink::CubebSink(std::string_view target_device_name) {
|
||||
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
|
||||
#ifdef _WIN32
|
||||
com_init_result = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
#endif
|
||||
|
||||
if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
cubeb_device_collection collection;
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||
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;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
}
|
||||
cubeb_device_collection_destroy(ctx, &collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CubebSink::~CubebSink() {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& sink_stream : sink_streams) {
|
||||
sink_stream.reset();
|
||||
}
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (SUCCEEDED(com_init_result)) {
|
||||
CoUninitialize();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
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));
|
||||
return *sink_streams.back();
|
||||
}
|
||||
|
||||
long CubebSinkStream::DataCallback([[maybe_unused]] cubeb_stream* stream, void* user_data,
|
||||
[[maybe_unused]] const void* input_buffer, void* output_buffer,
|
||||
long num_frames) {
|
||||
auto* impl = static_cast<CubebSinkStream*>(user_data);
|
||||
auto* buffer = static_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;
|
||||
|
||||
/*
|
||||
if (Settings::values.enable_audio_stretching.GetValue()) {
|
||||
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;
|
||||
|
||||
if (impl->should_flush) {
|
||||
impl->time_stretch.Flush();
|
||||
impl->should_flush = false;
|
||||
}
|
||||
} else {
|
||||
samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
}*/
|
||||
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));
|
||||
}
|
||||
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
void CubebSinkStream::StateCallback([[maybe_unused]] cubeb_stream* stream,
|
||||
[[maybe_unused]] void* user_data,
|
||||
[[maybe_unused]] cubeb_state state) {}
|
||||
|
||||
std::vector<std::string> ListCubebSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return {};
|
||||
}
|
||||
|
||||
cubeb_device_collection collection;
|
||||
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++) {
|
||||
const cubeb_device_info& device = collection.device[i];
|
||||
if (device.friendly_name) {
|
||||
device_list.emplace_back(device.friendly_name);
|
||||
}
|
||||
}
|
||||
cubeb_device_collection_destroy(ctx, &collection);
|
||||
}
|
||||
|
||||
cubeb_destroy(ctx);
|
||||
return device_list;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cubeb/cubeb.h>
|
||||
|
||||
#include "audio_core/sink.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class CubebSink final : public Sink {
|
||||
public:
|
||||
explicit CubebSink(std::string_view device_id);
|
||||
~CubebSink() override;
|
||||
|
||||
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) override;
|
||||
|
||||
private:
|
||||
cubeb* ctx{};
|
||||
cubeb_devid output_device{};
|
||||
std::vector<SinkStreamPtr> sink_streams;
|
||||
|
||||
#ifdef _WIN32
|
||||
u32 com_init_result = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
std::vector<std::string> ListCubebSinkDevices();
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "audio_core/delay_line.h"
|
||||
|
||||
namespace AudioCore {
|
||||
DelayLineBase::DelayLineBase() = default;
|
||||
DelayLineBase::~DelayLineBase() = default;
|
||||
|
||||
void DelayLineBase::Initialize(s32 max_delay_, float* src_buffer) {
|
||||
buffer = src_buffer;
|
||||
buffer_end = buffer + max_delay_;
|
||||
max_delay = max_delay_;
|
||||
output = buffer;
|
||||
SetDelay(max_delay_);
|
||||
Clear();
|
||||
}
|
||||
|
||||
void DelayLineBase::SetDelay(s32 new_delay) {
|
||||
if (max_delay < new_delay) {
|
||||
return;
|
||||
}
|
||||
delay = new_delay;
|
||||
input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetDelay() const {
|
||||
return delay;
|
||||
}
|
||||
|
||||
s32 DelayLineBase::GetMaxDelay() const {
|
||||
return max_delay;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::TapOut(s32 last_sample) {
|
||||
const float* ptr = input - (last_sample + 1);
|
||||
if (ptr < buffer) {
|
||||
ptr += (max_delay + 1);
|
||||
}
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::Tick(f32 sample) {
|
||||
*(input++) = sample;
|
||||
const auto out_sample = *(output++);
|
||||
|
||||
if (buffer_end < input) {
|
||||
input = buffer;
|
||||
}
|
||||
|
||||
if (buffer_end < output) {
|
||||
output = buffer;
|
||||
}
|
||||
|
||||
return out_sample;
|
||||
}
|
||||
|
||||
float* DelayLineBase::GetInput() {
|
||||
return input;
|
||||
}
|
||||
|
||||
const float* DelayLineBase::GetInput() const {
|
||||
return input;
|
||||
}
|
||||
|
||||
f32 DelayLineBase::GetOutputSample() const {
|
||||
return *output;
|
||||
}
|
||||
|
||||
void DelayLineBase::Clear() {
|
||||
std::memset(buffer, 0, sizeof(float) * max_delay);
|
||||
}
|
||||
|
||||
void DelayLineBase::Reset() {
|
||||
buffer = nullptr;
|
||||
buffer_end = nullptr;
|
||||
max_delay = 0;
|
||||
input = nullptr;
|
||||
output = nullptr;
|
||||
delay = 0;
|
||||
}
|
||||
|
||||
DelayLineAllPass::DelayLineAllPass() = default;
|
||||
DelayLineAllPass::~DelayLineAllPass() = default;
|
||||
|
||||
void DelayLineAllPass::Initialize(u32 delay_, float coeffcient_, f32* src_buffer) {
|
||||
DelayLineBase::Initialize(delay_, src_buffer);
|
||||
SetCoefficient(coeffcient_);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::SetCoefficient(float coeffcient_) {
|
||||
coefficient = coeffcient_;
|
||||
}
|
||||
|
||||
f32 DelayLineAllPass::Tick(f32 sample) {
|
||||
const auto temp = sample - coefficient * *output;
|
||||
return coefficient * temp + DelayLineBase::Tick(temp);
|
||||
}
|
||||
|
||||
void DelayLineAllPass::Reset() {
|
||||
coefficient = 0.0f;
|
||||
DelayLineBase::Reset();
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class DelayLineBase {
|
||||
public:
|
||||
DelayLineBase();
|
||||
~DelayLineBase();
|
||||
|
||||
void Initialize(s32 max_delay_, float* src_buffer);
|
||||
void SetDelay(s32 new_delay);
|
||||
s32 GetDelay() const;
|
||||
s32 GetMaxDelay() const;
|
||||
f32 TapOut(s32 last_sample);
|
||||
f32 Tick(f32 sample);
|
||||
float* GetInput();
|
||||
const float* GetInput() const;
|
||||
f32 GetOutputSample() const;
|
||||
void Clear();
|
||||
void Reset();
|
||||
|
||||
protected:
|
||||
float* buffer{nullptr};
|
||||
float* buffer_end{nullptr};
|
||||
s32 max_delay{};
|
||||
float* input{nullptr};
|
||||
float* output{nullptr};
|
||||
s32 delay{};
|
||||
};
|
||||
|
||||
class DelayLineAllPass final : public DelayLineBase {
|
||||
public:
|
||||
DelayLineAllPass();
|
||||
~DelayLineAllPass();
|
||||
|
||||
void Initialize(u32 delay, float coeffcient_, f32* src_buffer);
|
||||
void SetCoefficient(float coeffcient_);
|
||||
f32 Tick(f32 sample);
|
||||
void Reset();
|
||||
|
||||
private:
|
||||
float coefficient{};
|
||||
};
|
||||
} // namespace AudioCore
|
||||
@@ -1,21 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct AudioBuffer {
|
||||
/// Timestamp this buffer completed playing.
|
||||
s64 played_timestamp;
|
||||
/// Game memory address for these samples.
|
||||
VAddr samples;
|
||||
/// Unqiue identifier for this buffer.
|
||||
u64 tag;
|
||||
/// Size of the samples buffer.
|
||||
u64 size;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,304 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_buffer.h"
|
||||
#include "audio_core/device/device_session.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr s32 BufferAppendLimit = 4;
|
||||
|
||||
/**
|
||||
* A ringbuffer of N audio buffers.
|
||||
* The buffer contains 3 sections:
|
||||
* Appended - Buffers added to the ring, but have yet to be sent to the audio backend.
|
||||
* Registered - Buffers sent to the backend and queued for playback.
|
||||
* Released - Buffers which have been played, and can now be recycled.
|
||||
* Any others are free/untracked.
|
||||
*
|
||||
* @tparam N - Maximum number of buffers in the ring.
|
||||
*/
|
||||
template <size_t N>
|
||||
class AudioBuffers {
|
||||
public:
|
||||
explicit AudioBuffers(size_t limit) : append_limit{static_cast<u32>(limit)} {}
|
||||
|
||||
/**
|
||||
* Append a new audio buffer to the ring.
|
||||
*
|
||||
* @param buffer - The new buffer.
|
||||
*/
|
||||
void AppendBuffer(AudioBuffer& buffer) {
|
||||
std::scoped_lock l{lock};
|
||||
buffers[appended_index] = buffer;
|
||||
appended_count++;
|
||||
appended_index = (appended_index + 1) % append_limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register waiting buffers, up to a maximum of BufferAppendLimit.
|
||||
*
|
||||
* @param out_buffers - The buffers which were registered.
|
||||
*/
|
||||
void RegisterBuffers(std::vector<AudioBuffer>& out_buffers) {
|
||||
std::scoped_lock l{lock};
|
||||
const s32 to_register{std::min(std::min(appended_count, BufferAppendLimit),
|
||||
BufferAppendLimit - registered_count)};
|
||||
|
||||
for (s32 i = 0; i < to_register; i++) {
|
||||
s32 index{appended_index - appended_count};
|
||||
if (index < 0) {
|
||||
index += N;
|
||||
}
|
||||
out_buffers.push_back(buffers[index]);
|
||||
registered_count++;
|
||||
registered_index = (registered_index + 1) % append_limit;
|
||||
|
||||
appended_count--;
|
||||
if (appended_count == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release a single buffer. Must be already registered.
|
||||
*
|
||||
* @param index - The buffer index to release.
|
||||
* @param timestamp - The released timestamp for this buffer.
|
||||
*/
|
||||
void ReleaseBuffer(s32 index, s64 timestamp) {
|
||||
std::scoped_lock l{lock};
|
||||
buffers[index].played_timestamp = timestamp;
|
||||
|
||||
registered_count--;
|
||||
released_count++;
|
||||
released_index = (released_index + 1) % append_limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all registered buffers.
|
||||
*
|
||||
* @param timestamp - The released timestamp for this buffer.
|
||||
* @return Is the buffer was released.
|
||||
*/
|
||||
bool ReleaseBuffers(Core::Timing::CoreTiming& core_timing, DeviceSession& session) {
|
||||
std::scoped_lock l{lock};
|
||||
bool buffer_released{false};
|
||||
while (registered_count > 0) {
|
||||
auto index{registered_index - registered_count};
|
||||
if (index < 0) {
|
||||
index += N;
|
||||
}
|
||||
|
||||
// Check with the backend if this buffer can be released yet.
|
||||
if (!session.IsBufferConsumed(buffers[index].tag)) {
|
||||
break;
|
||||
}
|
||||
|
||||
ReleaseBuffer(index, core_timing.GetGlobalTimeNs().count());
|
||||
buffer_released = true;
|
||||
}
|
||||
|
||||
return buffer_released || registered_count == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all released buffers.
|
||||
*
|
||||
* @param tags - Container to be filled with the released buffers' tags.
|
||||
* @return The number of buffers released.
|
||||
*/
|
||||
u32 GetReleasedBuffers(std::span<u64> tags) {
|
||||
std::scoped_lock l{lock};
|
||||
u32 released{0};
|
||||
|
||||
while (released_count > 0) {
|
||||
auto index{released_index - released_count};
|
||||
if (index < 0) {
|
||||
index += N;
|
||||
}
|
||||
|
||||
auto& buffer{buffers[index]};
|
||||
released_count--;
|
||||
|
||||
auto tag{buffer.tag};
|
||||
buffer.played_timestamp = 0;
|
||||
buffer.samples = 0;
|
||||
buffer.tag = 0;
|
||||
buffer.size = 0;
|
||||
|
||||
if (tag == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
tags[released++] = tag;
|
||||
|
||||
if (released >= tags.size()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return released;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all appended and registered buffers.
|
||||
*
|
||||
* @param buffers_flushed - Output vector for the buffers which are released.
|
||||
* @param max_buffers - Maximum number of buffers to released.
|
||||
* @return The number of buffers released.
|
||||
*/
|
||||
u32 GetRegisteredAppendedBuffers(std::vector<AudioBuffer>& buffers_flushed, u32 max_buffers) {
|
||||
std::scoped_lock l{lock};
|
||||
if (registered_count + appended_count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t buffers_to_flush{
|
||||
std::min(static_cast<u32>(registered_count + appended_count), max_buffers)};
|
||||
if (buffers_to_flush == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (registered_count > 0) {
|
||||
auto index{registered_index - registered_count};
|
||||
if (index < 0) {
|
||||
index += N;
|
||||
}
|
||||
|
||||
buffers_flushed.push_back(buffers[index]);
|
||||
|
||||
registered_count--;
|
||||
released_count++;
|
||||
released_index = (released_index + 1) % append_limit;
|
||||
|
||||
if (buffers_flushed.size() >= buffers_to_flush) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (appended_count > 0) {
|
||||
auto index{appended_index - appended_count};
|
||||
if (index < 0) {
|
||||
index += N;
|
||||
}
|
||||
|
||||
buffers_flushed.push_back(buffers[index]);
|
||||
|
||||
appended_count--;
|
||||
released_count++;
|
||||
released_index = (released_index + 1) % append_limit;
|
||||
|
||||
if (buffers_flushed.size() >= buffers_to_flush) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<u32>(buffers_flushed.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given tag is in the buffers.
|
||||
*
|
||||
* @param tag - Unique tag of the buffer to search for.
|
||||
* @return True if the buffer is still in the ring, otherwise false.
|
||||
*/
|
||||
bool ContainsBuffer(const u64 tag) const {
|
||||
std::scoped_lock l{lock};
|
||||
const auto registered_buffers{appended_count + registered_count + released_count};
|
||||
|
||||
if (registered_buffers == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto index{released_index - released_count};
|
||||
if (index < 0) {
|
||||
index += append_limit;
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < registered_buffers; i++) {
|
||||
if (buffers[index].tag == tag) {
|
||||
return true;
|
||||
}
|
||||
index = (index + 1) % append_limit;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of active buffers in the ring.
|
||||
* That is, appended, registered and released buffers.
|
||||
*
|
||||
* @return Number of active buffers.
|
||||
*/
|
||||
u32 GetAppendedRegisteredCount() const {
|
||||
std::scoped_lock l{lock};
|
||||
return appended_count + registered_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of active buffers in the ring.
|
||||
* That is, appended, registered and released buffers.
|
||||
*
|
||||
* @return Number of active buffers.
|
||||
*/
|
||||
u32 GetTotalBufferCount() const {
|
||||
std::scoped_lock l{lock};
|
||||
return static_cast<u32>(appended_count + registered_count + released_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all of the currently appended and registered buffers
|
||||
*
|
||||
* @param buffers_released - Output count for the number of buffers released.
|
||||
* @return True if buffers were successfully flushed, otherwise false.
|
||||
*/
|
||||
bool FlushBuffers(u32& buffers_released) {
|
||||
std::scoped_lock l{lock};
|
||||
std::vector<AudioBuffer> buffers_flushed{};
|
||||
|
||||
buffers_released = GetRegisteredAppendedBuffers(buffers_flushed, append_limit);
|
||||
|
||||
if (registered_count > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<u32>(released_count + appended_count) > append_limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Buffer lock
|
||||
mutable std::recursive_mutex lock{};
|
||||
/// The audio buffers
|
||||
std::array<AudioBuffer, N> buffers{};
|
||||
/// Current released index
|
||||
s32 released_index{};
|
||||
/// Number of released buffers
|
||||
s32 released_count{};
|
||||
/// Current registered index
|
||||
s32 registered_index{};
|
||||
/// Number of registered buffers
|
||||
s32 registered_count{};
|
||||
/// Current appended index
|
||||
s32 appended_index{};
|
||||
/// Number of appended buffers
|
||||
s32 appended_count{};
|
||||
/// Maximum number of buffers (default 32)
|
||||
u32 append_limit{};
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
Loaded 100 of 1759 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user