mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-01-31 01:15:24 +01:00
Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
517fba3686 | ||
|
|
64e17fce3f | ||
|
|
518728ca36 | ||
|
|
d1a7430f80 | ||
|
|
973c522756 | ||
|
|
5536342f9d | ||
|
|
d9ddab1a33 | ||
|
|
c915aac1fa | ||
|
|
a3c2a4db5f | ||
|
|
aebebf5115 | ||
|
|
9a50218400 | ||
|
|
fd5a652270 | ||
|
|
c647a30aa9 | ||
|
|
40d5c78573 | ||
|
|
fe2a9fc2cd | ||
|
|
83f9add68b | ||
|
|
ffe8d16df4 | ||
|
|
31b5672870 | ||
|
|
777fc444ae | ||
|
|
cd5c961dc4 | ||
|
|
c2ea8c4eab | ||
|
|
d6507a945b | ||
|
|
f1abee5d0b | ||
|
|
e4dccc237c | ||
|
|
92d8c740d4 | ||
|
|
b6eddade00 | ||
|
|
04fdd09d1e | ||
|
|
69574aecc6 | ||
|
|
c72d98d4cd | ||
|
|
4f405adea9 | ||
|
|
5217ec375d | ||
|
|
10b264b9ec | ||
|
|
dc741e95bd | ||
|
|
a1ca0a9a02 | ||
|
|
c076c50b68 | ||
|
|
c688db8f08 | ||
|
|
469053de9b | ||
|
|
185b85f86a | ||
|
|
11096a520e | ||
|
|
07fd035aa2 | ||
|
|
6bedcd768f | ||
|
|
0c949db506 | ||
|
|
4c5253c0f5 | ||
|
|
325133e54c | ||
|
|
4113ac574d | ||
|
|
2d6c687913 | ||
|
|
f9fbecc783 | ||
|
|
c5ceba35b6 | ||
|
|
c7a53703b1 | ||
|
|
2151ffcb6a | ||
|
|
e25cb92041 | ||
|
|
c3ed4faec1 | ||
|
|
01de02677f | ||
|
|
6c44e985b1 | ||
|
|
31026e420d | ||
|
|
f2523bdf9f | ||
|
|
5c4d814b63 | ||
|
|
752f479483 | ||
|
|
3a53144bdb | ||
|
|
b0940094f0 | ||
|
|
92904994ef | ||
|
|
112d02eaf5 | ||
|
|
deb8e5bf18 | ||
|
|
fe3eab5815 | ||
|
|
72034da6ac | ||
|
|
2106c353d9 | ||
|
|
27b681bb4c | ||
|
|
ad3e66f903 | ||
|
|
3f7190e884 | ||
|
|
e2fd09df88 | ||
|
|
ebc3923b35 | ||
|
|
30e7de7555 | ||
|
|
15dfc0496e | ||
|
|
3a55653c3e | ||
|
|
1df2fdff3e | ||
|
|
dd16ff5021 | ||
|
|
2633bc4d59 | ||
|
|
e7a8c2b037 | ||
|
|
e292c0986f | ||
|
|
2320330b6a | ||
|
|
8cd12852ce | ||
|
|
51019dc456 | ||
|
|
5a35afadd7 | ||
|
|
63e516ebfe | ||
|
|
7086060724 | ||
|
|
93e4bd8215 | ||
|
|
85fc04598b | ||
|
|
36d4f2602f | ||
|
|
129be9dfe4 | ||
|
|
ed9b82097d | ||
|
|
2fea312187 | ||
|
|
c6b8df0588 | ||
|
|
40cd0d898f | ||
|
|
d6e57f3867 | ||
|
|
c3a5b13301 | ||
|
|
1c7b1d7ff9 | ||
|
|
a1ac86934e | ||
|
|
b0026ff59f | ||
|
|
a5a8a1a06b | ||
|
|
132431b7c8 | ||
|
|
e045d4a0ff | ||
|
|
a41be51385 | ||
|
|
acb0829d81 | ||
|
|
02e0d2acde | ||
|
|
6b6993cc24 | ||
|
|
3af50632d1 | ||
|
|
cd7cb18274 | ||
|
|
7a99ed3c8e | ||
|
|
7d0eaf9a05 | ||
|
|
0b59b930a3 | ||
|
|
5dc015c0a3 | ||
|
|
edd39dfe08 | ||
|
|
f5320e201e | ||
|
|
37569d1526 | ||
|
|
1efeab4078 | ||
|
|
2bff3579e5 | ||
|
|
2fa85c56c2 | ||
|
|
ded55635c1 | ||
|
|
c9b8874eb6 | ||
|
|
9172a1a6f8 | ||
|
|
b2ac7afbcb | ||
|
|
dcfd663c19 | ||
|
|
225f057a5c | ||
|
|
0017e33023 | ||
|
|
76ced16f9e | ||
|
|
245937eb85 | ||
|
|
3a72328d55 | ||
|
|
06bc0300c0 | ||
|
|
98dcf40967 | ||
|
|
6b61ffbb63 | ||
|
|
dd7eef723a | ||
|
|
328275cd45 | ||
|
|
a4f1431bcc | ||
|
|
473df8dad4 | ||
|
|
042959958e | ||
|
|
cd4de28b9e | ||
|
|
24a87c3fee | ||
|
|
17567b960f | ||
|
|
7a3d22ecdf | ||
|
|
7e5016fdab | ||
|
|
47606400fa | ||
|
|
d6076a6107 | ||
|
|
95c57462cc | ||
|
|
48a1ec3531 | ||
|
|
9da3bccca2 | ||
|
|
a1a92920b2 | ||
|
|
b6b775e44e | ||
|
|
5ea46ac076 | ||
|
|
ab008bf5d0 | ||
|
|
54782cbf70 | ||
|
|
3c7cff99f4 | ||
|
|
f326e8775f | ||
|
|
a2a711b1b3 | ||
|
|
dfb857b68f | ||
|
|
ad64d88e7b | ||
|
|
cbd207d3f4 | ||
|
|
4bf8b23204 | ||
|
|
951780b43d | ||
|
|
84fe413635 | ||
|
|
17aaa31362 | ||
|
|
f943bdad98 |
2
.github/ISSUE_TEMPLATE/emu_bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/emu_bug_report.yaml
vendored
@@ -18,6 +18,8 @@ body:
|
||||
|
||||
Please make an effort to make sure your issue isn't already reported.
|
||||
|
||||
Please make sure your game is verified using the built-in game verifier.
|
||||
|
||||
Do not create issues involving software piracy of BIOS or ISO files, our rules specifically prohibit this and your issue will be closed.
|
||||
|
||||
### Please Avoid Issues Pertaining to the Following:
|
||||
|
||||
8
.github/workflows/cron_publish_flatpak.yml
vendored
8
.github/workflows/cron_publish_flatpak.yml
vendored
@@ -23,12 +23,18 @@ jobs:
|
||||
FLATHUB_RELEASE=$(curl -L -s https://flathub.org/api/v2/appstream/net.pcsx2.PCSX2 | jq -r '.releases | max_by(.version) | .version')
|
||||
echo "Latest PCSX2 release is: '${PCSX2_RELEASE}'"
|
||||
echo "Latest Flathub release is: '${FLATHUB_RELEASE}'"
|
||||
PCSX2_RELEASE=$(echo $PCSX2_RELEASE | sed 's/[^0-9]*//g')
|
||||
FLATHUB_RELEASE=$(echo $FLATHUB_RELEASE | sed 's/[^0-9]*//g')
|
||||
echo "PCSX2_RELEASE=${PCSX2_RELEASE}" >> "$GITHUB_OUTPUT"
|
||||
echo "FLATHUB_RELEASE=${FLATHUB_RELEASE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
needs: check
|
||||
if: needs.check.outputs.FLATHUB_RELEASE < needs.check.outputs.PCSX2_RELEASE
|
||||
# outputs are automatically compared as strings. This doesn't work in our favour
|
||||
# Use fromJson() to convert them to proper integers...
|
||||
# see: https://github.com/github/docs/pull/25870
|
||||
# and: https://github.com/orgs/community/discussions/57480
|
||||
#if: fromJson(needs.check.outputs.FLATHUB_RELEASE) < fromJson(needs.check.outputs.PCSX2_RELEASE)
|
||||
name: "Build and publish Flatpak"
|
||||
uses: ./.github/workflows/linux_build_flatpak.yml
|
||||
with:
|
||||
|
||||
@@ -25,5 +25,6 @@ jobs:
|
||||
commit-message: "PAD: Update to latest controller database."
|
||||
committer: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
|
||||
author: "PCSX2 Bot <PCSX2Bot@users.noreply.github.com>"
|
||||
body: "Weekly automatic update of SDL Controller DB"
|
||||
reviewers: lightningterror
|
||||
body: "Weekly automatic update of SDL Controller DB."
|
||||
branch: update-controller-db
|
||||
delete-branch: true
|
||||
|
||||
6
.github/workflows/linux_build_flatpak.yml
vendored
6
.github/workflows/linux_build_flatpak.yml
vendored
@@ -94,12 +94,6 @@ jobs:
|
||||
cd bin/resources
|
||||
wget "${{ inputs.patchesUrl }}/patches.zip"
|
||||
|
||||
- name: Generate AppStream XML
|
||||
run: |
|
||||
./.github/workflows/scripts/linux/generate-metainfo.sh .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml
|
||||
cat .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml
|
||||
# flatpak-builder-lint appstream .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml
|
||||
|
||||
- name: Validate manifest
|
||||
run: |
|
||||
flatpak-builder-lint manifest .github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.json
|
||||
|
||||
10
.github/workflows/linux_build_qt.yml
vendored
10
.github/workflows/linux_build_qt.yml
vendored
@@ -58,16 +58,10 @@ jobs:
|
||||
CCACHE_MAXSIZE: 100M
|
||||
|
||||
steps:
|
||||
# Work around https://github.com/actions/runner-images/issues/8659
|
||||
- name: Remove GCC 13 from runner image
|
||||
shell: bash
|
||||
run: |
|
||||
sudo rm -f /etc/apt/sources.list.d/ubuntu-toolchain-r-ubuntu-test-jammy.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --allow-downgrades 'libc6=2.35-0ubuntu*' 'libc6-dev=2.35-0ubuntu*' libstdc++6=12.3.0-1ubuntu1~22.04 libgcc-s1=12.3.0-1ubuntu1~22.04
|
||||
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# actions/checkout elides tags, fetch them primarily for releases
|
||||
- name: Fetch Tags
|
||||
|
||||
25
.github/workflows/scripts/linux/appimage-qt.sh
vendored
25
.github/workflows/scripts/linux/appimage-qt.sh
vendored
@@ -63,9 +63,9 @@ declare -a REMOVE_LIBS=(
|
||||
|
||||
set -e
|
||||
|
||||
LINUXDEPLOY=./linuxdeploy-x86_64.AppImage
|
||||
LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64.AppImage
|
||||
APPIMAGETOOL=./appimagetool-x86_64.AppImage
|
||||
LINUXDEPLOY=./linuxdeploy-x86_64
|
||||
LINUXDEPLOY_PLUGIN_QT=./linuxdeploy-plugin-qt-x86_64
|
||||
APPIMAGETOOL=./appimagetool-x86_64
|
||||
PATCHELF=patchelf
|
||||
|
||||
if [ ! -f "$LINUXDEPLOY" ]; then
|
||||
@@ -78,8 +78,11 @@ if [ ! -f "$LINUXDEPLOY_PLUGIN_QT" ]; then
|
||||
chmod +x "$LINUXDEPLOY_PLUGIN_QT"
|
||||
fi
|
||||
|
||||
# Using go-appimage
|
||||
# Backported from https://github.com/stenzek/duckstation/pull/3251
|
||||
if [ ! -f "$APPIMAGETOOL" ]; then
|
||||
"$PCSX2DIR/tools/retry.sh" wget -O "$APPIMAGETOOL" https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
APPIMAGETOOLURL=$(wget -q https://api.github.com/repos/probonopd/go-appimage/releases -O - | sed 's/[()",{} ]/\n/g' | grep -o 'https.*continuous.*tool.*86_64.*mage$' | head -1)
|
||||
"$PCSX2DIR/tools/retry.sh" wget -O "$APPIMAGETOOL" "$APPIMAGETOOLURL"
|
||||
chmod +x "$APPIMAGETOOL"
|
||||
fi
|
||||
|
||||
@@ -199,6 +202,16 @@ for hookpath in "$SCRIPTDIR/apprun-hooks"/*; do
|
||||
done
|
||||
|
||||
echo "Generating AppImage..."
|
||||
rm -f "$NAME.AppImage"
|
||||
$APPIMAGETOOL -v "$OUTDIR" "$NAME.AppImage"
|
||||
GIT_VERSION=$(git tag --points-at HEAD)
|
||||
|
||||
if [[ "${GIT_VERSION}" == "" ]]; then
|
||||
# In the odd event that we run this script before the release gets tagged.
|
||||
GIT_VERSION=$(git describe --tags)
|
||||
if [[ "${GIT_VERSION}" == "" ]]; then
|
||||
GIT_VERSION=$(git rev-parse HEAD)
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f "$NAME.AppImage"
|
||||
ARCH=x86_64 VERSION="${GIT_VERSION}" "$APPIMAGETOOL" -s "$OUTDIR" && mv ./*.AppImage "$NAME.AppImage"
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ LIBJPEG=9f
|
||||
LIBPNG=1.6.43
|
||||
LIBWEBP=1.4.0
|
||||
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
|
||||
SDL=SDL2-2.30.5
|
||||
SDL=SDL2-2.30.6
|
||||
QT=6.7.2
|
||||
ZSTD=1.5.6
|
||||
|
||||
@@ -37,7 +37,7 @@ fd6f417fe9e3a071cf1424a5152d926a34c4a3c5070745470be6cf12a404ed79 $LIBBACKTRACE.
|
||||
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-$LIBPNG.tar.xz
|
||||
61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 libwebp-$LIBWEBP.tar.gz
|
||||
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
|
||||
f374f3fa29c37dfcc20822d4a7d7dc57e58924d1a5f2ad511bfab4c8193de63b $SDL.tar.gz
|
||||
c6ef64ca18a19d13df6eb22df9aff19fb0db65610a74cc81dae33a82235cacd4 $SDL.tar.gz
|
||||
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
|
||||
c5f22a5e10fb162895ded7de0963328e7307611c688487b5d152c9ee64767599 qtbase-everywhere-src-$QT.tar.xz
|
||||
e1a1d8785fae67d16ad0a443b01d5f32663a6b68d275f1806ebab257485ce5d6 qtimageformats-everywhere-src-$QT.tar.xz
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"sources": [
|
||||
{
|
||||
"type": "archive",
|
||||
"url": "https://libsdl.org/release/SDL2-2.30.5.tar.gz",
|
||||
"sha256": "f374f3fa29c37dfcc20822d4a7d7dc57e58924d1a5f2ad511bfab4c8193de63b"
|
||||
"url": "https://libsdl.org/release/SDL2-2.30.6.tar.gz",
|
||||
"sha256": "c6ef64ca18a19d13df6eb22df9aff19fb0db65610a74cc81dae33a82235cacd4"
|
||||
}
|
||||
],
|
||||
"cleanup": [
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"add-extensions": {
|
||||
"org.freedesktop.Platform.ffmpeg-full": {
|
||||
"directory": "lib/ffmpeg",
|
||||
"version": "22.08",
|
||||
"version": "23.08",
|
||||
"add-ld-path": ".",
|
||||
"autodownload": true
|
||||
}
|
||||
@@ -51,7 +51,7 @@
|
||||
"-DCMAKE_SHARED_LINKER_FLAGS_INIT=-fuse-ld=lld",
|
||||
"-DUSE_LINKED_FFMPEG=ON",
|
||||
"-DDISABLE_ADVANCE_SIMD=TRUE"
|
||||
]
|
||||
]
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
@@ -63,8 +63,10 @@
|
||||
"cp -a bin \"${FLATPAK_DEST}\"",
|
||||
"install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/bin/resources/icons/AppIconLarge.png\" \"${FLATPAK_DEST}/share/icons/hicolor/512x512/apps/net.pcsx2.PCSX2.png\"",
|
||||
"install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/.github/workflows/scripts/linux/pcsx2-qt.desktop\" \"${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop\"",
|
||||
"desktop-file-edit --set-key=Icon --set-value=net.pcsx2.PCSX2 \"${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop\"",
|
||||
"install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/.github/workflows/scripts/linux/flatpak/net.pcsx2.PCSX2.metainfo.xml\" \"${FLATPAK_DEST}/share/metainfo/net.pcsx2.PCSX2.metainfo.xml\"",
|
||||
"desktop-file-edit --set-key=Icon --set-value=net.pcsx2.PCSX2 \"${FLATPAK_DEST}/share/applications/net.pcsx2.PCSX2.desktop\"",
|
||||
"${FLATPAK_BUILDER_BUILDDIR}/.github/workflows/scripts/linux/generate-metainfo.sh \"${FLATPAK_BUILDER_BUILDDIR}/net.pcsx2.PCSX2.metainfo.xml\"",
|
||||
"cat \"${FLATPAK_BUILDER_BUILDDIR}/net.pcsx2.PCSX2.metainfo.xml\"",
|
||||
"install -Dm644 \"${FLATPAK_BUILDER_BUILDDIR}/net.pcsx2.PCSX2.metainfo.xml\" \"${FLATPAK_DEST}/share/metainfo/net.pcsx2.PCSX2.metainfo.xml\"",
|
||||
"mkdir -p \"${FLATPAK_DEST}/lib/ffmpeg\""
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,33 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Uses the AppStream standard https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html -->
|
||||
<component type="desktop">
|
||||
<id>net.pcsx2.PCSX2</id>
|
||||
<launchable type="desktop-id">net.pcsx2.PCSX2.desktop</launchable>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>GPL-3.0</project_license>
|
||||
<project_license>GPL-3.0+</project_license>
|
||||
<name>PCSX2</name>
|
||||
<developer_name>PCSX2</developer_name>
|
||||
<summary>PlayStation 2 Emulator</summary>
|
||||
<description>
|
||||
<p>PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your PC, with many additional features and benefits.</p>
|
||||
<p>PCSX2 is a free and open-source PlayStation 2 (PS2) emulator. Its purpose is to emulate the PS2's hardware, using a combination of MIPS CPU Interpreters, Recompilers, and a Virtual Machine which manages hardware states and PS2 system memory. This allows you to play PS2 games on your PC, with many additional features and benefits.</p>
|
||||
<p>PlayStation 2 and PS2 are registered trademarks of Sony Interactive Entertainment. This application is not affiliated in any way with Sony Interactive Entertainment.</p>
|
||||
</description>
|
||||
<url type="homepage">https://pcsx2.net/</url>
|
||||
<url type="bugtracker">https://github.com/PCSX2/pcsx2/issues</url>
|
||||
<url type="donation">https://github.com/sponsors/PCSX2</url>
|
||||
<url type="faq">https://pcsx2.net/docs/</url>
|
||||
<url type="help">https://discord.com/invite/TCz3t9k</url>
|
||||
<url type="help">https://pcsx2.net/discord</url>
|
||||
<url type="translate">https://crowdin.com/project/pcsx2-emulator</url>
|
||||
<url type="vcs-browser">https://github.com/PCSX2/pcsx2</url>
|
||||
<url type="contact">https://mastodon.social/@PCSX2</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot1.png</image>
|
||||
<image>
|
||||
https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot1.png
|
||||
</image>
|
||||
<caption>
|
||||
The main PCSX2 Qt interface
|
||||
</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot2.png</image>
|
||||
<image>
|
||||
https://raw.githubusercontent.com/PCSX2/pcsx2/master/.github/workflows/scripts/linux/flatpak/screenshots/screenshot2.png
|
||||
</image>
|
||||
<caption>
|
||||
PCSX2 running a game
|
||||
</caption>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1"/>
|
||||
<update_contact>stenzek_AT_gmail.com</update_contact>
|
||||
<update_contact>pcsx2_AT_pcsx2.net</update_contact>
|
||||
<releases>
|
||||
<release version="@GIT_VERSION@" date="@GIT_DATE@" />
|
||||
</releases>
|
||||
|
||||
@@ -22,7 +22,7 @@ fi
|
||||
|
||||
FREETYPE=2.13.2
|
||||
HARFBUZZ=8.3.1
|
||||
SDL=SDL2-2.30.5
|
||||
SDL=SDL2-2.30.6
|
||||
ZSTD=1.5.6
|
||||
LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
|
||||
LIBPNG=1.6.43
|
||||
@@ -56,7 +56,7 @@ CMAKE_COMMON=(
|
||||
cat > SHASUMS <<EOF
|
||||
12991c4e55c506dd7f9b765933e62fd2be2e06d421505d7950a132e4f1bb484d freetype-$FREETYPE.tar.xz
|
||||
19a54fe9596f7a47c502549fce8e8a10978c697203774008cc173f8360b19a9a harfbuzz-$HARFBUZZ.tar.gz
|
||||
f374f3fa29c37dfcc20822d4a7d7dc57e58924d1a5f2ad511bfab4c8193de63b $SDL.tar.gz
|
||||
c6ef64ca18a19d13df6eb22df9aff19fb0db65610a74cc81dae33a82235cacd4 $SDL.tar.gz
|
||||
8c29e06cf42aacc1eafc4077ae2ec6c6fcb96a626157e0593d5e82a34fd403c1 zstd-$ZSTD.tar.gz
|
||||
0728800155f3ed0a0c87e03addbd30ecbe374f7b080678bbca1506051d50dec3 $LZ4.tar.gz
|
||||
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-$LIBPNG.tar.xz
|
||||
|
||||
@@ -47,13 +47,13 @@ set HARFBUZZ=8.3.1
|
||||
set LIBJPEG=9f
|
||||
set LIBPNG=1643
|
||||
set LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
|
||||
set QT=6.7.0
|
||||
set QT=6.7.2
|
||||
set QTMINOR=6.7
|
||||
set SDL=SDL2-2.30.2
|
||||
set WEBP=1.3.2
|
||||
set SDL=SDL2-2.30.6
|
||||
set WEBP=1.4.0
|
||||
set ZLIB=1.3.1
|
||||
set ZLIBSHORT=131
|
||||
set ZSTD=1.5.5
|
||||
set ZSTD=1.5.6
|
||||
|
||||
set SHADERC=2024.1
|
||||
set SHADERC_GLSLANG=142052fa30f9eca191aa9dcf65359fcaed09eeec
|
||||
@@ -64,16 +64,17 @@ call :downloadfile "freetype-%FREETYPE%.tar.gz" https://sourceforge.net/projects
|
||||
call :downloadfile "harfbuzz-%HARFBUZZ%.zip" https://github.com/harfbuzz/harfbuzz/archive/refs/tags/%HARFBUZZ%.zip b2bc56184ae37324bc4829fde7d3f9e6916866ad711ee85792e457547c9fd127 || goto error
|
||||
call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lpng1643.zip fc466a1e638e635d6c66363bdf3f38555b81b0141d0b06ba45b49ccca327436d || goto error
|
||||
call :downloadfile "jpegsr%LIBJPEG%.zip" https://ijg.org/files/jpegsr%LIBJPEG%.zip 6255da8c89e09d694e6800688c76145eb6870a76ac0d36c74fccd61b3940aafa || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 2a499607df669e40258e53d0ade8035ba4ec0175244869d1025d460562aa09b4 || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 || goto error
|
||||
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/%LZ4%.zip" 0c33119688d6b180c7e760b0acd70059222389cfd581632623784bee27e51a31 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" c5d78a9e0346c6695f03df8ba25e5e111a1e23c8aefa8372a1c5a0dd79acaf10 || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 3027fb990cb66436b5476b9dd17cd58f23b55a42eb1c1bcf9c7d78bca3ad7d63 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" f30065289517847d721467827ee446a5f606f6d1c750e166cbfb0ed5bf066c5e || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 882d1db58d35155bd92e4ed92b485d7d713b6aff0d7cf78655de0b414a980689 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 93a31892ca8a4f38464e923328788ce3da881d82016fe18fa1ed12dd43ce486c || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" 102ca62fa682c0d3e700ba89069fd3b6663b9f0c2890b058e97998b6e8971fb3 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 6d4e00fcbee9fd8985cc2869edeb0b1a751912b87506cf2fb6471e73d981e1f4 || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 488119aad60719a085a1e45c31641ac2406ef86fc088a3c99885c18e9d6b4bb9 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 8e736b02db7dd67dbe834d56503b242344ce85d3532da692f1812b30ccf80997 || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 85a22142270a92be0dd0ab5d27cc53617b2a2f1a45fc0a3890024164032f8475 || goto error
|
||||
call :downloadfile "qttools-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttools-everywhere-src-%QT%.zip" 9e15f1fdbd83e4123e733bff20aff1b45921c09056c3790fa42eb71d0a5cd01f || goto error
|
||||
call :downloadfile "qttranslations-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qttranslations-everywhere-src-%QT%.zip" d1f25e0f68a1282feffdd5fe795a027ee5f16ad19e3b1fa2e04a51cea19110ec || goto error
|
||||
call :downloadfile "zlib%ZLIBSHORT%.zip" "https://zlib.net/zlib%ZLIBSHORT%.zip" 72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" c5c8daa1d40dabc51790c62a5b86af2b36dfc4e1a738ff10dc4a46ea4e68ee51 || goto error
|
||||
call :downloadfile "zstd-%ZSTD%.zip" "https://github.com/facebook/zstd/archive/refs/tags/v%ZSTD%.zip" 3b1c3b46e416d36931efd34663122d7f51b550c87f74de2d38249516fe7d8be5 || goto error
|
||||
call :downloadfile "zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" https://github.com/facebook/zstd/commit/fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch 675f144b11f8ab2424b64bed8ccdca5d3f35b9326046fa7a883925dd180f0651 || goto error
|
||||
|
||||
call :downloadfile "shaderc-%SHADERC%.zip" "https://github.com/google/shaderc/archive/refs/tags/v%SHADERC%.zip" 6c9f42ed6bf42750f5369b089909abfdcf0101488b4a1f41116d5159d00af8e7 || goto error
|
||||
call :downloadfile "shaderc-glslang-%SHADERC_GLSLANG%.zip" "https://github.com/KhronosGroup/glslang/archive/%SHADERC_GLSLANG%.zip" 03ad8a6fa987af4653d0cfe6bdaed41bcf617f1366a151fb1574da75950cd3e8 || goto error
|
||||
@@ -108,6 +109,7 @@ ninja -C build install || goto error
|
||||
cd .. || goto error
|
||||
|
||||
echo Building libjpeg...
|
||||
rmdir /S /Q "jpeg-%LIBJPEG%"
|
||||
%SEVENZIP% x "jpegsr%LIBJPEG%.zip" || goto error
|
||||
cd "jpeg-%LIBJPEG%" || goto error
|
||||
%PATCH% -p1 < "%SCRIPTDIR%\libjpeg-cmake.patch" || goto error
|
||||
@@ -155,8 +157,9 @@ cd .. || goto error
|
||||
|
||||
echo Building Zstandard...
|
||||
rmdir /S /Q "zstd-%ZSTD%"
|
||||
%SEVENZIP% x "-x^!zstd-1.5.5\tests\cli-tests\bin" "zstd-%ZSTD%.zip" || goto error
|
||||
%SEVENZIP% x "-x^!zstd-%ZSTD%\tests\cli-tests\bin" "zstd-%ZSTD%.zip" || goto error
|
||||
cd "zstd-%ZSTD%"
|
||||
%PATCH% -p1 < "..\zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" || goto error
|
||||
cmake %ARM64TOOLCHAIN% -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
|
||||
cmake --build build --parallel || goto error
|
||||
ninja -C build install || goto error
|
||||
|
||||
@@ -47,7 +47,7 @@ set LIBPNG=1643
|
||||
set LZ4=b8fd2d15309dd4e605070bd4486e26b6ef814e29
|
||||
set QT=6.7.2
|
||||
set QTMINOR=6.7
|
||||
set SDL=SDL2-2.30.5
|
||||
set SDL=SDL2-2.30.6
|
||||
set WEBP=1.4.0
|
||||
set ZLIB=1.3.1
|
||||
set ZLIBSHORT=131
|
||||
@@ -64,7 +64,7 @@ call :downloadfile "lpng%LIBPNG%.zip" https://download.sourceforge.net/libpng/lp
|
||||
call :downloadfile "jpegsr%LIBJPEG%.zip" https://ijg.org/files/jpegsr%LIBJPEG%.zip 6255da8c89e09d694e6800688c76145eb6870a76ac0d36c74fccd61b3940aafa || goto error
|
||||
call :downloadfile "libwebp-%WEBP%.tar.gz" "https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-%WEBP%.tar.gz" 61f873ec69e3be1b99535634340d5bde750b2e4447caa1db9f61be3fd49ab1e5 || goto error
|
||||
call :downloadfile "lz4-%LZ4%.zip" "https://github.com/lz4/lz4/archive/%LZ4%.zip" 0c33119688d6b180c7e760b0acd70059222389cfd581632623784bee27e51a31 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 688d3da2bf7e887d0ba8e0f81c926119f85029544f4f6da8dea96db70f9d28e3 || goto error
|
||||
call :downloadfile "%SDL%.zip" "https://libsdl.org/release/%SDL%.zip" 6d4e00fcbee9fd8985cc2869edeb0b1a751912b87506cf2fb6471e73d981e1f4 || goto error
|
||||
call :downloadfile "qtbase-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtbase-everywhere-src-%QT%.zip" 488119aad60719a085a1e45c31641ac2406ef86fc088a3c99885c18e9d6b4bb9 || goto error
|
||||
call :downloadfile "qtimageformats-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtimageformats-everywhere-src-%QT%.zip" 8e736b02db7dd67dbe834d56503b242344ce85d3532da692f1812b30ccf80997 || goto error
|
||||
call :downloadfile "qtsvg-everywhere-src-%QT%.zip" "https://download.qt.io/official_releases/qt/%QTMINOR%/%QT%/submodules/qtsvg-everywhere-src-%QT%.zip" 85a22142270a92be0dd0ab5d27cc53617b2a2f1a45fc0a3890024164032f8475 || goto error
|
||||
@@ -154,7 +154,7 @@ cd .. || goto error
|
||||
|
||||
echo Building Zstandard...
|
||||
rmdir /S /Q "zstd-%ZSTD%"
|
||||
%SEVENZIP% x "-x^!zstd-1.5.6\tests\cli-tests\bin" "zstd-%ZSTD%.zip" || goto error
|
||||
%SEVENZIP% x "-x^!zstd-%ZSTD%\tests\cli-tests\bin" "zstd-%ZSTD%.zip" || goto error
|
||||
cd "zstd-%ZSTD%"
|
||||
%PATCH% -p1 < "..\zstd-fd5f8106a58601a963ee816e6a57aa7c61fafc53.patch" || goto error
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="%INSTALLDIR%" -DCMAKE_INSTALL_PREFIX="%INSTALLDIR%" -DBUILD_SHARED_LIBS=ON -DZSTD_BUILD_SHARED=ON -DZSTD_BUILD_STATIC=OFF -DZSTD_BUILD_PROGRAMS=OFF -B build -G Ninja build/cmake
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -63,6 +63,7 @@ oprofile_data/
|
||||
*.kdev4
|
||||
/.kdev4*
|
||||
|
||||
# Resources and docs in /bin are tracked
|
||||
/bin/**/*.dll
|
||||
/bin/**/*.dmp
|
||||
/bin/**/*.exp
|
||||
@@ -93,6 +94,9 @@ oprofile_data/
|
||||
/bin/portable.ini
|
||||
/bin/portable.txt
|
||||
|
||||
# Resources and docs copied from /bin
|
||||
/bin-arm64
|
||||
|
||||
# Manually added by user.
|
||||
/bin/resources/patches.zip
|
||||
|
||||
@@ -115,3 +119,4 @@ oprofile_data/
|
||||
CMakeSettings.json
|
||||
/ci-artifacts/
|
||||
/out/
|
||||
/.cache/
|
||||
|
||||
15
3rdparty/cpuinfo/cpuinfo.vcxproj
vendored
15
3rdparty/cpuinfo/cpuinfo.vcxproj
vendored
@@ -30,13 +30,16 @@
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\duckstation\dep\cpuinfo\src\arm\cache.c">
|
||||
<ClCompile Include="src\arm\cache.c">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\duckstation\dep\cpuinfo\src\arm\uarch.c">
|
||||
<ClCompile Include="src\arm\uarch.c">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\duckstation\dep\cpuinfo\src\arm\windows\init.c">
|
||||
<ClCompile Include="src\arm\windows\init-by-logical-sys-info.c">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\arm\windows\init.c">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="deps\clog\src\clog.c" />
|
||||
@@ -78,13 +81,13 @@
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\duckstation\dep\cpuinfo\src\arm\api.h">
|
||||
<ClInclude Include="src\arm\api.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\duckstation\dep\cpuinfo\src\arm\midr.h">
|
||||
<ClInclude Include="src\arm\midr.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\duckstation\dep\cpuinfo\src\arm\windows\api.h">
|
||||
<ClInclude Include="src\arm\windows\windows-arm-init.h">
|
||||
<ExcludedFromBuild Condition="'$(Platform)'!='ARM64'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="deps\clog\include\clog.h" />
|
||||
|
||||
15
3rdparty/cpuinfo/cpuinfo.vcxproj.filters
vendored
15
3rdparty/cpuinfo/cpuinfo.vcxproj.filters
vendored
@@ -63,13 +63,16 @@
|
||||
<ClCompile Include="deps\clog\src\clog.c">
|
||||
<Filter>clog</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\duckstation\dep\cpuinfo\src\arm\cache.c">
|
||||
<ClCompile Include="src\arm\cache.c">
|
||||
<Filter>arm</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\duckstation\dep\cpuinfo\src\arm\uarch.c">
|
||||
<ClCompile Include="src\arm\uarch.c">
|
||||
<Filter>arm</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\duckstation\dep\cpuinfo\src\arm\windows\init.c">
|
||||
<ClCompile Include="src\arm\windows\init.c">
|
||||
<Filter>arm\windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\arm\windows\init-by-logical-sys-info.c">
|
||||
<Filter>arm\windows</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
@@ -99,13 +102,13 @@
|
||||
<ClInclude Include="deps\clog\include\clog.h">
|
||||
<Filter>clog</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\duckstation\dep\cpuinfo\src\arm\api.h">
|
||||
<ClInclude Include="src\arm\api.h">
|
||||
<Filter>arm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\duckstation\dep\cpuinfo\src\arm\midr.h">
|
||||
<ClInclude Include="src\arm\midr.h">
|
||||
<Filter>arm</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\duckstation\dep\cpuinfo\src\arm\windows\api.h">
|
||||
<ClInclude Include="src\arm\windows\windows-arm-init.h">
|
||||
<Filter>arm\windows</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
|
||||
15
3rdparty/imgui/include/imconfig.h
vendored
15
3rdparty/imgui/include/imconfig.h
vendored
@@ -21,13 +21,14 @@
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
@@ -42,6 +43,7 @@
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default io.PlatformOpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
@@ -49,6 +51,9 @@
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
|
||||
747
3rdparty/imgui/include/imgui.h
vendored
747
3rdparty/imgui/include/imgui.h
vendored
File diff suppressed because it is too large
Load Diff
634
3rdparty/imgui/include/imgui_internal.h
vendored
634
3rdparty/imgui/include/imgui_internal.h
vendored
File diff suppressed because it is too large
Load Diff
2
3rdparty/imgui/include/imstb_textedit.h
vendored
2
3rdparty/imgui/include/imstb_textedit.h
vendored
@@ -41,7 +41,7 @@
|
||||
// 1.13 (2019-02-07) fix bug in undo size management
|
||||
// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash
|
||||
// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield
|
||||
// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual
|
||||
// 1.10 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
|
||||
// 1.9 (2016-08-27) customizable move-by-word
|
||||
// 1.8 (2016-04-02) better keyboard handling when mouse button is down
|
||||
// 1.7 (2015-09-13) change y range handling in case baseline is non-0
|
||||
|
||||
1853
3rdparty/imgui/src/imgui.cpp
vendored
1853
3rdparty/imgui/src/imgui.cpp
vendored
File diff suppressed because it is too large
Load Diff
2536
3rdparty/imgui/src/imgui_demo.cpp
vendored
2536
3rdparty/imgui/src/imgui_demo.cpp
vendored
File diff suppressed because it is too large
Load Diff
384
3rdparty/imgui/src/imgui_draw.cpp
vendored
384
3rdparty/imgui/src/imgui_draw.cpp
vendored
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.90.4
|
||||
// dear imgui, v1.91.0
|
||||
// (drawing and font code)
|
||||
|
||||
/*
|
||||
@@ -8,6 +8,7 @@ Index of this file:
|
||||
// [SECTION] STB libraries implementation
|
||||
// [SECTION] Style functions
|
||||
// [SECTION] ImDrawList
|
||||
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
|
||||
// [SECTION] ImDrawListSplitter
|
||||
// [SECTION] ImDrawData
|
||||
// [SECTION] Helpers ShadeVertsXXX functions
|
||||
@@ -64,6 +65,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#pragma clang diagnostic ignored "-Wreserved-identifier" // warning: identifier '_Xxx' is reserved because it starts with '_' followed by a capital letter
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||
@@ -209,11 +211,13 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.20f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
|
||||
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||||
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
@@ -223,6 +227,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);
|
||||
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);
|
||||
@@ -269,11 +274,13 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.10f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
|
||||
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
|
||||
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_TabDimmedSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
@@ -283,6 +290,7 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.26f, 0.26f, 0.28f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.07f);
|
||||
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f);
|
||||
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
|
||||
@@ -330,11 +338,13 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.35f, 0.35f, 0.35f, 0.17f);
|
||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
|
||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
|
||||
colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
|
||||
colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
|
||||
colors[ImGuiCol_TabSelected] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
|
||||
colors[ImGuiCol_TabSelectedOverline] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_TabDimmed] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
|
||||
colors[ImGuiCol_TabDimmedSelected] = ImLerp(colors[ImGuiCol_TabSelected], colors[ImGuiCol_TitleBg], 0.40f);
|
||||
colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.26f, 0.59f, 1.00f, 1.00f);
|
||||
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
|
||||
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
|
||||
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
|
||||
@@ -344,6 +354,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
|
||||
colors[ImGuiCol_TableBorderLight] = ImVec4(0.68f, 0.68f, 0.74f, 1.00f); // Prefer using Alpha=1.0 here
|
||||
colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
|
||||
colors[ImGuiCol_TableRowBgAlt] = ImVec4(0.30f, 0.30f, 0.30f, 0.09f);
|
||||
colors[ImGuiCol_TextLink] = colors[ImGuiCol_HeaderActive];
|
||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f);
|
||||
colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
|
||||
colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered];
|
||||
@@ -383,6 +394,7 @@ void ImDrawListSharedData::SetCircleTessellationMaxError(float max_error)
|
||||
}
|
||||
|
||||
// Initialize before use in a new frame. We always have a command ready in the buffer.
|
||||
// In the majority of cases, you would want to call PushClipRect() and PushTextureID() after this.
|
||||
void ImDrawList::_ResetForNewFrame()
|
||||
{
|
||||
// Verify that the ImDrawCmd fields we want to memcmp() are contiguous in memory.
|
||||
@@ -1217,10 +1229,10 @@ void ImDrawList::PathArcTo(const ImVec2& center, float radius, float a_min, floa
|
||||
}
|
||||
}
|
||||
|
||||
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float radius_y, float rot, float a_min, float a_max, int num_segments)
|
||||
void ImDrawList::PathEllipticalArcTo(const ImVec2& center, const ImVec2& radius, float rot, float a_min, float a_max, int num_segments)
|
||||
{
|
||||
if (num_segments <= 0)
|
||||
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||
|
||||
_Path.reserve(_Path.Size + (num_segments + 1));
|
||||
|
||||
@@ -1229,11 +1241,10 @@ void ImDrawList::PathEllipticalArcTo(const ImVec2& center, float radius_x, float
|
||||
for (int i = 0; i <= num_segments; i++)
|
||||
{
|
||||
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
|
||||
ImVec2 point(ImCos(a) * radius_x, ImSin(a) * radius_y);
|
||||
const float rel_x = (point.x * cos_rot) - (point.y * sin_rot);
|
||||
const float rel_y = (point.x * sin_rot) + (point.y * cos_rot);
|
||||
point.x = rel_x + center.x;
|
||||
point.y = rel_y + center.y;
|
||||
ImVec2 point(ImCos(a) * radius.x, ImSin(a) * radius.y);
|
||||
const ImVec2 rel((point.x * cos_rot) - (point.y * sin_rot), (point.x * sin_rot) + (point.y * cos_rot));
|
||||
point.x = rel.x + center.x;
|
||||
point.y = rel.y + center.y;
|
||||
_Path.push_back(point);
|
||||
}
|
||||
}
|
||||
@@ -1558,31 +1569,31 @@ void ImDrawList::AddNgonFilled(const ImVec2& center, float radius, ImU32 col, in
|
||||
}
|
||||
|
||||
// Ellipse
|
||||
void ImDrawList::AddEllipse(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments, float thickness)
|
||||
void ImDrawList::AddEllipse(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments, float thickness)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
if (num_segments <= 0)
|
||||
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||
|
||||
// Because we are filling a closed shape we remove 1 from the count of segments/points
|
||||
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
|
||||
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
|
||||
PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
|
||||
PathStroke(col, true, thickness);
|
||||
}
|
||||
|
||||
void ImDrawList::AddEllipseFilled(const ImVec2& center, float radius_x, float radius_y, ImU32 col, float rot, int num_segments)
|
||||
void ImDrawList::AddEllipseFilled(const ImVec2& center, const ImVec2& radius, ImU32 col, float rot, int num_segments)
|
||||
{
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
if (num_segments <= 0)
|
||||
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius_x, radius_y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||
num_segments = _CalcCircleAutoSegmentCount(ImMax(radius.x, radius.y)); // A bit pessimistic, maybe there's a better computation to do here.
|
||||
|
||||
// Because we are filling a closed shape we remove 1 from the count of segments/points
|
||||
const float a_max = IM_PI * 2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
|
||||
PathEllipticalArcTo(center, radius_x, radius_y, rot, 0.0f, a_max, num_segments - 1);
|
||||
PathEllipticalArcTo(center, radius, rot, 0.0f, a_max, num_segments - 1);
|
||||
PathFillConvex(col);
|
||||
}
|
||||
|
||||
@@ -1613,10 +1624,11 @@ void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos,
|
||||
if ((col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
// Accept null ranges
|
||||
if (text_begin == text_end || text_begin[0] == 0)
|
||||
return;
|
||||
if (text_end == NULL)
|
||||
text_end = text_begin + strlen(text_begin);
|
||||
if (text_begin == text_end)
|
||||
return;
|
||||
|
||||
// Pull default font/size from the shared ImDrawListSharedData instance
|
||||
if (font == NULL)
|
||||
@@ -1700,6 +1712,316 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& p_mi
|
||||
PopTextureID();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImTriangulator, ImDrawList concave polygon fill
|
||||
//-----------------------------------------------------------------------------
|
||||
// Triangulate concave polygons. Based on "Triangulation by Ear Clipping" paper, O(N^2) complexity.
|
||||
// Reference: https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
|
||||
// Provided as a convenience for user but not used by main library.
|
||||
//-----------------------------------------------------------------------------
|
||||
// - ImTriangulator [Internal]
|
||||
// - AddConcavePolyFilled()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
enum ImTriangulatorNodeType
|
||||
{
|
||||
ImTriangulatorNodeType_Convex,
|
||||
ImTriangulatorNodeType_Ear,
|
||||
ImTriangulatorNodeType_Reflex
|
||||
};
|
||||
|
||||
struct ImTriangulatorNode
|
||||
{
|
||||
ImTriangulatorNodeType Type;
|
||||
int Index;
|
||||
ImVec2 Pos;
|
||||
ImTriangulatorNode* Next;
|
||||
ImTriangulatorNode* Prev;
|
||||
|
||||
void Unlink() { Next->Prev = Prev; Prev->Next = Next; }
|
||||
};
|
||||
|
||||
struct ImTriangulatorNodeSpan
|
||||
{
|
||||
ImTriangulatorNode** Data = NULL;
|
||||
int Size = 0;
|
||||
|
||||
void push_back(ImTriangulatorNode* node) { Data[Size++] = node; }
|
||||
void find_erase_unsorted(int idx) { for (int i = Size - 1; i >= 0; i--) if (Data[i]->Index == idx) { Data[i] = Data[Size - 1]; Size--; return; } }
|
||||
};
|
||||
|
||||
struct ImTriangulator
|
||||
{
|
||||
static int EstimateTriangleCount(int points_count) { return (points_count < 3) ? 0 : points_count - 2; }
|
||||
static int EstimateScratchBufferSize(int points_count) { return sizeof(ImTriangulatorNode) * points_count + sizeof(ImTriangulatorNode*) * points_count * 2; }
|
||||
|
||||
void Init(const ImVec2* points, int points_count, void* scratch_buffer);
|
||||
void GetNextTriangle(unsigned int out_triangle[3]); // Return relative indexes for next triangle
|
||||
|
||||
// Internal functions
|
||||
void BuildNodes(const ImVec2* points, int points_count);
|
||||
void BuildReflexes();
|
||||
void BuildEars();
|
||||
void FlipNodeList();
|
||||
bool IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const;
|
||||
void ReclassifyNode(ImTriangulatorNode* node);
|
||||
|
||||
// Internal members
|
||||
int _TrianglesLeft = 0;
|
||||
ImTriangulatorNode* _Nodes = NULL;
|
||||
ImTriangulatorNodeSpan _Ears;
|
||||
ImTriangulatorNodeSpan _Reflexes;
|
||||
};
|
||||
|
||||
// Distribute storage for nodes, ears and reflexes.
|
||||
// FIXME-OPT: if everything is convex, we could report it to caller and let it switch to an convex renderer
|
||||
// (this would require first building reflexes to bail to convex if empty, without even building nodes)
|
||||
void ImTriangulator::Init(const ImVec2* points, int points_count, void* scratch_buffer)
|
||||
{
|
||||
IM_ASSERT(scratch_buffer != NULL && points_count >= 3);
|
||||
_TrianglesLeft = EstimateTriangleCount(points_count);
|
||||
_Nodes = (ImTriangulatorNode*)scratch_buffer; // points_count x Node
|
||||
_Ears.Data = (ImTriangulatorNode**)(_Nodes + points_count); // points_count x Node*
|
||||
_Reflexes.Data = (ImTriangulatorNode**)(_Nodes + points_count) + points_count; // points_count x Node*
|
||||
BuildNodes(points, points_count);
|
||||
BuildReflexes();
|
||||
BuildEars();
|
||||
}
|
||||
|
||||
void ImTriangulator::BuildNodes(const ImVec2* points, int points_count)
|
||||
{
|
||||
for (int i = 0; i < points_count; i++)
|
||||
{
|
||||
_Nodes[i].Type = ImTriangulatorNodeType_Convex;
|
||||
_Nodes[i].Index = i;
|
||||
_Nodes[i].Pos = points[i];
|
||||
_Nodes[i].Next = _Nodes + i + 1;
|
||||
_Nodes[i].Prev = _Nodes + i - 1;
|
||||
}
|
||||
_Nodes[0].Prev = _Nodes + points_count - 1;
|
||||
_Nodes[points_count - 1].Next = _Nodes;
|
||||
}
|
||||
|
||||
void ImTriangulator::BuildReflexes()
|
||||
{
|
||||
ImTriangulatorNode* n1 = _Nodes;
|
||||
for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
|
||||
{
|
||||
if (ImTriangleIsClockwise(n1->Prev->Pos, n1->Pos, n1->Next->Pos))
|
||||
continue;
|
||||
n1->Type = ImTriangulatorNodeType_Reflex;
|
||||
_Reflexes.push_back(n1);
|
||||
}
|
||||
}
|
||||
|
||||
void ImTriangulator::BuildEars()
|
||||
{
|
||||
ImTriangulatorNode* n1 = _Nodes;
|
||||
for (int i = _TrianglesLeft; i >= 0; i--, n1 = n1->Next)
|
||||
{
|
||||
if (n1->Type != ImTriangulatorNodeType_Convex)
|
||||
continue;
|
||||
if (!IsEar(n1->Prev->Index, n1->Index, n1->Next->Index, n1->Prev->Pos, n1->Pos, n1->Next->Pos))
|
||||
continue;
|
||||
n1->Type = ImTriangulatorNodeType_Ear;
|
||||
_Ears.push_back(n1);
|
||||
}
|
||||
}
|
||||
|
||||
void ImTriangulator::GetNextTriangle(unsigned int out_triangle[3])
|
||||
{
|
||||
if (_Ears.Size == 0)
|
||||
{
|
||||
FlipNodeList();
|
||||
|
||||
ImTriangulatorNode* node = _Nodes;
|
||||
for (int i = _TrianglesLeft; i >= 0; i--, node = node->Next)
|
||||
node->Type = ImTriangulatorNodeType_Convex;
|
||||
_Reflexes.Size = 0;
|
||||
BuildReflexes();
|
||||
BuildEars();
|
||||
|
||||
// If we still don't have ears, it means geometry is degenerated.
|
||||
if (_Ears.Size == 0)
|
||||
{
|
||||
// Return first triangle available, mimicking the behavior of convex fill.
|
||||
IM_ASSERT(_TrianglesLeft > 0); // Geometry is degenerated
|
||||
_Ears.Data[0] = _Nodes;
|
||||
_Ears.Size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
ImTriangulatorNode* ear = _Ears.Data[--_Ears.Size];
|
||||
out_triangle[0] = ear->Prev->Index;
|
||||
out_triangle[1] = ear->Index;
|
||||
out_triangle[2] = ear->Next->Index;
|
||||
|
||||
ear->Unlink();
|
||||
if (ear == _Nodes)
|
||||
_Nodes = ear->Next;
|
||||
|
||||
ReclassifyNode(ear->Prev);
|
||||
ReclassifyNode(ear->Next);
|
||||
_TrianglesLeft--;
|
||||
}
|
||||
|
||||
void ImTriangulator::FlipNodeList()
|
||||
{
|
||||
ImTriangulatorNode* prev = _Nodes;
|
||||
ImTriangulatorNode* temp = _Nodes;
|
||||
ImTriangulatorNode* current = _Nodes->Next;
|
||||
prev->Next = prev;
|
||||
prev->Prev = prev;
|
||||
while (current != _Nodes)
|
||||
{
|
||||
temp = current->Next;
|
||||
|
||||
current->Next = prev;
|
||||
prev->Prev = current;
|
||||
_Nodes->Next = current;
|
||||
current->Prev = _Nodes;
|
||||
|
||||
prev = current;
|
||||
current = temp;
|
||||
}
|
||||
_Nodes = prev;
|
||||
}
|
||||
|
||||
// A triangle is an ear is no other vertex is inside it. We can test reflexes vertices only (see reference algorithm)
|
||||
bool ImTriangulator::IsEar(int i0, int i1, int i2, const ImVec2& v0, const ImVec2& v1, const ImVec2& v2) const
|
||||
{
|
||||
ImTriangulatorNode** p_end = _Reflexes.Data + _Reflexes.Size;
|
||||
for (ImTriangulatorNode** p = _Reflexes.Data; p < p_end; p++)
|
||||
{
|
||||
ImTriangulatorNode* reflex = *p;
|
||||
if (reflex->Index != i0 && reflex->Index != i1 && reflex->Index != i2)
|
||||
if (ImTriangleContainsPoint(v0, v1, v2, reflex->Pos))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImTriangulator::ReclassifyNode(ImTriangulatorNode* n1)
|
||||
{
|
||||
// Classify node
|
||||
ImTriangulatorNodeType type;
|
||||
const ImTriangulatorNode* n0 = n1->Prev;
|
||||
const ImTriangulatorNode* n2 = n1->Next;
|
||||
if (!ImTriangleIsClockwise(n0->Pos, n1->Pos, n2->Pos))
|
||||
type = ImTriangulatorNodeType_Reflex;
|
||||
else if (IsEar(n0->Index, n1->Index, n2->Index, n0->Pos, n1->Pos, n2->Pos))
|
||||
type = ImTriangulatorNodeType_Ear;
|
||||
else
|
||||
type = ImTriangulatorNodeType_Convex;
|
||||
|
||||
// Update lists when a type changes
|
||||
if (type == n1->Type)
|
||||
return;
|
||||
if (n1->Type == ImTriangulatorNodeType_Reflex)
|
||||
_Reflexes.find_erase_unsorted(n1->Index);
|
||||
else if (n1->Type == ImTriangulatorNodeType_Ear)
|
||||
_Ears.find_erase_unsorted(n1->Index);
|
||||
if (type == ImTriangulatorNodeType_Reflex)
|
||||
_Reflexes.push_back(n1);
|
||||
else if (type == ImTriangulatorNodeType_Ear)
|
||||
_Ears.push_back(n1);
|
||||
n1->Type = type;
|
||||
}
|
||||
|
||||
// Use ear-clipping algorithm to triangulate a simple polygon (no self-interaction, no holes).
|
||||
// (Reminder: we don't perform any coarse clipping/culling in ImDrawList layer!
|
||||
// It is up to caller to ensure not making costly calls that will be outside of visible area.
|
||||
// As concave fill is noticeably more expensive than other primitives, be mindful of this...
|
||||
// Caller can build AABB of points, and avoid filling if 'draw_list->_CmdHeader.ClipRect.Overlays(points_bb) == false')
|
||||
void ImDrawList::AddConcavePolyFilled(const ImVec2* points, const int points_count, ImU32 col)
|
||||
{
|
||||
if (points_count < 3 || (col & IM_COL32_A_MASK) == 0)
|
||||
return;
|
||||
|
||||
const ImVec2 uv = _Data->TexUvWhitePixel;
|
||||
ImTriangulator triangulator;
|
||||
unsigned int triangle[3];
|
||||
if (Flags & ImDrawListFlags_AntiAliasedFill)
|
||||
{
|
||||
// Anti-aliased Fill
|
||||
const float AA_SIZE = _FringeScale;
|
||||
const ImU32 col_trans = col & ~IM_COL32_A_MASK;
|
||||
const int idx_count = (points_count - 2) * 3 + points_count * 6;
|
||||
const int vtx_count = (points_count * 2);
|
||||
PrimReserve(idx_count, vtx_count);
|
||||
|
||||
// Add indexes for fill
|
||||
unsigned int vtx_inner_idx = _VtxCurrentIdx;
|
||||
unsigned int vtx_outer_idx = _VtxCurrentIdx + 1;
|
||||
|
||||
_Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
|
||||
triangulator.Init(points, points_count, _Data->TempBuffer.Data);
|
||||
while (triangulator._TrianglesLeft > 0)
|
||||
{
|
||||
triangulator.GetNextTriangle(triangle);
|
||||
_IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (triangle[0] << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (triangle[1] << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx + (triangle[2] << 1));
|
||||
_IdxWritePtr += 3;
|
||||
}
|
||||
|
||||
// Compute normals
|
||||
_Data->TempBuffer.reserve_discard(points_count);
|
||||
ImVec2* temp_normals = _Data->TempBuffer.Data;
|
||||
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
||||
{
|
||||
const ImVec2& p0 = points[i0];
|
||||
const ImVec2& p1 = points[i1];
|
||||
float dx = p1.x - p0.x;
|
||||
float dy = p1.y - p0.y;
|
||||
IM_NORMALIZE2F_OVER_ZERO(dx, dy);
|
||||
temp_normals[i0].x = dy;
|
||||
temp_normals[i0].y = -dx;
|
||||
}
|
||||
|
||||
for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++)
|
||||
{
|
||||
// Average normals
|
||||
const ImVec2& n0 = temp_normals[i0];
|
||||
const ImVec2& n1 = temp_normals[i1];
|
||||
float dm_x = (n0.x + n1.x) * 0.5f;
|
||||
float dm_y = (n0.y + n1.y) * 0.5f;
|
||||
IM_FIXNORMAL2F(dm_x, dm_y);
|
||||
dm_x *= AA_SIZE * 0.5f;
|
||||
dm_y *= AA_SIZE * 0.5f;
|
||||
|
||||
// Add vertices
|
||||
_VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
|
||||
_VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
|
||||
_VtxWritePtr += 2;
|
||||
|
||||
// Add indexes for fringes
|
||||
_IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx + (i0 << 1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1));
|
||||
_IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx + (i0 << 1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx + (i1 << 1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx + (i1 << 1));
|
||||
_IdxWritePtr += 6;
|
||||
}
|
||||
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non Anti-aliased Fill
|
||||
const int idx_count = (points_count - 2) * 3;
|
||||
const int vtx_count = points_count;
|
||||
PrimReserve(idx_count, vtx_count);
|
||||
for (int i = 0; i < vtx_count; i++)
|
||||
{
|
||||
_VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
|
||||
_VtxWritePtr++;
|
||||
}
|
||||
_Data->TempBuffer.reserve_discard((ImTriangulator::EstimateScratchBufferSize(points_count) + sizeof(ImVec2)) / sizeof(ImVec2));
|
||||
triangulator.Init(points, points_count, _Data->TempBuffer.Data);
|
||||
while (triangulator._TrianglesLeft > 0)
|
||||
{
|
||||
triangulator.GetNextTriangle(triangle);
|
||||
_IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx + triangle[0]); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx + triangle[1]); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx + triangle[2]);
|
||||
_IdxWritePtr += 3;
|
||||
}
|
||||
_VtxCurrentIdx += (ImDrawIdx)vtx_count;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] ImDrawListSplitter
|
||||
@@ -2672,8 +2994,8 @@ static bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
|
||||
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
|
||||
stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
|
||||
|
||||
const float ascent = ImTrunc(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
|
||||
const float descent = ImTrunc(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
|
||||
const float ascent = ImCeil(unscaled_ascent * font_scale);
|
||||
const float descent = ImFloor(unscaled_descent * font_scale);
|
||||
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||
const float font_off_x = cfg.GlyphOffset.x;
|
||||
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
||||
@@ -3768,6 +4090,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im
|
||||
{
|
||||
x = start_x;
|
||||
y += line_height;
|
||||
if (y > clip_rect.w)
|
||||
break; // break out of main loop
|
||||
word_wrap_eol = NULL;
|
||||
s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks
|
||||
continue;
|
||||
|
||||
4
3rdparty/imgui/src/imgui_freetype.cpp
vendored
4
3rdparty/imgui/src/imgui_freetype.cpp
vendored
@@ -11,7 +11,7 @@
|
||||
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
||||
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
|
||||
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
||||
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a prefered texture format.
|
||||
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format.
|
||||
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
|
||||
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
|
||||
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
|
||||
@@ -357,7 +357,7 @@ namespace
|
||||
case FT_PIXEL_MODE_BGRA:
|
||||
{
|
||||
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
|
||||
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
|
||||
#define DE_MULTIPLY(color, alpha) ImMin((ImU32)(255.0f * (float)color / (float)(alpha + FLT_MIN) + 0.5f), 255u)
|
||||
if (multiply_table == nullptr)
|
||||
{
|
||||
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||
|
||||
226
3rdparty/imgui/src/imgui_tables.cpp
vendored
226
3rdparty/imgui/src/imgui_tables.cpp
vendored
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.90.4
|
||||
// dear imgui, v1.91.0
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@@ -24,8 +24,9 @@ Index of this file:
|
||||
*/
|
||||
|
||||
// Navigating this file:
|
||||
// - In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||||
// - With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments.
|
||||
// - In Visual Studio: CTRL+comma ("Edit.GoToAll") can follow symbols inside comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot.
|
||||
// - In Visual Studio w/ Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols inside comments.
|
||||
// - In VS Code, CLion, etc.: CTRL+click can follow symbols inside comments.
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// [SECTION] Commentary
|
||||
@@ -227,6 +228,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wenum-enum-conversion" // warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_')
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
|
||||
@@ -318,6 +320,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
IM_ASSERT(inner_width >= 0.0f);
|
||||
|
||||
// If an outer size is specified ahead we will be able to early out when not visible. Exact clipping criteria may evolve.
|
||||
// FIXME: coarse clipping because access to table data causes two issues:
|
||||
// - instance numbers varying/unstable. may not be a direct problem for users, but could make outside access broken or confusing, e.g. TestEngine.
|
||||
// - can't implement support for ImGuiChildFlags_ResizeY as we need to somehow pull the height data from somewhere. this also needs stable instance numbers.
|
||||
// The side-effects of accessing table data on coarse clip would be:
|
||||
// - always reserving the pooled ImGuiTable data ahead for a fully clipped table (minor IMHO). Also the 'outer_window_is_measuring_size' criteria may already be defeating this in some situations.
|
||||
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
|
||||
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
|
||||
const ImVec2 avail_size = GetContentRegionAvail();
|
||||
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
|
||||
@@ -326,6 +334,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
|
||||
{
|
||||
ItemSize(outer_rect);
|
||||
ItemAdd(outer_rect, id);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -335,7 +344,6 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
|
||||
// Acquire storage for the table
|
||||
ImGuiTable* table = g.Tables.GetOrAddByKey(id);
|
||||
const ImGuiTableFlags table_last_flags = table->Flags;
|
||||
|
||||
// Acquire temporary buffers
|
||||
const int table_idx = g.Tables.GetIndex(table);
|
||||
@@ -353,6 +361,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// Initialize
|
||||
const int previous_frame_active = table->LastFrameActive;
|
||||
const int instance_no = (previous_frame_active != g.FrameCount) ? 0 : table->InstanceCurrent + 1;
|
||||
const ImGuiTableFlags previous_flags = table->Flags;
|
||||
table->ID = id;
|
||||
table->Flags = flags;
|
||||
table->LastFrameActive = g.FrameCount;
|
||||
@@ -399,12 +408,12 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
SetNextWindowContentSize(ImVec2(override_content_size.x != FLT_MAX ? override_content_size.x : 0.0f, override_content_size.y != FLT_MAX ? override_content_size.y : 0.0f));
|
||||
|
||||
// Reset scroll if we are reactivating it
|
||||
if ((table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
|
||||
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
|
||||
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||||
|
||||
// Create scrolling region (without border and zero window padding)
|
||||
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
|
||||
ImGuiWindowFlags child_window_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), ImGuiChildFlags_None, child_window_flags);
|
||||
table->InnerWindow = g.CurrentWindow;
|
||||
table->WorkRect = table->InnerWindow->WorkRect;
|
||||
table->OuterRect = table->InnerWindow->Rect();
|
||||
@@ -428,6 +437,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// For non-scrolling tables, WorkRect == OuterRect == InnerRect.
|
||||
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
|
||||
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
|
||||
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
|
||||
}
|
||||
|
||||
// Push a standardized ID for both child-using and not-child-using tables
|
||||
@@ -498,6 +508,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->DeclColumnsCount = table->AngledHeadersCount = 0;
|
||||
if (previous_frame_active + 1 < g.FrameCount)
|
||||
table->IsActiveIdInTable = false;
|
||||
table->AngledHeadersHeight = 0.0f;
|
||||
temp_data->AngledHeadersExtraWidth = 0.0f;
|
||||
|
||||
// Using opaque colors facilitate overlapping lines of the grid, otherwise we'd need to improve TableDrawBorders()
|
||||
@@ -511,7 +522,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
if (inner_window != outer_window) // So EndChild() within the inner window can restore the table properly.
|
||||
inner_window->DC.CurrentTableIdx = table_idx;
|
||||
|
||||
if ((table_last_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
|
||||
if ((previous_flags & ImGuiTableFlags_Reorderable) && (flags & ImGuiTableFlags_Reorderable) == 0)
|
||||
table->IsResetDisplayOrderRequest = true;
|
||||
|
||||
// Mark as used to avoid GC
|
||||
@@ -1066,6 +1077,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// - ClipRect.Max.x: using WorkMaxX instead of MaxX (aka including padding) makes things more consistent when resizing down, tho slightly detrimental to visibility in very-small column.
|
||||
// - ClipRect.Max.x: using MaxX makes it easier for header to receive hover highlight with no discontinuity and display sorting arrow.
|
||||
// - FIXME-TABLE: We want equal width columns to have equal (ClipRect.Max.x - WorkMinX) width, which means ClipRect.max.x cannot stray off host_clip_rect.Max.x else right-most column may appear shorter.
|
||||
const float previous_instance_work_min_x = column->WorkMinX;
|
||||
column->WorkMinX = column->MinX + table->CellPaddingX + table->CellSpacingX1;
|
||||
column->WorkMaxX = column->MaxX - table->CellPaddingX - table->CellSpacingX2; // Expected max
|
||||
column->ItemWidth = ImTrunc(column->WidthGiven * 0.65f);
|
||||
@@ -1118,8 +1130,22 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// column->WorkMinX = ImLerp(column->WorkMinX, ImMax(column->StartX, column->MaxX - column->ContentWidthRowsUnfrozen), 0.5f);
|
||||
|
||||
// Reset content width variables
|
||||
column->ContentMaxXFrozen = column->ContentMaxXUnfrozen = column->WorkMinX;
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX;
|
||||
if (table->InstanceCurrent == 0)
|
||||
{
|
||||
column->ContentMaxXFrozen = column->WorkMinX;
|
||||
column->ContentMaxXUnfrozen = column->WorkMinX;
|
||||
column->ContentMaxXHeadersUsed = column->WorkMinX;
|
||||
column->ContentMaxXHeadersIdeal = column->WorkMinX;
|
||||
}
|
||||
else
|
||||
{
|
||||
// As we store an absolute value to make per-cell updates faster, we need to offset values used for width computation.
|
||||
const float offset_from_previous_instance = column->WorkMinX - previous_instance_work_min_x;
|
||||
column->ContentMaxXFrozen += offset_from_previous_instance;
|
||||
column->ContentMaxXUnfrozen += offset_from_previous_instance;
|
||||
column->ContentMaxXHeadersUsed += offset_from_previous_instance;
|
||||
column->ContentMaxXHeadersIdeal += offset_from_previous_instance;
|
||||
}
|
||||
|
||||
// Don't decrement auto-fit counters until container window got a chance to submit its items
|
||||
if (table->HostSkipItems == false)
|
||||
@@ -1238,9 +1264,9 @@ void ImGui::TableUpdateBorders(ImGuiTable* table)
|
||||
// really problematic (whereas the actual visual will be displayed in EndTable() and using the current frame height).
|
||||
// Actual columns highlight/render will be performed in EndTable() and not be affected.
|
||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||
const float hit_half_width = TABLE_RESIZE_SEPARATOR_HALF_THICKNESS;
|
||||
const float hit_half_width = ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale);
|
||||
const float hit_y1 = (table->FreezeRowsCount >= 1 ? table->OuterRect.Min.y : table->WorkRect.Min.y) + table->AngledHeadersHeight;
|
||||
const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight);
|
||||
const float hit_y2_body = ImMax(table->OuterRect.Max.y, hit_y1 + table_instance->LastOuterHeight - table->AngledHeadersHeight);
|
||||
const float hit_y2_head = hit_y1 + table_instance->LastTopHeadersRowHeight;
|
||||
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
@@ -1415,7 +1441,7 @@ void ImGui::EndTable()
|
||||
if (table->ResizedColumn != -1 && table->InstanceCurrent == table->InstanceInteracted)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[table->ResizedColumn];
|
||||
const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + TABLE_RESIZE_SEPARATOR_HALF_THICKNESS);
|
||||
const float new_x2 = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(TABLE_RESIZE_SEPARATOR_HALF_THICKNESS * g.CurrentDpiScale));
|
||||
const float new_width = ImTrunc(new_x2 - column->MinX - table->CellSpacingX1 - table->CellPaddingX * 2.0f);
|
||||
table->ResizedColumnNextWidth = new_width;
|
||||
}
|
||||
@@ -1444,7 +1470,10 @@ void ImGui::EndTable()
|
||||
// CursorPosPrevLine and CursorMaxPos manually. That should be a more general layout feature, see same problem e.g. #3414)
|
||||
if (inner_window != outer_window)
|
||||
{
|
||||
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
|
||||
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
|
||||
EndChild();
|
||||
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1462,9 +1491,13 @@ void ImGui::EndTable()
|
||||
}
|
||||
else if (temp_data->UserOuterSize.x <= 0.0f)
|
||||
{
|
||||
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.x : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, table->OuterRect.Min.x + table->ColumnsAutoFitWidth));
|
||||
// Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block
|
||||
// - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags.
|
||||
// - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback.
|
||||
const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y
|
||||
const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f);
|
||||
outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x);
|
||||
outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1472,9 +1505,9 @@ void ImGui::EndTable()
|
||||
}
|
||||
if (temp_data->UserOuterSize.y <= 0.0f)
|
||||
{
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.y : 0.0f;
|
||||
const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f;
|
||||
outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y);
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y));
|
||||
outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1890,7 +1923,7 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
if (is_visible)
|
||||
{
|
||||
// Update data for TableGetHoveredRow()
|
||||
if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2)
|
||||
if (table->HoveredColumnBody != -1 && g.IO.MousePos.y >= bg_y1 && g.IO.MousePos.y < bg_y2 && table_instance->HoveredRowNext < 0)
|
||||
table_instance->HoveredRowNext = table->CurrentRow;
|
||||
|
||||
// Decide of background color for the row
|
||||
@@ -1945,7 +1978,8 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
cell_bg_rect.ClipWith(table->BgClipRect);
|
||||
cell_bg_rect.Min.x = ImMax(cell_bg_rect.Min.x, column->ClipRect.Min.x); // So that first column after frozen one gets clipped when scrolling
|
||||
cell_bg_rect.Max.x = ImMin(cell_bg_rect.Max.x, column->MaxX);
|
||||
window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
|
||||
if (cell_bg_rect.Min.y < cell_bg_rect.Max.y)
|
||||
window->DrawList->AddRectFilled(cell_bg_rect.Min, cell_bg_rect.Max, cell_data->BgColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1962,34 +1996,37 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
|
||||
// get the new cursor position.
|
||||
if (unfreeze_rows_request)
|
||||
{
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
||||
table->IsUnfrozenRows = true;
|
||||
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||||
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
table->IsUnfrozenRows = true;
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
|
||||
@@ -2766,7 +2803,7 @@ ImGuiTableSortSpecs* ImGui::TableGetSortSpecs()
|
||||
static inline ImGuiSortDirection TableGetColumnAvailSortDirection(ImGuiTableColumn* column, int n)
|
||||
{
|
||||
IM_ASSERT(n < column->SortDirectionsAvailCount);
|
||||
return (column->SortDirectionsAvailList >> (n << 1)) & 0x03;
|
||||
return (ImGuiSortDirection)((column->SortDirectionsAvailList >> (n << 1)) & 0x03);
|
||||
}
|
||||
|
||||
// Fix sort direction if currently set on a value which is unavailable (e.g. activating NoSortAscending/NoSortDescending)
|
||||
@@ -2907,6 +2944,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
||||
}
|
||||
|
||||
// Write output
|
||||
// May be able to move all SortSpecs data from table (48 bytes) to ImGuiTableTempData if we decide to write it back on every BeginTable()
|
||||
ImGuiTableColumnSortSpecs* sort_specs = (table->SortSpecsCount == 0) ? NULL : (table->SortSpecsCount == 1) ? &table->SortSpecsSingle : table->SortSpecsMulti.Data;
|
||||
if (dirty && sort_specs != NULL)
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
@@ -2919,7 +2957,7 @@ void ImGui::TableSortSpecsBuild(ImGuiTable* table)
|
||||
sort_spec->ColumnUserID = column->UserID;
|
||||
sort_spec->ColumnIndex = (ImGuiTableColumnIdx)column_n;
|
||||
sort_spec->SortOrder = (ImGuiTableColumnIdx)column->SortOrder;
|
||||
sort_spec->SortDirection = column->SortDirection;
|
||||
sort_spec->SortDirection = (ImGuiSortDirection)column->SortDirection;
|
||||
}
|
||||
|
||||
table->SortSpecs.Specs = sort_specs;
|
||||
@@ -2967,7 +3005,7 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
|
||||
|
||||
// [Public] This is a helper to output TableHeader() calls based on the column names declared in TableSetupColumn().
|
||||
// The intent is that advanced users willing to create customized headers would not need to use this helper
|
||||
// and can create their own! For example: TableHeader() may be preceeded by Checkbox() or other custom widgets.
|
||||
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
|
||||
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
|
||||
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
|
||||
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
|
||||
@@ -3153,15 +3191,43 @@ void ImGui::TableHeader(const char* label)
|
||||
}
|
||||
|
||||
// Unlike TableHeadersRow() it is not expected that you can reimplement or customize this with custom widgets.
|
||||
// FIXME: highlight without ImGuiTableFlags_HighlightHoveredColumn
|
||||
// FIXME: No hit-testing/button on the angled header.
|
||||
void ImGui::TableAngledHeadersRow()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
TableAngledHeadersRowEx(g.Style.TableAngledHeadersAngle, 0.0f);
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
ImGuiTableTempData* temp_data = table->TempData;
|
||||
temp_data->AngledHeadersRequests.resize(0);
|
||||
temp_data->AngledHeadersRequests.reserve(table->ColumnsEnabledCount);
|
||||
|
||||
// Which column needs highlight?
|
||||
const ImGuiID row_id = GetID("##AngledHeaders");
|
||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||
int highlight_column_n = table->HighlightColumnHeader;
|
||||
if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
|
||||
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
|
||||
highlight_column_n = table->HoveredColumnBody;
|
||||
|
||||
// Build up request
|
||||
ImU32 col_header_bg = GetColorU32(ImGuiCol_TableHeaderBg);
|
||||
ImU32 col_text = GetColorU32(ImGuiCol_Text);
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
if (IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||
{
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
|
||||
continue;
|
||||
ImGuiTableHeaderData request = { (ImGuiTableColumnIdx)column_n, col_text, col_header_bg, (column_n == highlight_column_n) ? GetColorU32(ImGuiCol_Header) : 0 };
|
||||
temp_data->AngledHeadersRequests.push_back(request);
|
||||
}
|
||||
|
||||
// Render row
|
||||
TableAngledHeadersRowEx(row_id, g.Style.TableAngledHeadersAngle, 0.0f, temp_data->AngledHeadersRequests.Data, temp_data->AngledHeadersRequests.Size);
|
||||
}
|
||||
|
||||
void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
// Important: data must be fed left to right
|
||||
void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label_width, const ImGuiTableHeaderData* data, int data_count)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
@@ -3185,7 +3251,7 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
// Calculate our base metrics and set angled headers data _before_ the first call to TableNextRow()
|
||||
// FIXME-STYLE: Would it be better for user to submit 'max_label_width' or 'row_height' ? One can be derived from the other.
|
||||
const float header_height = g.FontSize + g.Style.CellPadding.x * 2.0f;
|
||||
const float row_height = ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y);
|
||||
const float row_height = ImTrunc(ImFabs(ImRotate(ImVec2(max_label_width, flip_label ? +header_height : -header_height), cos_a, sin_a).y));
|
||||
table->AngledHeadersHeight = row_height;
|
||||
table->AngledHeadersSlope = (sin_a != 0.0f) ? (cos_a / sin_a) : 0.0f;
|
||||
const ImVec2 header_angled_vector = unit_right * (row_height / -sin_a); // vector from bottom-left to top-left, and from bottom-right to top-right
|
||||
@@ -3203,28 +3269,22 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
draw_list->AddRectFilled(ImVec2(table->BgClipRect.Min.x, row_r.Min.y), ImVec2(table->BgClipRect.Max.x, row_r.Max.y), GetColorU32(ImGuiCol_TableHeaderBg, 0.25f)); // FIXME-STYLE: Change row background with an arbitrary color.
|
||||
PushClipRect(ImVec2(clip_rect_min_x, table->BgClipRect.Min.y), table->BgClipRect.Max, true); // Span all columns
|
||||
|
||||
const ImGuiID row_id = GetID("##AngledHeaders");
|
||||
ButtonBehavior(row_r, row_id, NULL, NULL);
|
||||
KeepAliveID(row_id);
|
||||
|
||||
ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent);
|
||||
int highlight_column_n = table->HighlightColumnHeader;
|
||||
if (highlight_column_n == -1 && table->HoveredColumnBody != -1)
|
||||
if (table_instance->HoveredRowLast == 0 && table->HoveredColumnBorder == -1 && (g.ActiveId == 0 || g.ActiveId == row_id || (table->IsActiveIdInTable || g.DragDropActive)))
|
||||
highlight_column_n = table->HoveredColumnBody;
|
||||
const float ascent_scaled = g.Font->Ascent * g.FontScale; // FIXME: Standardize those scaling factors better
|
||||
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
|
||||
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||||
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
|
||||
|
||||
// Draw background and labels in first pass, then all borders.
|
||||
float max_x = 0.0f;
|
||||
ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||||
for (int pass = 0; pass < 2; pass++)
|
||||
for (int order_n = 0; order_n < table->ColumnsCount; order_n++)
|
||||
for (int order_n = 0; order_n < data_count; order_n++)
|
||||
{
|
||||
if (!IM_BITARRAY_TESTBIT(table->EnabledMaskByDisplayOrder, order_n))
|
||||
continue;
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
const ImGuiTableHeaderData* request = &data[order_n];
|
||||
const int column_n = request->Index;
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
if ((column->Flags & ImGuiTableColumnFlags_AngledHeader) == 0) // Note: can't rely on ImGuiTableColumnFlags_IsVisible test here.
|
||||
continue;
|
||||
|
||||
ImVec2 bg_shape[4];
|
||||
bg_shape[0] = ImVec2(column->MaxX, row_r.Max.y);
|
||||
@@ -3234,9 +3294,8 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
if (pass == 0)
|
||||
{
|
||||
// Draw shape
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_TableHeaderBg));
|
||||
if (column_n == highlight_column_n)
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], GetColorU32(ImGuiCol_Header)); // Highlight on hover
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor0);
|
||||
draw_list->AddQuadFilled(bg_shape[0], bg_shape[1], bg_shape[2], bg_shape[3], request->BgColor1); // Optional highlight
|
||||
max_x = ImMax(max_x, bg_shape[3].x);
|
||||
|
||||
// Draw label
|
||||
@@ -3244,8 +3303,17 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
// - Handle multiple lines manually, as we want each lines to follow on the horizontal border, rather than see a whole block rotated.
|
||||
const char* label_name = TableGetColumnName(table, column_n);
|
||||
const char* label_name_end = FindRenderedTextEnd(label_name);
|
||||
const float line_off_step_x = g.FontSize / -sin_a;
|
||||
float line_off_curr_x = 0.0f;
|
||||
const float line_off_step_x = (g.FontSize / -sin_a);
|
||||
const int label_lines = ImTextCountLines(label_name, label_name_end);
|
||||
|
||||
// Left<>Right alignment
|
||||
float line_off_curr_x = flip_label ? (label_lines - 1) * line_off_step_x : 0.0f;
|
||||
float line_off_for_align_x = ImMax((((column->MaxX - column->MinX) - padding.x * 2.0f) - (label_lines * line_off_step_x)), 0.0f) * align.x;
|
||||
line_off_curr_x += line_off_for_align_x - line_off_for_ascent_x;
|
||||
|
||||
// Register header width
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(label_lines * line_off_step_x - line_off_for_align_x);
|
||||
|
||||
while (label_name < label_name_end)
|
||||
{
|
||||
const char* label_name_eol = strchr(label_name, '\n');
|
||||
@@ -3254,26 +3322,30 @@ void ImGui::TableAngledHeadersRowEx(float angle, float max_label_width)
|
||||
|
||||
// FIXME: Individual line clipping for right-most column is broken for negative angles.
|
||||
ImVec2 label_size = CalcTextSize(label_name, label_name_eol);
|
||||
float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symetrical but hide more text.
|
||||
float clip_width = max_label_width - padding.y; // Using padding.y*2.0f would be symmetrical but hide more text.
|
||||
float clip_height = ImMin(label_size.y, column->ClipRect.Max.x - column->WorkMinX - line_off_curr_x);
|
||||
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
|
||||
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
|
||||
PushStyleColor(ImGuiCol_Text, request->TextColor);
|
||||
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||||
PopStyleColor();
|
||||
int vtx_idx_end = draw_list->_VtxCurrentIdx;
|
||||
|
||||
// Up<>Down alignment
|
||||
const float available_space = ImMax(clip_width - label_size.x + ImAbs(padding.x * cos_a) * 2.0f - ImAbs(padding.y * sin_a) * 2.0f, 0.0f);
|
||||
const float vertical_offset = available_space * align.y * (flip_label ? -1.0f : 1.0f);
|
||||
|
||||
// Rotate and offset label
|
||||
ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x, window->ClipRect.Min.y + label_size.y);
|
||||
ImVec2 pivot_in = ImVec2(window->ClipRect.Min.x - vertical_offset, window->ClipRect.Min.y + label_size.y);
|
||||
ImVec2 pivot_out = ImVec2(column->WorkMinX, row_r.Max.y);
|
||||
line_off_curr_x += line_off_step_x;
|
||||
line_off_curr_x += flip_label ? -line_off_step_x : line_off_step_x;
|
||||
pivot_out += unit_right * padding.y;
|
||||
if (flip_label)
|
||||
pivot_out += unit_right * (clip_width - ImMax(0.0f, clip_width - label_size.x));
|
||||
pivot_out.x += flip_label ? line_off_curr_x - line_off_step_x : line_off_curr_x;
|
||||
pivot_out.x += flip_label ? line_off_curr_x + line_off_step_x : line_off_curr_x;
|
||||
ShadeVertsTransformPos(draw_list, vtx_idx_begin, vtx_idx_end, pivot_in, label_cos_a, label_sin_a, pivot_out); // Rotate and offset
|
||||
//if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 2.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
|
||||
//if (g.IO.KeyShift) { ImDrawList* fg_dl = GetForegroundDrawList(); vtx_idx_begin = fg_dl->_VtxCurrentIdx; fg_dl->AddRect(clip_r.Min, clip_r.Max, IM_COL32(0, 255, 0, 255), 0.0f, 0, 1.0f); ShadeVertsTransformPos(fg_dl, vtx_idx_begin, fg_dl->_VtxCurrentIdx, pivot_in, label_cos_a, label_sin_a, pivot_out); }
|
||||
|
||||
// Register header width
|
||||
column->ContentMaxXHeadersUsed = column->ContentMaxXHeadersIdeal = column->WorkMinX + ImCeil(line_off_curr_x);
|
||||
label_name = label_name_eol + 1;
|
||||
}
|
||||
}
|
||||
@@ -3402,7 +3474,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
Separator();
|
||||
want_separator = true;
|
||||
|
||||
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
|
||||
PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
|
||||
{
|
||||
ImGuiTableColumn* other_column = &table->Columns[other_column_n];
|
||||
@@ -3988,7 +4060,7 @@ float ImGui::GetColumnNormFromOffset(const ImGuiOldColumns* columns, float offse
|
||||
return offset / (columns->OffMaxX - columns->OffMinX);
|
||||
}
|
||||
|
||||
static const float COLUMNS_HIT_RECT_HALF_WIDTH = 4.0f;
|
||||
static const float COLUMNS_HIT_RECT_HALF_THICKNESS = 4.0f;
|
||||
|
||||
static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
|
||||
{
|
||||
@@ -3999,7 +4071,7 @@ static float GetDraggedColumnOffset(ImGuiOldColumns* columns, int column_index)
|
||||
IM_ASSERT(column_index > 0); // We are not supposed to drag column 0.
|
||||
IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index));
|
||||
|
||||
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + COLUMNS_HIT_RECT_HALF_WIDTH - window->Pos.x;
|
||||
float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale) - window->Pos.x;
|
||||
x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing);
|
||||
if ((columns->Flags & ImGuiOldColumnFlags_NoPreserveWidths))
|
||||
x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing);
|
||||
@@ -4314,7 +4386,7 @@ void ImGui::EndColumns()
|
||||
ImGuiOldColumnData* column = &columns->Columns[n];
|
||||
float x = window->Pos.x + GetColumnOffset(n);
|
||||
const ImGuiID column_id = columns->ID + ImGuiID(n);
|
||||
const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH;
|
||||
const float column_hit_hw = ImTrunc(COLUMNS_HIT_RECT_HALF_THICKNESS * g.CurrentDpiScale);
|
||||
const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2));
|
||||
if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav))
|
||||
continue;
|
||||
|
||||
1790
3rdparty/imgui/src/imgui_widgets.cpp
vendored
1790
3rdparty/imgui/src/imgui_widgets.cpp
vendored
File diff suppressed because it is too large
Load Diff
2
3rdparty/imgui/src/imstb_truetype.h
vendored
2
3rdparty/imgui/src/imstb_truetype.h
vendored
@@ -656,7 +656,7 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h
|
||||
STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip);
|
||||
// If skip != 0, this tells stb_truetype to skip any codepoints for which
|
||||
// there is no corresponding glyph. If skip=0, which is the default, then
|
||||
// codepoints without a glyph recived the font's "missing character" glyph,
|
||||
// codepoints without a glyph received the font's "missing character" glyph,
|
||||
// typically an empty box by convention.
|
||||
|
||||
STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above
|
||||
|
||||
9
3rdparty/rcheevos/CHANGELOG.md
vendored
9
3rdparty/rcheevos/CHANGELOG.md
vendored
@@ -1,3 +1,12 @@
|
||||
# v11.5.0
|
||||
* add total_entries to rc_api_fetch_leaderboard_info_response_t
|
||||
* add RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED event
|
||||
* modify rc_client_begin_identify_and_load_game and rc_client_begin_change_media to use locally
|
||||
registered filereader/cdreader for hash resolution when using rc_client_raintegration
|
||||
* add support for ISO-8601 timestamps in JSON responses
|
||||
* update RC_CONSOLE_MS_DOS hash logic to support parent archives
|
||||
* fix infinite loop that sometimes occurs when resetting while progress tracker is onscreen
|
||||
|
||||
# v11.4.0
|
||||
* add RC_CONDITION_REMEMBER and RC_OPERAND_RECALL
|
||||
* add RC_OPERATOR_ADD and RC_OPERATOR_SUB
|
||||
|
||||
3
3rdparty/rcheevos/include/rc_api_info.h
vendored
3
3rdparty/rcheevos/include/rc_api_info.h
vendored
@@ -129,6 +129,9 @@ typedef struct rc_api_fetch_leaderboard_info_response_t {
|
||||
/* The number of items in the entries array */
|
||||
uint32_t num_entries;
|
||||
|
||||
/* The total number of entries on the server */
|
||||
uint32_t total_entries;
|
||||
|
||||
/* Common server-provided response information */
|
||||
rc_api_response_t response;
|
||||
}
|
||||
|
||||
1
3rdparty/rcheevos/include/rc_client.h
vendored
1
3rdparty/rcheevos/include/rc_client.h
vendored
@@ -517,6 +517,7 @@ typedef struct rc_client_leaderboard_entry_t {
|
||||
typedef struct rc_client_leaderboard_entry_list_t {
|
||||
rc_client_leaderboard_entry_t* entries;
|
||||
uint32_t num_entries;
|
||||
uint32_t total_entries;
|
||||
int32_t user_index;
|
||||
} rc_client_leaderboard_entry_list_t;
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ enum {
|
||||
RC_CLIENT_RAINTEGRATION_EVENT_TYPE_NONE = 0,
|
||||
RC_CLIENT_RAINTEGRATION_EVENT_MENUITEM_CHECKED_CHANGED = 1, /* [menu_item] checked changed */
|
||||
RC_CLIENT_RAINTEGRATION_EVENT_HARDCORE_CHANGED = 2, /* hardcore was enabled or disabled */
|
||||
RC_CLIENT_RAINTEGRATION_EVENT_PAUSE = 3 /* emulated system should be paused */
|
||||
RC_CLIENT_RAINTEGRATION_EVENT_PAUSE = 3, /* emulated system should be paused */
|
||||
RC_CLIENT_RAINTEGRATION_EVENT_MENU_CHANGED = 4 /* one or more items were added/removed from the menu and it should be rebuilt */
|
||||
};
|
||||
|
||||
typedef struct rc_client_raintegration_event_t {
|
||||
|
||||
7
3rdparty/rcheevos/src/rapi/rc_api_common.c
vendored
7
3rdparty/rcheevos/src/rapi/rc_api_common.c
vendored
@@ -864,8 +864,11 @@ int rc_json_get_datetime(time_t* out, const rc_json_field_t* field, const char*
|
||||
|
||||
if (*field->value_start == '\"') {
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if (sscanf_s(field->value_start + 1, "%d-%d-%d %d:%d:%d",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
|
||||
if (sscanf_s(field->value_start + 1, "%d-%d-%d %d:%d:%d", /* DB format "2013-10-20 22:12:21" */
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6 ||
|
||||
/* NOTE: relies on sscanf stopping when it sees a non-digit after the seconds. could be 'Z', '.', '+', or '-' */
|
||||
sscanf_s(field->value_start + 1, "%d-%d-%dT%d:%d:%d", /* ISO format "2013-10-20T22:12:21.000000Z */
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) {
|
||||
tm.tm_mon--; /* 0-based */
|
||||
tm.tm_year -= 1900; /* 1900 based */
|
||||
|
||||
|
||||
5
3rdparty/rcheevos/src/rapi/rc_api_info.c
vendored
5
3rdparty/rcheevos/src/rapi/rc_api_info.c
vendored
@@ -189,7 +189,8 @@ int rc_api_process_fetch_leaderboard_info_server_response(rc_api_fetch_leaderboa
|
||||
RC_JSON_NEW_FIELD("LBAuthor"),
|
||||
RC_JSON_NEW_FIELD("LBCreated"),
|
||||
RC_JSON_NEW_FIELD("LBUpdated"),
|
||||
RC_JSON_NEW_FIELD("Entries") /* array */
|
||||
RC_JSON_NEW_FIELD("Entries"), /* array */
|
||||
RC_JSON_NEW_FIELD("TotalEntries")
|
||||
/* unused fields
|
||||
RC_JSON_NEW_FIELD("GameTitle"),
|
||||
RC_JSON_NEW_FIELD("ConsoleID"),
|
||||
@@ -235,6 +236,8 @@ int rc_api_process_fetch_leaderboard_info_server_response(rc_api_fetch_leaderboa
|
||||
return RC_MISSING_VALUE;
|
||||
if (!rc_json_get_required_datetime(&response->updated, &response->response, &leaderboarddata_fields[9], "LBUpdated"))
|
||||
return RC_MISSING_VALUE;
|
||||
if (!rc_json_get_required_unum(&response->total_entries, &response->response, &leaderboarddata_fields[11], "TotalEntries"))
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
if (!leaderboarddata_fields[1].value_end)
|
||||
return RC_MISSING_VALUE;
|
||||
|
||||
435
3rdparty/rcheevos/src/rc_client.c
vendored
435
3rdparty/rcheevos/src/rc_client.c
vendored
@@ -78,6 +78,7 @@ typedef struct rc_client_load_state_t
|
||||
#endif
|
||||
} rc_client_load_state_t;
|
||||
|
||||
static void rc_client_process_resolved_hash(rc_client_load_state_t* load_state);
|
||||
static void rc_client_begin_fetch_game_data(rc_client_load_state_t* callback_data);
|
||||
static void rc_client_hide_progress_tracker(rc_client_t* client, rc_client_game_info_t* game);
|
||||
static void rc_client_load_error(rc_client_load_state_t* load_state, int result, const char* error_message);
|
||||
@@ -1984,12 +1985,101 @@ static void rc_client_fetch_game_data_callback(const rc_api_server_response_t* s
|
||||
rc_api_destroy_fetch_game_data_response(&fetch_game_data_response);
|
||||
}
|
||||
|
||||
static void rc_client_begin_fetch_game_data(rc_client_load_state_t* load_state)
|
||||
static rc_client_game_info_t* rc_client_allocate_game(void)
|
||||
{
|
||||
rc_client_game_info_t* game = (rc_client_game_info_t*)calloc(1, sizeof(*game));
|
||||
if (!game)
|
||||
return NULL;
|
||||
|
||||
rc_buffer_init(&game->buffer);
|
||||
rc_runtime_init(&game->runtime);
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
static int rc_client_attach_load_state(rc_client_t* client, rc_client_load_state_t* load_state)
|
||||
{
|
||||
if (client->state.load == NULL) {
|
||||
rc_client_unload_game(client);
|
||||
client->state.load = load_state;
|
||||
|
||||
if (load_state->game == NULL) {
|
||||
load_state->game = rc_client_allocate_game();
|
||||
if (!load_state->game) {
|
||||
if (load_state->callback)
|
||||
load_state->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, load_state->callback_userdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (client->state.load != load_state) {
|
||||
/* previous load was aborted */
|
||||
if (load_state->callback)
|
||||
load_state->callback(RC_ABORTED, "The requested game is no longer active", client, load_state->callback_userdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
|
||||
static void rc_client_external_load_state_callback(int result, const char* error_message, rc_client_t* client, void* userdata)
|
||||
{
|
||||
rc_client_load_state_t* load_state = (rc_client_load_state_t*)userdata;
|
||||
int async_aborted;
|
||||
|
||||
client = load_state->client;
|
||||
async_aborted = rc_client_end_async(client, &load_state->async_handle);
|
||||
if (async_aborted) {
|
||||
if (async_aborted != RC_CLIENT_ASYNC_DESTROYED) {
|
||||
RC_CLIENT_LOG_VERBOSE(client, "Load aborted during external loading");
|
||||
}
|
||||
|
||||
rc_client_unload_game(client); /* unload the game from the external client */
|
||||
rc_client_free_load_state(load_state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result != RC_OK) {
|
||||
rc_client_load_error(load_state, result, error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
load_state->progress = (client->state.load == load_state) ?
|
||||
RC_CLIENT_LOAD_GAME_STATE_DONE : RC_CLIENT_LOAD_GAME_STATE_ABORTED;
|
||||
client->state.load = NULL;
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (load_state->progress != RC_CLIENT_LOAD_GAME_STATE_DONE) {
|
||||
/* previous load state was aborted */
|
||||
if (load_state->callback)
|
||||
load_state->callback(RC_ABORTED, "The requested game is no longer active", client, load_state->callback_userdata);
|
||||
}
|
||||
else {
|
||||
/* keep partial game object for media_hash management */
|
||||
if (client->state.external_client && client->state.external_client->get_game_info) {
|
||||
const rc_client_game_t* info = client->state.external_client->get_game_info();
|
||||
load_state->game->public_.console_id = info->console_id;
|
||||
client->game = load_state->game;
|
||||
load_state->game = NULL;
|
||||
}
|
||||
|
||||
if (load_state->callback)
|
||||
load_state->callback(RC_OK, NULL, client, load_state->callback_userdata);
|
||||
}
|
||||
|
||||
rc_client_free_load_state(load_state);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void rc_client_process_resolved_hash(rc_client_load_state_t* load_state)
|
||||
{
|
||||
rc_api_fetch_game_data_request_t fetch_game_data_request;
|
||||
rc_client_t* client = load_state->client;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
|
||||
if (load_state->hash->game_id == 0) {
|
||||
#ifdef RC_CLIENT_SUPPORTS_HASH
|
||||
@@ -2058,20 +2148,35 @@ static void rc_client_begin_fetch_game_data(rc_client_load_state_t* load_state)
|
||||
#endif /* RC_CLIENT_SUPPORTS_HASH */
|
||||
|
||||
if (load_state->hash->game_id == 0) {
|
||||
rc_client_subset_info_t* subset;
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client) {
|
||||
if (client->state.external_client->load_unknown_game) {
|
||||
client->state.external_client->load_unknown_game(load_state->game->public_.hash);
|
||||
rc_client_load_error(load_state, RC_NO_GAME_LOADED, "Unknown game");
|
||||
return;
|
||||
}
|
||||
/* no external method specifically for unknown game, just pass the hash through to begin_load_game below */
|
||||
}
|
||||
else {
|
||||
#endif
|
||||
/* mimics rc_client_load_unknown_game without allocating a new game object */
|
||||
rc_client_subset_info_t* subset;
|
||||
|
||||
subset = (rc_client_subset_info_t*)rc_buffer_alloc(&load_state->game->buffer, sizeof(rc_client_subset_info_t));
|
||||
memset(subset, 0, sizeof(*subset));
|
||||
subset->public_.title = "";
|
||||
subset = (rc_client_subset_info_t*)rc_buffer_alloc(&load_state->game->buffer, sizeof(rc_client_subset_info_t));
|
||||
memset(subset, 0, sizeof(*subset));
|
||||
subset->public_.title = "";
|
||||
|
||||
load_state->game->public_.title = "Unknown Game";
|
||||
load_state->game->public_.badge_name = "";
|
||||
load_state->game->subsets = subset;
|
||||
client->game = load_state->game;
|
||||
load_state->game = NULL;
|
||||
load_state->game->public_.title = "Unknown Game";
|
||||
load_state->game->public_.badge_name = "";
|
||||
load_state->game->subsets = subset;
|
||||
client->game = load_state->game;
|
||||
load_state->game = NULL;
|
||||
|
||||
rc_client_load_error(load_state, RC_NO_GAME_LOADED, "Unknown game");
|
||||
return;
|
||||
rc_client_load_error(load_state, RC_NO_GAME_LOADED, "Unknown game");
|
||||
return;
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2083,6 +2188,60 @@ static void rc_client_begin_fetch_game_data(rc_client_load_state_t* load_state)
|
||||
/* done with the hashing code, release the global pointer */
|
||||
g_hash_client = NULL;
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client) {
|
||||
if (client->state.external_client->add_game_hash)
|
||||
client->state.external_client->add_game_hash(load_state->hash->hash, load_state->hash->game_id);
|
||||
|
||||
if (client->state.external_client->begin_load_game) {
|
||||
rc_client_begin_async(client, &load_state->async_handle);
|
||||
client->state.external_client->begin_load_game(client, load_state->hash->hash, rc_client_external_load_state_callback, load_state);
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
rc_client_begin_fetch_game_data(load_state);
|
||||
}
|
||||
|
||||
void rc_client_load_unknown_game(rc_client_t* client, const char* tried_hashes)
|
||||
{
|
||||
rc_client_subset_info_t* subset;
|
||||
rc_client_game_info_t* game;
|
||||
|
||||
game = rc_client_allocate_game();
|
||||
if (!game)
|
||||
return;
|
||||
|
||||
subset = (rc_client_subset_info_t*)rc_buffer_alloc(&game->buffer, sizeof(rc_client_subset_info_t));
|
||||
memset(subset, 0, sizeof(*subset));
|
||||
subset->public_.title = "";
|
||||
game->subsets = subset;
|
||||
|
||||
game->public_.title = "Unknown Game";
|
||||
game->public_.badge_name = "";
|
||||
game->public_.console_id = RC_CONSOLE_UNKNOWN;
|
||||
|
||||
if (strlen(tried_hashes) == 32) { /* only one hash tried, add it to the list */
|
||||
rc_client_game_hash_t* game_hash = rc_client_find_game_hash(client, tried_hashes);
|
||||
game_hash->game_id = 0;
|
||||
game->public_.hash = game_hash->hash;
|
||||
}
|
||||
else {
|
||||
game->public_.hash = rc_buffer_strcpy(&game->buffer, tried_hashes);
|
||||
}
|
||||
|
||||
rc_client_unload_game(client);
|
||||
client->game = game;
|
||||
}
|
||||
|
||||
static void rc_client_begin_fetch_game_data(rc_client_load_state_t* load_state)
|
||||
{
|
||||
rc_api_fetch_game_data_request_t fetch_game_data_request;
|
||||
rc_client_t* client = load_state->client;
|
||||
rc_api_request_t request;
|
||||
int result;
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
result = client->state.user;
|
||||
if (result == RC_CLIENT_USER_STATE_LOGIN_REQUESTED)
|
||||
@@ -2160,7 +2319,7 @@ static void rc_client_identify_game_callback(const rc_api_server_response_t* ser
|
||||
/* previous load state was aborted, load_state was free'd */
|
||||
}
|
||||
else {
|
||||
rc_client_begin_fetch_game_data(load_state);
|
||||
rc_client_process_resolved_hash(load_state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2193,35 +2352,25 @@ rc_client_game_hash_t* rc_client_find_game_hash(rc_client_t* client, const char*
|
||||
return game_hash;
|
||||
}
|
||||
|
||||
void rc_client_add_game_hash(rc_client_t* client, const char* hash, uint32_t game_id)
|
||||
{
|
||||
/* store locally, even if passing to external client */
|
||||
rc_client_game_hash_t* game_hash = rc_client_find_game_hash(client, hash);
|
||||
game_hash->game_id = game_id;
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->add_game_hash)
|
||||
client->state.external_client->add_game_hash(hash, game_id);
|
||||
#endif
|
||||
}
|
||||
|
||||
static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* load_state,
|
||||
const char* hash, const char* file_path)
|
||||
{
|
||||
rc_client_t* client = load_state->client;
|
||||
rc_client_game_hash_t* old_hash;
|
||||
|
||||
if (client->state.load == NULL) {
|
||||
rc_client_unload_game(client);
|
||||
client->state.load = load_state;
|
||||
|
||||
if (load_state->game == NULL) {
|
||||
load_state->game = (rc_client_game_info_t*)calloc(1, sizeof(*load_state->game));
|
||||
if (!load_state->game) {
|
||||
if (load_state->callback)
|
||||
load_state->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, load_state->callback_userdata);
|
||||
|
||||
rc_client_free_load_state(load_state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc_buffer_init(&load_state->game->buffer);
|
||||
rc_runtime_init(&load_state->game->runtime);
|
||||
}
|
||||
}
|
||||
else if (client->state.load != load_state) {
|
||||
/* previous load was aborted */
|
||||
if (load_state->callback)
|
||||
load_state->callback(RC_ABORTED, "The requested game is no longer active", client, load_state->callback_userdata);
|
||||
|
||||
if (!rc_client_attach_load_state(client, load_state)) {
|
||||
rc_client_free_load_state(load_state);
|
||||
return NULL;
|
||||
}
|
||||
@@ -2265,7 +2414,7 @@ static rc_client_async_handle_t* rc_client_load_game(rc_client_load_state_t* loa
|
||||
else {
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Identified game: %u (%s)", load_state->hash->game_id, load_state->hash->hash);
|
||||
|
||||
rc_client_begin_fetch_game_data(load_state);
|
||||
rc_client_process_resolved_hash(load_state);
|
||||
}
|
||||
|
||||
return (client->state.load == load_state) ? &load_state->async_handle : NULL;
|
||||
@@ -2327,8 +2476,12 @@ rc_client_async_handle_t* rc_client_begin_identify_and_load_game(rc_client_t* cl
|
||||
}
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->begin_identify_and_load_game)
|
||||
return client->state.external_client->begin_identify_and_load_game(client, console_id, file_path, data, data_size, callback, callback_userdata);
|
||||
/* if a add_game_hash handler exists, do the identification locally, then pass the
|
||||
* resulting game_id/hash to the external client */
|
||||
if (client->state.external_client && !client->state.external_client->add_game_hash) {
|
||||
if (client->state.external_client->begin_identify_and_load_game)
|
||||
return client->state.external_client->begin_identify_and_load_game(client, console_id, file_path, data, data_size, callback, callback_userdata);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (data) {
|
||||
@@ -2471,6 +2624,13 @@ void rc_client_unload_game(rc_client_t* client)
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->unload_game) {
|
||||
client->state.external_client->unload_game();
|
||||
|
||||
/* a game object may have been allocated to manage hashes */
|
||||
game = client->game;
|
||||
client->game = NULL;
|
||||
if (game != NULL)
|
||||
rc_client_free_game(game);
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -2629,15 +2789,77 @@ static rc_client_async_handle_t* rc_client_begin_change_media_internal(rc_client
|
||||
return rc_client_async_handle_valid(client, async_handle) ? async_handle : NULL;
|
||||
}
|
||||
|
||||
static rc_client_game_info_t* rc_client_check_pending_media(rc_client_t* client, const rc_client_pending_media_t* media)
|
||||
{
|
||||
rc_client_game_info_t* game;
|
||||
rc_client_pending_media_t* pending_media = NULL;
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
if (client->state.load) {
|
||||
game = client->state.load->game;
|
||||
if (!game || game->public_.console_id == 0) {
|
||||
/* still waiting for game data */
|
||||
pending_media = client->state.load->pending_media;
|
||||
if (pending_media)
|
||||
rc_client_free_pending_media(pending_media);
|
||||
|
||||
pending_media = (rc_client_pending_media_t*)malloc(sizeof(*pending_media));
|
||||
if (!pending_media) {
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
media->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, media->callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(pending_media, media, sizeof(*pending_media));
|
||||
if (media->hash)
|
||||
pending_media->hash = strdup(media->hash);
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_HASH
|
||||
if (media->file_path)
|
||||
pending_media->file_path = strdup(media->file_path);
|
||||
|
||||
if (media->data && media->data_size) {
|
||||
pending_media->data = (uint8_t*)malloc(media->data_size);
|
||||
if (!pending_media->data) {
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
media->callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, media->callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(pending_media->data, media->data, media->data_size);
|
||||
} else {
|
||||
pending_media->data = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
client->state.load->pending_media = pending_media;
|
||||
}
|
||||
}
|
||||
else {
|
||||
game = client->game;
|
||||
}
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (!game) {
|
||||
media->callback(RC_NO_GAME_LOADED, rc_error_str(RC_NO_GAME_LOADED), client, media->callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* still waiting for game data - don't call callback - it's queued */
|
||||
if (pending_media)
|
||||
return NULL;
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_HASH
|
||||
|
||||
rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, const char* file_path,
|
||||
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata)
|
||||
{
|
||||
rc_client_pending_media_t media;
|
||||
rc_client_game_hash_t* game_hash = NULL;
|
||||
rc_client_media_hash_t* media_hash;
|
||||
rc_client_game_info_t* game;
|
||||
rc_client_pending_media_t* pending_media = NULL;
|
||||
rc_client_media_hash_t* media_hash;
|
||||
uint32_t path_djb2;
|
||||
|
||||
if (!client) {
|
||||
@@ -2651,55 +2873,21 @@ rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, cons
|
||||
}
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->begin_change_media)
|
||||
return client->state.external_client->begin_change_media(client, file_path, data, data_size, callback, callback_userdata);
|
||||
if (client->state.external_client && !client->state.external_client->begin_change_media_from_hash) {
|
||||
if (client->state.external_client->begin_change_media)
|
||||
return client->state.external_client->begin_change_media(client, file_path, data, data_size, callback, callback_userdata);
|
||||
}
|
||||
#endif
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
if (client->state.load) {
|
||||
game = client->state.load->game;
|
||||
if (game->public_.console_id == 0) {
|
||||
/* still waiting for game data */
|
||||
pending_media = client->state.load->pending_media;
|
||||
if (pending_media)
|
||||
rc_client_free_pending_media(pending_media);
|
||||
memset(&media, 0, sizeof(media));
|
||||
media.file_path = file_path;
|
||||
media.data = (uint8_t*)data;
|
||||
media.data_size = data_size;
|
||||
media.callback = callback;
|
||||
media.callback_userdata = callback_userdata;
|
||||
|
||||
pending_media = (rc_client_pending_media_t*)calloc(1, sizeof(*pending_media));
|
||||
if (!pending_media) {
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pending_media->file_path = strdup(file_path);
|
||||
pending_media->callback = callback;
|
||||
pending_media->callback_userdata = callback_userdata;
|
||||
if (data && data_size) {
|
||||
pending_media->data_size = data_size;
|
||||
pending_media->data = (uint8_t*)malloc(data_size);
|
||||
if (!pending_media->data) {
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(pending_media->data, data, data_size);
|
||||
}
|
||||
|
||||
client->state.load->pending_media = pending_media;
|
||||
}
|
||||
}
|
||||
else {
|
||||
game = client->game;
|
||||
}
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (!game) {
|
||||
callback(RC_NO_GAME_LOADED, rc_error_str(RC_NO_GAME_LOADED), client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* still waiting for game data */
|
||||
if (pending_media)
|
||||
game = rc_client_check_pending_media(client, &media);
|
||||
if (game == NULL)
|
||||
return NULL;
|
||||
|
||||
/* check to see if we've already hashed this file */
|
||||
@@ -2749,11 +2937,25 @@ rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, cons
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (!result) {
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client && client->state.external_client->begin_change_media_from_hash)
|
||||
return client->state.external_client->begin_change_media_from_hash(client, game_hash->hash, callback, callback_userdata);
|
||||
#endif
|
||||
|
||||
rc_client_change_media_internal(client, game_hash, callback, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef RC_CLIENT_SUPPORTS_EXTERNAL
|
||||
if (client->state.external_client) {
|
||||
if (client->state.external_client->add_game_hash)
|
||||
client->state.external_client->add_game_hash(game_hash->hash, game_hash->game_id);
|
||||
if (client->state.external_client->begin_change_media_from_hash)
|
||||
return client->state.external_client->begin_change_media_from_hash(client, game_hash->hash, callback, callback_userdata);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc_client_begin_change_media_internal(client, game, game_hash, callback, callback_userdata);
|
||||
}
|
||||
|
||||
@@ -2762,9 +2964,9 @@ rc_client_async_handle_t* rc_client_begin_change_media(rc_client_t* client, cons
|
||||
rc_client_async_handle_t* rc_client_begin_change_media_from_hash(rc_client_t* client, const char* hash,
|
||||
rc_client_callback_t callback, void* callback_userdata)
|
||||
{
|
||||
rc_client_pending_media_t media;
|
||||
rc_client_game_hash_t* game_hash;
|
||||
rc_client_game_info_t* game;
|
||||
rc_client_pending_media_t* pending_media = NULL;
|
||||
|
||||
if (!client) {
|
||||
callback(RC_INVALID_STATE, "client is required", client, callback_userdata);
|
||||
@@ -2782,40 +2984,13 @@ rc_client_async_handle_t* rc_client_begin_change_media_from_hash(rc_client_t* cl
|
||||
}
|
||||
#endif
|
||||
|
||||
rc_mutex_lock(&client->state.mutex);
|
||||
if (client->state.load) {
|
||||
game = client->state.load->game;
|
||||
if (game->public_.console_id == 0) {
|
||||
/* still waiting for game data */
|
||||
pending_media = client->state.load->pending_media;
|
||||
if (pending_media)
|
||||
rc_client_free_pending_media(pending_media);
|
||||
memset(&media, 0, sizeof(media));
|
||||
media.hash = hash;
|
||||
media.callback = callback;
|
||||
media.callback_userdata = callback_userdata;
|
||||
|
||||
pending_media = (rc_client_pending_media_t*)calloc(1, sizeof(*pending_media));
|
||||
if (!pending_media) {
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
callback(RC_OUT_OF_MEMORY, rc_error_str(RC_OUT_OF_MEMORY), client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pending_media->hash = strdup(hash);
|
||||
pending_media->callback = callback;
|
||||
pending_media->callback_userdata = callback_userdata;
|
||||
|
||||
client->state.load->pending_media = pending_media;
|
||||
}
|
||||
} else {
|
||||
game = client->game;
|
||||
}
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
|
||||
if (!game) {
|
||||
callback(RC_NO_GAME_LOADED, rc_error_str(RC_NO_GAME_LOADED), client, callback_userdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* still waiting for game data */
|
||||
if (pending_media)
|
||||
game = rc_client_check_pending_media(client, &media);
|
||||
if (game == NULL)
|
||||
return NULL;
|
||||
|
||||
/* check to see if we've already hashed this file. */
|
||||
@@ -2882,7 +3057,7 @@ rc_client_async_handle_t* rc_client_begin_load_subset(rc_client_t* client, uint3
|
||||
load_state->hash->game_id = subset_id;
|
||||
client->state.load = load_state;
|
||||
|
||||
rc_client_begin_fetch_game_data(load_state);
|
||||
rc_client_process_resolved_hash(load_state);
|
||||
|
||||
return (client->state.load == load_state) ? &load_state->async_handle : NULL;
|
||||
}
|
||||
@@ -3570,9 +3745,11 @@ static void rc_client_award_achievement(rc_client_t* client, rc_client_achieveme
|
||||
callback_data->client = client;
|
||||
callback_data->id = achievement->public_.id;
|
||||
callback_data->hardcore = client->state.hardcore;
|
||||
callback_data->game_hash = client->game->public_.hash;
|
||||
callback_data->unlock_time = achievement->public_.unlock_time;
|
||||
|
||||
if (client->game) /* may be NULL if this gets called while unloading the game (from another thread - events are raised outside the lock) */
|
||||
callback_data->game_hash = client->game->public_.hash;
|
||||
|
||||
RC_CLIENT_LOG_INFO_FORMATTED(client, "Awarding achievement %u: %s", achievement->public_.id, achievement->public_.title);
|
||||
rc_client_award_achievement_server_call(callback_data);
|
||||
}
|
||||
@@ -4311,6 +4488,7 @@ static void rc_client_fetch_leaderboard_entries_callback(const rc_api_server_res
|
||||
}
|
||||
|
||||
list->num_entries = lbinfo_response.num_entries;
|
||||
list->total_entries = lbinfo_response.total_entries;
|
||||
|
||||
lbinfo_callback_data->callback(RC_OK, NULL, list, client, lbinfo_callback_data->callback_userdata);
|
||||
}
|
||||
@@ -4797,6 +4975,8 @@ static void rc_client_progress_tracker_timer_elapsed(rc_client_scheduled_callbac
|
||||
|
||||
static void rc_client_do_frame_update_progress_tracker(rc_client_t* client, rc_client_game_info_t* game)
|
||||
{
|
||||
/* ASSERT: this should only be called if the mutex is held */
|
||||
|
||||
if (!game->progress_tracker.hide_callback) {
|
||||
game->progress_tracker.hide_callback = (rc_client_scheduled_callback_data_t*)
|
||||
rc_buffer_alloc(&game->buffer, sizeof(rc_client_scheduled_callback_data_t));
|
||||
@@ -5181,6 +5361,7 @@ void rc_client_idle(rc_client_t* client)
|
||||
else {
|
||||
/* remove the callback from the queue while we process it. callback can requeue if desired */
|
||||
client->state.scheduled_callbacks = scheduled_callback->next;
|
||||
scheduled_callback->next = NULL;
|
||||
}
|
||||
}
|
||||
rc_mutex_unlock(&client->state.mutex);
|
||||
@@ -5256,7 +5437,7 @@ static void rc_client_reschedule_callback(rc_client_t* client,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!next || when < next->when) {
|
||||
if (!next || (when < next->when && when != 0)) {
|
||||
/* insert here */
|
||||
callback->next = next;
|
||||
*last = callback;
|
||||
|
||||
10
3rdparty/rcheevos/src/rc_client_external.h
vendored
10
3rdparty/rcheevos/src/rc_client_external.h
vendored
@@ -38,6 +38,7 @@ typedef const rc_client_subset_t* (RC_CCONV *rc_client_external_get_subset_info_
|
||||
typedef void (RC_CCONV *rc_client_external_get_user_game_summary_func_t)(rc_client_user_game_summary_t* summary);
|
||||
typedef rc_client_async_handle_t* (RC_CCONV *rc_client_external_begin_change_media_func_t)(rc_client_t* client, const char* file_path,
|
||||
const uint8_t* data, size_t data_size, rc_client_callback_t callback, void* callback_userdata);
|
||||
typedef void (RC_CCONV* rc_client_external_add_game_hash_func_t)(const char* hash, uint32_t game_id);
|
||||
|
||||
/* NOTE: rc_client_external_create_achievement_list_func_t returns an internal wrapper structure which contains the public list
|
||||
* and a destructor function. */
|
||||
@@ -124,9 +125,16 @@ typedef struct rc_client_external_t
|
||||
rc_client_external_serialize_progress_func_t serialize_progress;
|
||||
rc_client_external_deserialize_progress_func_t deserialize_progress;
|
||||
|
||||
/* VERSION 2 */
|
||||
rc_client_external_add_game_hash_func_t add_game_hash;
|
||||
rc_client_external_set_string_func_t load_unknown_game;
|
||||
|
||||
} rc_client_external_t;
|
||||
|
||||
#define RC_CLIENT_EXTERNAL_VERSION 1
|
||||
#define RC_CLIENT_EXTERNAL_VERSION 2
|
||||
|
||||
void rc_client_add_game_hash(rc_client_t* client, const char* hash, uint32_t game_id);
|
||||
void rc_client_load_unknown_game(rc_client_t* client, const char* hash);
|
||||
|
||||
RC_END_C_DECLS
|
||||
|
||||
|
||||
2
3rdparty/rcheevos/src/rc_version.h
vendored
2
3rdparty/rcheevos/src/rc_version.h
vendored
@@ -8,7 +8,7 @@
|
||||
RC_BEGIN_C_DECLS
|
||||
|
||||
#define RCHEEVOS_VERSION_MAJOR 11
|
||||
#define RCHEEVOS_VERSION_MINOR 4
|
||||
#define RCHEEVOS_VERSION_MINOR 5
|
||||
#define RCHEEVOS_VERSION_PATCH 0
|
||||
|
||||
#define RCHEEVOS_MAKE_VERSION(major, minor, patch) (major * 1000000 + minor * 1000 + patch)
|
||||
|
||||
@@ -809,7 +809,7 @@ uint32_t rc_runtime_progress_size(const rc_runtime_t* runtime, lua_State* L)
|
||||
|
||||
result = rc_runtime_progress_serialize_internal(&progress);
|
||||
if (result != RC_OK)
|
||||
return 0;
|
||||
return result;
|
||||
|
||||
return progress.offset;
|
||||
}
|
||||
|
||||
760
3rdparty/rcheevos/src/rhash/hash.c
vendored
760
3rdparty/rcheevos/src/rhash/hash.c
vendored
@@ -700,6 +700,12 @@ struct rc_hash_zip_idx
|
||||
uint8_t* data;
|
||||
};
|
||||
|
||||
struct rc_hash_ms_dos_dosz_state
|
||||
{
|
||||
const char* path;
|
||||
const struct rc_hash_ms_dos_dosz_state* child;
|
||||
};
|
||||
|
||||
static int rc_hash_zip_idx_sort(const void* a, const void* b)
|
||||
{
|
||||
struct rc_hash_zip_idx *A = (struct rc_hash_zip_idx*)a, *B = (struct rc_hash_zip_idx*)b;
|
||||
@@ -707,9 +713,12 @@ static int rc_hash_zip_idx_sort(const void* a, const void* b)
|
||||
return memcmp(A->data, B->data, len);
|
||||
}
|
||||
|
||||
static int rc_hash_zip_file(md5_state_t* md5, void* file_handle)
|
||||
static int rc_hash_ms_dos_parent(md5_state_t* md5, const struct rc_hash_ms_dos_dosz_state *child, const char* parentname, uint32_t parentname_len);
|
||||
static int rc_hash_ms_dos_dosc(md5_state_t* md5, const struct rc_hash_ms_dos_dosz_state *dosz);
|
||||
|
||||
static int rc_hash_zip_file(md5_state_t* md5, void* file_handle, const struct rc_hash_ms_dos_dosz_state* dosz)
|
||||
{
|
||||
uint8_t buf[2048], *alloc_buf, *cdir_start, *cdir_max, *cdir, *hashdata, eocdirhdr_size, cdirhdr_size;
|
||||
uint8_t buf[2048], *alloc_buf, *cdir_start, *cdir_max, *cdir, *hashdata, eocdirhdr_size, cdirhdr_size, nparents;
|
||||
uint32_t cdir_entry_len;
|
||||
size_t sizeof_idx, indices_offset, alloc_size;
|
||||
int64_t i_file, archive_size, ecdh_ofs, total_files, cdir_size, cdir_ofs;
|
||||
@@ -773,7 +782,7 @@ static int rc_hash_zip_file(md5_state_t* md5, void* file_handle)
|
||||
rc_file_seek(file_handle, ecdh_ofs - 20, SEEK_SET);
|
||||
if (rc_file_read(file_handle, buf, 20) == 20 && RC_ZIP_READ_LE32(buf) == 0x07064b50) /* locator signature */
|
||||
{
|
||||
/* Found the locator, now read the actual ZIP64 end of central directory header */
|
||||
/* Found the locator, now read the actual ZIP64 end of central directory header */
|
||||
int64_t ecdh64_ofs = (int64_t)RC_ZIP_READ_LE64(buf + 0x08);
|
||||
if (ecdh64_ofs <= (archive_size - 56))
|
||||
{
|
||||
@@ -821,7 +830,7 @@ static int rc_hash_zip_file(md5_state_t* md5, void* file_handle)
|
||||
hashindex = hashindices;
|
||||
|
||||
/* Now process the central directory file records */
|
||||
for (i_file = 0, cdir = cdir_start; i_file < total_files && cdir >= cdir_start && cdir <= cdir_max; i_file++, cdir += cdir_entry_len)
|
||||
for (i_file = nparents = 0, cdir = cdir_start; i_file < total_files && cdir >= cdir_start && cdir <= cdir_max; i_file++, cdir += cdir_entry_len)
|
||||
{
|
||||
const uint8_t *name, *name_end;
|
||||
uint32_t signature = RC_ZIP_READ_LE32(cdir + 0x00);
|
||||
@@ -891,6 +900,27 @@ static int rc_hash_zip_file(md5_state_t* md5, void* file_handle)
|
||||
return rc_hash_error("Encountered invalid entry in ZIP central directory");
|
||||
}
|
||||
|
||||
/* A DOSZ file can contain a special empty <base>.dosz.parent file in its root which means a parent dosz file is used */
|
||||
if (dosz && decomp_size == 0 && filename_len > 7 && !strncasecmp((const char*)name + filename_len - 7, ".parent", 7) && !memchr(name, '/', filename_len) && !memchr(name, '\\', filename_len))
|
||||
{
|
||||
/* A DOSZ file can only have one parent file */
|
||||
if (nparents++)
|
||||
{
|
||||
free(alloc_buf);
|
||||
return rc_hash_error("Invalid DOSZ file with multiple parents");
|
||||
}
|
||||
|
||||
/* If there is an error with the parent DOSZ, abort now */
|
||||
if (!rc_hash_ms_dos_parent(md5, dosz, (const char*)name, (filename_len - 7)))
|
||||
{
|
||||
free(alloc_buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We don't hash this meta file so a user is free to rename it and the parent file */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Write the pointer and length of the data we record about this file */
|
||||
hashindex->data = hashdata;
|
||||
hashindex->length = filename_len + 1 + 4 + 8;
|
||||
@@ -935,6 +965,11 @@ static int rc_hash_zip_file(md5_state_t* md5, void* file_handle)
|
||||
md5_append(md5, hashindices->data, (int)hashindices->length);
|
||||
|
||||
free(alloc_buf);
|
||||
|
||||
/* If this is a .dosz file, check if an associated .dosc file exists */
|
||||
if (dosz && !rc_hash_ms_dos_dosc(md5, dosz))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
|
||||
#undef RC_ZIP_READ_LE16
|
||||
@@ -944,10 +979,86 @@ static int rc_hash_zip_file(md5_state_t* md5, void* file_handle)
|
||||
#undef RC_ZIP_WRITE_LE64
|
||||
}
|
||||
|
||||
static int rc_hash_ms_dos_parent(md5_state_t* md5, const struct rc_hash_ms_dos_dosz_state *child, const char* parentname, uint32_t parentname_len)
|
||||
{
|
||||
const char *lastfslash = strrchr(child->path, '/');
|
||||
const char *lastbslash = strrchr(child->path, '\\');
|
||||
const char *lastslash = (lastbslash > lastfslash ? lastbslash : lastfslash);
|
||||
size_t dir_len = (lastslash ? (lastslash + 1 - child->path) : 0);
|
||||
char* parent_path = (char*)malloc(dir_len + parentname_len + 1);
|
||||
struct rc_hash_ms_dos_dosz_state parent;
|
||||
const struct rc_hash_ms_dos_dosz_state *check;
|
||||
void* parent_handle;
|
||||
int parent_res;
|
||||
|
||||
/* Build the path of the parent by combining the directory of the current file with the name */
|
||||
if (!parent_path)
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
|
||||
memcpy(parent_path, child->path, dir_len);
|
||||
memcpy(parent_path + dir_len, parentname, parentname_len);
|
||||
parent_path[dir_len + parentname_len] = '\0';
|
||||
|
||||
/* Make sure there is no recursion where a parent DOSZ is an already seen child DOSZ */
|
||||
for (check = child->child; check; check = check->child)
|
||||
{
|
||||
if (!strcmp(check->path, parent_path))
|
||||
{
|
||||
free(parent_path);
|
||||
return rc_hash_error("Invalid DOSZ file with recursive parents");
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to open the parent DOSZ file */
|
||||
parent_handle = rc_file_open(parent_path);
|
||||
if (!parent_handle)
|
||||
{
|
||||
char message[1024];
|
||||
snprintf(message, sizeof(message), "DOSZ parent file '%s' does not exist", parent_path);
|
||||
free(parent_path);
|
||||
return rc_hash_error(message);
|
||||
}
|
||||
|
||||
/* Fully hash the parent DOSZ ahead of the child */
|
||||
parent.path = parent_path;
|
||||
parent.child = child;
|
||||
parent_res = rc_hash_zip_file(md5, parent_handle, &parent);
|
||||
rc_file_close(parent_handle);
|
||||
free(parent_path);
|
||||
return parent_res;
|
||||
}
|
||||
|
||||
static int rc_hash_ms_dos_dosc(md5_state_t* md5, const struct rc_hash_ms_dos_dosz_state *dosz)
|
||||
{
|
||||
size_t path_len = strlen(dosz->path);
|
||||
if (dosz->path[path_len-1] == 'z' || dosz->path[path_len-1] == 'Z')
|
||||
{
|
||||
void* file_handle;
|
||||
char *dosc_path = strdup(dosz->path);
|
||||
if (!dosc_path)
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
|
||||
/* Swap the z to c and use the same capitalization, hash the file if it exists */
|
||||
dosc_path[path_len-1] = (dosz->path[path_len-1] == 'z' ? 'c' : 'C');
|
||||
file_handle = rc_file_open(dosc_path);
|
||||
free(dosc_path);
|
||||
|
||||
if (file_handle)
|
||||
{
|
||||
/* Hash the DOSC as a plain zip file (pass NULL as dosz state) */
|
||||
int res = rc_hash_zip_file(md5, file_handle, NULL);
|
||||
rc_file_close(file_handle);
|
||||
if (!res)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rc_hash_ms_dos(char hash[33], const char* path)
|
||||
{
|
||||
struct rc_hash_ms_dos_dosz_state dosz;
|
||||
md5_state_t md5;
|
||||
size_t path_len;
|
||||
int res;
|
||||
|
||||
void* file_handle = rc_file_open(path);
|
||||
@@ -956,34 +1067,14 @@ static int rc_hash_ms_dos(char hash[33], const char* path)
|
||||
|
||||
/* hash the main content zip file first */
|
||||
md5_init(&md5);
|
||||
res = rc_hash_zip_file(&md5, file_handle);
|
||||
dosz.path = path;
|
||||
dosz.child = NULL;
|
||||
res = rc_hash_zip_file(&md5, file_handle, &dosz);
|
||||
rc_file_close(file_handle);
|
||||
|
||||
if (!res)
|
||||
return 0;
|
||||
|
||||
/* if this is a .dosz file, check if an associated .dosc file exists */
|
||||
path_len = strlen(path);
|
||||
if (path[path_len-1] == 'z' || path[path_len-1] == 'Z')
|
||||
{
|
||||
char *dosc_path = strdup(path);
|
||||
if (!dosc_path)
|
||||
return rc_hash_error("Could not allocate temporary buffer");
|
||||
|
||||
/* swap the z to c and use the same capitalization, hash the file if it exists*/
|
||||
dosc_path[path_len-1] = (path[path_len-1] == 'z' ? 'c' : 'C');
|
||||
file_handle = rc_file_open(dosc_path);
|
||||
free((void*)dosc_path);
|
||||
if (file_handle)
|
||||
{
|
||||
res = rc_hash_zip_file(&md5, file_handle);
|
||||
rc_file_close(file_handle);
|
||||
|
||||
if (!res)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
@@ -1463,6 +1554,619 @@ static int rc_hash_n64(char hash[33], const char* path)
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
static int rc_hash_nintendo_3ds_ncch(md5_state_t* md5, void* file_handle, uint8_t header[0x200], struct AES_ctx* cia_aes)
|
||||
{
|
||||
struct AES_ctx ncch_aes;
|
||||
uint8_t* hash_buffer;
|
||||
uint64_t exefs_offset, exefs_real_size;
|
||||
uint32_t exefs_buffer_size;
|
||||
uint8_t primary_key[AES_KEYLEN], secondary_key[AES_KEYLEN];
|
||||
uint8_t fixed_key_flag, no_crypto_flag, seed_crypto_flag;
|
||||
uint8_t crypto_method, secondary_key_x_slot;
|
||||
uint16_t ncch_version;
|
||||
uint32_t i;
|
||||
uint8_t primary_key_y[AES_KEYLEN], program_id[sizeof(uint64_t)];
|
||||
uint8_t iv[AES_BLOCKLEN];
|
||||
uint8_t exefs_section_name[8];
|
||||
uint64_t exefs_section_offset, exefs_section_size;
|
||||
|
||||
exefs_offset = ((uint32_t)header[0x1A3] << 24) | (header[0x1A2] << 16) | (header[0x1A1] << 8) | header[0x1A0];
|
||||
exefs_real_size = ((uint32_t)header[0x1A7] << 24) | (header[0x1A6] << 16) | (header[0x1A5] << 8) | header[0x1A4];
|
||||
|
||||
/* Offset and size are in "media units" (1 media unit = 0x200 bytes) */
|
||||
exefs_offset *= 0x200;
|
||||
exefs_real_size *= 0x200;
|
||||
|
||||
if (exefs_real_size > MAX_BUFFER_SIZE)
|
||||
exefs_buffer_size = MAX_BUFFER_SIZE;
|
||||
else
|
||||
exefs_buffer_size = (uint32_t)exefs_real_size;
|
||||
|
||||
/* This region is technically optional, but it should always be present for executable content (i.e. games) */
|
||||
if (exefs_offset == 0 || exefs_real_size == 0)
|
||||
return rc_hash_error("ExeFS was not available");
|
||||
|
||||
/* NCCH flag 7 is a bitfield of various crypto related flags */
|
||||
fixed_key_flag = header[0x188 + 7] & 0x01;
|
||||
no_crypto_flag = header[0x188 + 7] & 0x04;
|
||||
seed_crypto_flag = header[0x188 + 7] & 0x20;
|
||||
|
||||
ncch_version = (header[0x113] << 8) | header[0x112];
|
||||
|
||||
if (no_crypto_flag == 0)
|
||||
{
|
||||
rc_hash_verbose("Encrypted NCCH detected");
|
||||
|
||||
if (fixed_key_flag != 0)
|
||||
{
|
||||
/* Fixed crypto key means all 0s for both keys */
|
||||
memset(primary_key, 0, sizeof(primary_key));
|
||||
memset(secondary_key, 0, sizeof(secondary_key));
|
||||
rc_hash_verbose("Using fixed key crypto");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_3ds_get_ncch_normal_keys_func == NULL)
|
||||
return rc_hash_error("An encrypted NCCH was detected, but the NCCH normal keys callback was not set");
|
||||
|
||||
/* Primary key y is just the first 16 bytes of the header */
|
||||
memcpy(primary_key_y, header, sizeof(primary_key_y));
|
||||
|
||||
/* NCCH flag 3 indicates which secondary key x slot is used */
|
||||
crypto_method = header[0x188 + 3];
|
||||
|
||||
switch (crypto_method)
|
||||
{
|
||||
case 0x00:
|
||||
rc_hash_verbose("Using NCCH crypto method v1");
|
||||
secondary_key_x_slot = 0x2C;
|
||||
break;
|
||||
case 0x01:
|
||||
rc_hash_verbose("Using NCCH crypto method v2");
|
||||
secondary_key_x_slot = 0x25;
|
||||
break;
|
||||
case 0x0A:
|
||||
rc_hash_verbose("Using NCCH crypto method v3");
|
||||
secondary_key_x_slot = 0x18;
|
||||
break;
|
||||
case 0x0B:
|
||||
rc_hash_verbose("Using NCCH crypto method v4");
|
||||
secondary_key_x_slot = 0x1B;
|
||||
break;
|
||||
default:
|
||||
snprintf((char*)header, 0x200, "Invalid crypto method %02X", (unsigned)crypto_method);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
/* We only need the program id if we're doing seed crypto */
|
||||
if (seed_crypto_flag != 0)
|
||||
{
|
||||
rc_hash_verbose("Using seed crypto");
|
||||
memcpy(program_id, &header[0x118], sizeof(program_id));
|
||||
}
|
||||
|
||||
if (_3ds_get_ncch_normal_keys_func(primary_key_y, secondary_key_x_slot, seed_crypto_flag != 0 ? program_id : NULL, primary_key, secondary_key) == 0)
|
||||
return rc_hash_error("Could not obtain NCCH normal keys");
|
||||
}
|
||||
|
||||
switch (ncch_version)
|
||||
{
|
||||
case 0:
|
||||
case 2:
|
||||
rc_hash_verbose("Detected NCCH version 0/2");
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
/* First 8 bytes is the partition id in reverse byte order */
|
||||
iv[7 - i] = header[0x108 + i];
|
||||
}
|
||||
|
||||
/* Magic number for ExeFS */
|
||||
iv[8] = 2;
|
||||
|
||||
/* Rest of the bytes are 0 */
|
||||
memset(&iv[9], 0, sizeof(iv) - 9);
|
||||
break;
|
||||
case 1:
|
||||
rc_hash_verbose("Detected NCCH version 1");
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
/* First 8 bytes is the partition id in normal byte order */
|
||||
iv[i] = header[0x108 + i];
|
||||
}
|
||||
|
||||
/* Next 4 bytes are 0 */
|
||||
memset(&iv[8], 0, 4);
|
||||
|
||||
/* Last 4 bytes is the ExeFS byte offset in big endian */
|
||||
iv[12] = (exefs_offset >> 24) & 0xFF;
|
||||
iv[13] = (exefs_offset >> 16) & 0xFF;
|
||||
iv[14] = (exefs_offset >> 8) & 0xFF;
|
||||
iv[15] = exefs_offset & 0xFF;
|
||||
break;
|
||||
default:
|
||||
snprintf((char*)header, 0x200, "Invalid NCCH version %04X", (unsigned)ncch_version);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
}
|
||||
|
||||
/* ASSERT: file position must be +0x200 from start of NCCH (i.e. end of header) */
|
||||
exefs_offset -= 0x200;
|
||||
|
||||
if (cia_aes)
|
||||
{
|
||||
/* We have to decrypt the data between the header and the ExeFS so the CIA AES state is correct
|
||||
* when we reach the ExeFS. This decrypted data is not included in the RetroAchievements hash */
|
||||
|
||||
/* This should never happen in practice, but just in case */
|
||||
if (exefs_offset > MAX_BUFFER_SIZE)
|
||||
return rc_hash_error("Too much data required to decrypt in order to hash");
|
||||
|
||||
hash_buffer = (uint8_t*)malloc((uint32_t)exefs_offset);
|
||||
if (!hash_buffer)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Failed to allocate %u bytes", (unsigned)exefs_offset);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
if (rc_file_read(file_handle, hash_buffer, (uint32_t)exefs_offset) != (uint32_t)exefs_offset)
|
||||
{
|
||||
free(hash_buffer);
|
||||
return rc_hash_error("Could not read NCCH data");
|
||||
}
|
||||
|
||||
AES_CBC_decrypt_buffer(cia_aes, hash_buffer, (uint32_t)exefs_offset);
|
||||
free(hash_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No decryption needed, just skip over the in-between data */
|
||||
rc_file_seek(file_handle, (int64_t)exefs_offset, SEEK_CUR);
|
||||
}
|
||||
|
||||
hash_buffer = (uint8_t*)malloc(exefs_buffer_size);
|
||||
if (!hash_buffer)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Failed to allocate %u bytes", (unsigned)exefs_buffer_size);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
/* Clear out crypto flags to ensure we get the same hash for decrypted and encrypted ROMs */
|
||||
memset(&header[0x114], 0, 4);
|
||||
header[0x188 + 3] = 0;
|
||||
header[0x188 + 7] &= ~(0x20 | 0x04 | 0x01);
|
||||
|
||||
rc_hash_verbose("Hashing 512 byte NCCH header");
|
||||
md5_append(md5, header, 0x200);
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Hashing %u bytes for ExeFS (at NCCH offset %08X%08X)", (unsigned)exefs_buffer_size, (unsigned)(exefs_offset >> 32), (unsigned)exefs_offset);
|
||||
verbose_message_callback((const char*)header);
|
||||
}
|
||||
|
||||
if (rc_file_read(file_handle, hash_buffer, exefs_buffer_size) != exefs_buffer_size)
|
||||
{
|
||||
free(hash_buffer);
|
||||
return rc_hash_error("Could not read ExeFS data");
|
||||
}
|
||||
|
||||
if (cia_aes)
|
||||
{
|
||||
rc_hash_verbose("Performing CIA decryption for ExeFS");
|
||||
AES_CBC_decrypt_buffer(cia_aes, hash_buffer, exefs_buffer_size);
|
||||
}
|
||||
|
||||
if (no_crypto_flag == 0)
|
||||
{
|
||||
rc_hash_verbose("Performing NCCH decryption for ExeFS");
|
||||
|
||||
AES_init_ctx_iv(&ncch_aes, primary_key, iv);
|
||||
AES_CTR_xcrypt_buffer(&ncch_aes, hash_buffer, 0x200);
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
memcpy(exefs_section_name, &hash_buffer[i * 16], sizeof(exefs_section_name));
|
||||
exefs_section_offset = ((uint32_t)hash_buffer[i * 16 + 11] << 24) | (hash_buffer[i * 16 + 10] << 16) | (hash_buffer[i * 16 + 9] << 8) | hash_buffer[i * 16 + 8];
|
||||
exefs_section_size = ((uint32_t)hash_buffer[i * 16 + 15] << 24) | (hash_buffer[i * 16 + 14] << 16) | (hash_buffer[i * 16 + 13] << 8) | hash_buffer[i * 16 + 12];
|
||||
|
||||
/* 0 size indicates an unused section */
|
||||
if (exefs_section_size == 0)
|
||||
continue;
|
||||
|
||||
/* Offsets must be aligned by a media unit */
|
||||
if (exefs_section_offset & 0x1FF)
|
||||
return rc_hash_error("ExeFS section offset is misaligned");
|
||||
|
||||
/* Offset is relative to the end of the header */
|
||||
exefs_section_offset += 0x200;
|
||||
|
||||
/* Check against malformed sections */
|
||||
if (exefs_section_offset + ((exefs_section_size + 0x1FF) & ~(uint64_t)0x1FF) > (uint64_t)exefs_real_size)
|
||||
return rc_hash_error("ExeFS section would overflow");
|
||||
|
||||
if (memcmp(exefs_section_name, "icon", 4) == 0 || memcmp(exefs_section_name, "banner", 6) == 0)
|
||||
{
|
||||
/* Align size up by a media unit */
|
||||
exefs_section_size = (exefs_section_size + 0x1FF) & ~(uint64_t)0x1FF;
|
||||
AES_init_ctx(&ncch_aes, primary_key);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We don't align size up here, as the padding bytes will use the primary key rather than the secondary key */
|
||||
AES_init_ctx(&ncch_aes, secondary_key);
|
||||
}
|
||||
|
||||
/* In theory, the section offset + size could be greater than the buffer size */
|
||||
/* In practice, this likely never occurs, but just in case it does, ignore the section or constrict the size */
|
||||
if (exefs_section_offset + exefs_section_size > exefs_buffer_size)
|
||||
{
|
||||
if (exefs_section_offset >= exefs_buffer_size)
|
||||
continue;
|
||||
|
||||
exefs_section_size = exefs_buffer_size - exefs_section_offset;
|
||||
}
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
exefs_section_name[7] = '\0';
|
||||
snprintf((char*)header, 0x200, "Decrypting ExeFS file %s at ExeFS offset %08X with size %08X", (const char*)exefs_section_name, (unsigned)exefs_section_offset, (unsigned)exefs_section_size);
|
||||
verbose_message_callback((const char*)header);
|
||||
}
|
||||
|
||||
AES_CTR_xcrypt_buffer(&ncch_aes, &hash_buffer[exefs_section_offset], exefs_section_size & ~(uint64_t)0xF);
|
||||
|
||||
if (exefs_section_size & 0x1FF)
|
||||
{
|
||||
/* Handle padding bytes, these always use the primary key */
|
||||
exefs_section_offset += exefs_section_size;
|
||||
exefs_section_size = 0x200 - (exefs_section_size & 0x1FF);
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Decrypting ExeFS padding at ExeFS offset %08X with size %08X", (unsigned)exefs_section_offset, (unsigned)exefs_section_size);
|
||||
verbose_message_callback((const char*)header);
|
||||
}
|
||||
|
||||
/* Align our decryption start to an AES block boundary */
|
||||
if (exefs_section_size & 0xF)
|
||||
{
|
||||
/* We're a little evil here re-using the IV like this, but this seems to be the best way to deal with this... */
|
||||
memcpy(iv, ncch_aes.Iv, sizeof(iv));
|
||||
exefs_section_offset &= ~(uint64_t)0xF;
|
||||
|
||||
/* First decrypt these last bytes using the secondary key */
|
||||
AES_CTR_xcrypt_buffer(&ncch_aes, &hash_buffer[exefs_section_offset], 0x10 - (exefs_section_size & 0xF));
|
||||
|
||||
/* Now re-encrypt these bytes using the primary key */
|
||||
AES_init_ctx_iv(&ncch_aes, primary_key, iv);
|
||||
AES_CTR_xcrypt_buffer(&ncch_aes, &hash_buffer[exefs_section_offset], 0x10 - (exefs_section_size & 0xF));
|
||||
|
||||
/* All of the padding can now be decrypted using the primary key */
|
||||
AES_ctx_set_iv(&ncch_aes, iv);
|
||||
exefs_section_size += 0x10 - (exefs_section_size & 0xF);
|
||||
}
|
||||
|
||||
AES_init_ctx(&ncch_aes, primary_key);
|
||||
AES_CTR_xcrypt_buffer(&ncch_aes, &hash_buffer[exefs_section_offset], (size_t)exefs_section_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
md5_append(md5, hash_buffer, exefs_buffer_size);
|
||||
|
||||
free(hash_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uint32_t rc_hash_nintendo_3ds_cia_signature_size(uint8_t header[0x200])
|
||||
{
|
||||
uint32_t signature_type;
|
||||
|
||||
signature_type = ((uint32_t)header[0] << 24) | (header[1] << 16) | (header[2] << 8) | header[3];
|
||||
switch (signature_type)
|
||||
{
|
||||
case 0x010000:
|
||||
case 0x010003:
|
||||
return 0x200 + 0x3C;
|
||||
case 0x010001:
|
||||
case 0x010004:
|
||||
return 0x100 + 0x3C;
|
||||
case 0x010002:
|
||||
case 0x010005:
|
||||
return 0x3C + 0x40;
|
||||
default:
|
||||
snprintf((char*)header, 0x200, "Invalid signature type %08X", (unsigned)signature_type);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
}
|
||||
|
||||
static int rc_hash_nintendo_3ds_cia(md5_state_t* md5, void* file_handle, uint8_t header[0x200])
|
||||
{
|
||||
const uint32_t CIA_HEADER_SIZE = 0x2020; /* Yes, this is larger than the header[0x200], but we only use the beginning of the header */
|
||||
const uint64_t CIA_ALIGNMENT_MASK = 64 - 1; /* sizes are aligned by 64 bytes */
|
||||
struct AES_ctx aes;
|
||||
uint8_t iv[AES_BLOCKLEN], normal_key[AES_KEYLEN], title_key[AES_KEYLEN], title_id[sizeof(uint64_t)];
|
||||
uint32_t cert_size, tik_size, tmd_size;
|
||||
int64_t cert_offset, tik_offset, tmd_offset, content_offset;
|
||||
uint32_t signature_size, i;
|
||||
uint16_t content_count;
|
||||
uint8_t common_key_index;
|
||||
|
||||
cert_size = ((uint32_t)header[0x0B] << 24) | (header[0x0A] << 16) | (header[0x09] << 8) | header[0x08];
|
||||
tik_size = ((uint32_t)header[0x0F] << 24) | (header[0x0E] << 16) | (header[0x0D] << 8) | header[0x0C];
|
||||
tmd_size = ((uint32_t)header[0x13] << 24) | (header[0x12] << 16) | (header[0x11] << 8) | header[0x10];
|
||||
|
||||
cert_offset = (CIA_HEADER_SIZE + CIA_ALIGNMENT_MASK) & ~CIA_ALIGNMENT_MASK;
|
||||
tik_offset = (cert_offset + cert_size + CIA_ALIGNMENT_MASK) & ~CIA_ALIGNMENT_MASK;
|
||||
tmd_offset = (tik_offset + tik_size + CIA_ALIGNMENT_MASK) & ~CIA_ALIGNMENT_MASK;
|
||||
content_offset = (tmd_offset + tmd_size + CIA_ALIGNMENT_MASK) & ~CIA_ALIGNMENT_MASK;
|
||||
|
||||
/* Check if this CIA is encrypted, if it isn't, we can hash it right away */
|
||||
|
||||
rc_file_seek(file_handle, tmd_offset, SEEK_SET);
|
||||
if (rc_file_read(file_handle, header, 4) != 4)
|
||||
return rc_hash_error("Could not read TMD signature type");
|
||||
|
||||
signature_size = rc_hash_nintendo_3ds_cia_signature_size(header);
|
||||
if (signature_size == 0)
|
||||
return 0; /* rc_hash_nintendo_3ds_cia_signature_size will call rc_hash_error, so we don't need to do so here */
|
||||
|
||||
rc_file_seek(file_handle, signature_size + 0x9E, SEEK_CUR);
|
||||
if (rc_file_read(file_handle, header, 2) != 2)
|
||||
return rc_hash_error("Could not read TMD content count");
|
||||
|
||||
content_count = (header[0] << 8) | header[1];
|
||||
|
||||
rc_file_seek(file_handle, 0x9C4 - 0x9E - 2, SEEK_CUR);
|
||||
for (i = 0; i < content_count; i++)
|
||||
{
|
||||
if (rc_file_read(file_handle, header, 0x30) != 0x30)
|
||||
return rc_hash_error("Could not read TMD content chunk");
|
||||
|
||||
/* Content index 0 is the main content (i.e. the 3DS executable) */
|
||||
if (((header[4] << 8) | header[5]) == 0)
|
||||
break;
|
||||
|
||||
content_offset += ((uint32_t)header[0xC] << 24) | (header[0xD] << 16) | (header[0xE] << 8) | header[0xF];
|
||||
}
|
||||
|
||||
if (i == content_count)
|
||||
return rc_hash_error("Could not find main content chunk in TMD");
|
||||
|
||||
if ((header[7] & 1) == 0)
|
||||
{
|
||||
/* Not encrypted, we can hash the NCCH immediately */
|
||||
rc_file_seek(file_handle, content_offset, SEEK_SET);
|
||||
if (rc_file_read(file_handle, header, 0x200) != 0x200)
|
||||
return rc_hash_error("Could not read NCCH header");
|
||||
|
||||
if (memcmp(&header[0x100], "NCCH", 4) != 0)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "NCCH header was not at %08X%08X", (unsigned)(content_offset >> 32), (unsigned)content_offset);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
return rc_hash_nintendo_3ds_ncch(md5, file_handle, header, NULL);
|
||||
}
|
||||
|
||||
if (_3ds_get_cia_normal_key_func == NULL)
|
||||
return rc_hash_error("An encrypted CIA was detected, but the CIA normal key callback was not set");
|
||||
|
||||
/* Acquire the encrypted title key, title id, and common key index from the ticket */
|
||||
/* These will be needed to decrypt the title key, and that will be needed to decrypt the CIA */
|
||||
|
||||
rc_file_seek(file_handle, tik_offset, SEEK_SET);
|
||||
if (rc_file_read(file_handle, header, 4) != 4)
|
||||
return rc_hash_error("Could not read ticket signature type");
|
||||
|
||||
signature_size = rc_hash_nintendo_3ds_cia_signature_size(header);
|
||||
if (signature_size == 0)
|
||||
return 0;
|
||||
|
||||
rc_file_seek(file_handle, signature_size, SEEK_CUR);
|
||||
if (rc_file_read(file_handle, header, 0xB2) != 0xB2)
|
||||
return rc_hash_error("Could not read ticket data");
|
||||
|
||||
memcpy(title_key, &header[0x7F], sizeof(title_key));
|
||||
memcpy(title_id, &header[0x9C], sizeof(title_id));
|
||||
common_key_index = header[0xB1];
|
||||
|
||||
if (common_key_index > 5)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Invalid common key index %02X", (unsigned)common_key_index);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
if (_3ds_get_cia_normal_key_func(common_key_index, normal_key) == 0)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Could not obtain common key %02X", (unsigned)common_key_index);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
memset(iv, 0, sizeof(iv));
|
||||
memcpy(iv, title_id, sizeof(title_id));
|
||||
AES_init_ctx_iv(&aes, normal_key, iv);
|
||||
|
||||
/* Finally, decrypt the title key */
|
||||
AES_CBC_decrypt_buffer(&aes, title_key, sizeof(title_key));
|
||||
|
||||
/* Now we can hash the NCCH */
|
||||
|
||||
rc_file_seek(file_handle, content_offset, SEEK_SET);
|
||||
if (rc_file_read(file_handle, header, 0x200) != 0x200)
|
||||
return rc_hash_error("Could not read NCCH header");
|
||||
|
||||
memset(iv, 0, sizeof(iv)); /* Content index is iv (which is always 0 for main content) */
|
||||
AES_init_ctx_iv(&aes, title_key, iv);
|
||||
AES_CBC_decrypt_buffer(&aes, header, 0x200);
|
||||
|
||||
if (memcmp(&header[0x100], "NCCH", 4) != 0)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "NCCH header was not at %08X%08X", (unsigned)(content_offset >> 32), (unsigned)content_offset);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
return rc_hash_nintendo_3ds_ncch(md5, file_handle, header, &aes);
|
||||
}
|
||||
|
||||
static int rc_hash_nintendo_3ds_3dsx(md5_state_t* md5, void* file_handle, uint8_t header[0x200])
|
||||
{
|
||||
uint8_t* hash_buffer;
|
||||
uint32_t header_size, reloc_header_size, code_size;
|
||||
int64_t code_offset;
|
||||
|
||||
header_size = (header[5] << 8) | header[4];
|
||||
reloc_header_size = (header[7] << 8) | header[6];
|
||||
code_size = ((uint32_t)header[0x13] << 24) | (header[0x12] << 16) | (header[0x11] << 8) | header[0x10];
|
||||
|
||||
/* 3 relocation headers are in-between the 3DSX header and code segment */
|
||||
code_offset = header_size + reloc_header_size * 3;
|
||||
|
||||
if (code_size > MAX_BUFFER_SIZE)
|
||||
code_size = MAX_BUFFER_SIZE;
|
||||
|
||||
hash_buffer = (uint8_t*)malloc(code_size);
|
||||
if (!hash_buffer)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Failed to allocate %u bytes", (unsigned)code_size);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
|
||||
rc_file_seek(file_handle, code_offset, SEEK_SET);
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf((char*)header, 0x200, "Hashing %u bytes for 3DSX (at %08X)", (unsigned)code_size, (unsigned)code_offset);
|
||||
verbose_message_callback((const char*)header);
|
||||
}
|
||||
|
||||
if (rc_file_read(file_handle, hash_buffer, code_size) != code_size)
|
||||
{
|
||||
free(hash_buffer);
|
||||
return rc_hash_error("Could not read 3DSX code segment");
|
||||
}
|
||||
|
||||
md5_append(md5, hash_buffer, code_size);
|
||||
|
||||
free(hash_buffer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int rc_hash_nintendo_3ds(char hash[33], const char* path)
|
||||
{
|
||||
md5_state_t md5;
|
||||
void* file_handle;
|
||||
uint8_t header[0x200]; /* NCCH and NCSD headers are both 0x200 bytes */
|
||||
int64_t header_offset;
|
||||
|
||||
file_handle = rc_file_open(path);
|
||||
if (!file_handle)
|
||||
return rc_hash_error("Could not open file");
|
||||
|
||||
rc_file_seek(file_handle, 0, SEEK_SET);
|
||||
|
||||
/* If we don't have a full header, this is probably not a 3DS ROM */
|
||||
if (rc_file_read(file_handle, header, sizeof(header)) != sizeof(header))
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Could not read 3DS ROM header");
|
||||
}
|
||||
|
||||
md5_init(&md5);
|
||||
|
||||
if (memcmp(&header[0x100], "NCSD", 4) == 0)
|
||||
{
|
||||
/* A NCSD container contains 1-8 NCCH partitions */
|
||||
/* The first partition (index 0) is reserved for executable content */
|
||||
header_offset = ((uint32_t)header[0x123] << 24) | (header[0x122] << 16) | (header[0x121] << 8) | header[0x120];
|
||||
/* Offset is in "media units" (1 media unit = 0x200 bytes) */
|
||||
header_offset *= 0x200;
|
||||
|
||||
/* We include the NCSD header in the hash, as that will ensure different versions of a game result in a different hash
|
||||
* This is due to some revisions / languages only ever changing other NCCH paritions (e.g. the game manual)
|
||||
*/
|
||||
rc_hash_verbose("Hashing 512 byte NCSD header");
|
||||
md5_append(&md5, header, sizeof(header));
|
||||
|
||||
if (verbose_message_callback)
|
||||
{
|
||||
snprintf((char*)header, sizeof(header), "Detected NCSD header, seeking to NCCH partition at %08X%08X", (unsigned)(header_offset >> 32), (unsigned)header_offset);
|
||||
verbose_message_callback((const char*)header);
|
||||
}
|
||||
|
||||
rc_file_seek(file_handle, header_offset, SEEK_SET);
|
||||
if (rc_file_read(file_handle, header, sizeof(header)) != sizeof(header))
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Could not read 3DS NCCH header");
|
||||
}
|
||||
|
||||
if (memcmp(&header[0x100], "NCCH", 4) != 0)
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
snprintf((char*)header, sizeof(header), "3DS NCCH header was not at %08X%08X", (unsigned)(header_offset >> 32), (unsigned)header_offset);
|
||||
return rc_hash_error((const char*)header);
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(&header[0x100], "NCCH", 4) == 0)
|
||||
{
|
||||
if (rc_hash_nintendo_3ds_ncch(&md5, file_handle, header, NULL))
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Failed to hash 3DS NCCH container");
|
||||
}
|
||||
|
||||
/* Couldn't identify either an NCSD or NCCH */
|
||||
|
||||
/* Try to identify this as a CIA */
|
||||
if (header[0] == 0x20 && header[1] == 0x20 && header[2] == 0x00 && header[3] == 0x00)
|
||||
{
|
||||
rc_hash_verbose("Detected CIA, attempting to find executable NCCH");
|
||||
|
||||
if (rc_hash_nintendo_3ds_cia(&md5, file_handle, header))
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Failed to hash 3DS CIA container");
|
||||
}
|
||||
|
||||
/* This might be a homebrew game, try to detect that */
|
||||
if (memcmp(&header[0], "3DSX", 4) == 0)
|
||||
{
|
||||
rc_hash_verbose("Detected 3DSX");
|
||||
|
||||
if (rc_hash_nintendo_3ds_3dsx(&md5, file_handle, header))
|
||||
{
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_finalize(&md5, hash);
|
||||
}
|
||||
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Failed to hash 3DS 3DSX container");
|
||||
}
|
||||
|
||||
/* Raw ELF marker (AXF/ELF files) */
|
||||
if (memcmp(&header[0], "\x7f\x45\x4c\x46", 4) == 0)
|
||||
{
|
||||
rc_hash_verbose("Detected AXF/ELF file, hashing entire file");
|
||||
|
||||
/* Don't bother doing anything fancy here, just hash entire file */
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_whole_file(hash, path);
|
||||
}
|
||||
|
||||
rc_file_close(file_handle);
|
||||
return rc_hash_error("Not a 3DS ROM");
|
||||
}
|
||||
|
||||
static int rc_hash_nintendo_ds(char hash[33], const char* path)
|
||||
{
|
||||
uint8_t header[512];
|
||||
|
||||
41
3rdparty/soundtouch/README.html
vendored
41
3rdparty/soundtouch/README.html
vendored
@@ -15,8 +15,8 @@
|
||||
|
||||
<body class="normal">
|
||||
<hr>
|
||||
<h1>SoundTouch audio processing library v2.3.1</h1>
|
||||
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2021</p>
|
||||
<h1>SoundTouch audio processing library v2.3.3</h1>
|
||||
<p class="normal">SoundTouch library Copyright © Olli Parviainen 2001-2024</p>
|
||||
<hr>
|
||||
<h2>1. Introduction </h2>
|
||||
<p>SoundTouch is an open-source audio processing library that allows
|
||||
@@ -35,7 +35,7 @@
|
||||
<p>Author email: oparviai 'at' iki.fi </p>
|
||||
<p>SoundTouch WWW page: <a href="http://soundtouch.surina.net">http://soundtouch.surina.net</a></p>
|
||||
<p>SoundTouch git repository: <a
|
||||
href="https://gitlab.com/soundtouch/soundtouch.git">https://gitlab.com/soundtouch/soundtouch.git</a></p>
|
||||
href="https://codeberg.org/soundtouch/soundtouch.git">https://codeberg.org/soundtouch/soundtouch.git</a></p>
|
||||
<hr>
|
||||
<h2>2. Compiling SoundTouch</h2>
|
||||
<p>Before compiling, notice that you can choose the sample data format if it's
|
||||
@@ -131,10 +131,12 @@
|
||||
</table>
|
||||
|
||||
<b>Compiling portable Shared Library / DLL version</b>
|
||||
<p> The GNU autotools compilation does not automatically create a shared-library version of
|
||||
SoundTouch (.so or .dll) that features position-independent code and C-language
|
||||
api that are more suitable for cross-language development than C++ libraries.</p>
|
||||
<p> Use script "make-gnu-dll-sh" to build a portable dynamic library version if such is desired.</p>
|
||||
<p> The GNU autotools compilation automatically builds an additional dynamic-link version
|
||||
of SoundTouch library that features position-independent code and "C"-style API that is
|
||||
more suitable for calling the SoundTouch routines from other programming languages.</p>
|
||||
<p>This dynamic-link library is built under source/SoundTouchDLL directory, whose
|
||||
subdirectories also comtain simple example apps that use the dynamic-link library.
|
||||
</p>
|
||||
|
||||
<h4><b>2.2.2 Compiling with cmake</b></h4>
|
||||
<p>'cmake' build scripts are provided as an alternative to the autotools toolchain.</p>
|
||||
@@ -145,6 +147,9 @@
|
||||
cmake .
|
||||
make -j
|
||||
make install</pre>
|
||||
<p>To list available build options:</p>
|
||||
<pre>
|
||||
cmake -LH</pre>
|
||||
<p>To compile the additional portable Shared Library / DLL version with the native C-language API:</p>
|
||||
<pre>
|
||||
cmake . -DSOUNDTOUCH_DLL=ON
|
||||
@@ -448,7 +453,7 @@
|
||||
<h2><a name="SoundStretch"></a>4. SoundStretch audio processing utility
|
||||
</h2>
|
||||
<p>SoundStretch audio processing utility<br>
|
||||
Copyright (c) Olli Parviainen 2002-2015</p>
|
||||
Copyright (c) Olli Parviainen 2002-2024</p>
|
||||
<p>SoundStretch is a simple command-line application that can change
|
||||
tempo, pitch and playback rates of WAV sound files. This program is
|
||||
intended primarily to demonstrate how the "SoundTouch" library can be
|
||||
@@ -603,6 +608,18 @@
|
||||
<hr>
|
||||
<h2>5. Change History</h2>
|
||||
<h3>5.1. SoundTouch library Change History </h3>
|
||||
<p><b>2.3.3:</b></p>
|
||||
<ul class="current">
|
||||
<li>Fixing compiler warnings, maintenance fixes to make/build files for various systems
|
||||
</li>
|
||||
</ul>
|
||||
<p><b>2.3.2:</b></p>
|
||||
<ul>
|
||||
<li>Improve autotools makefiles to build the `SoundTouchDLL` dynamic-link link library with
|
||||
C-style API. This library variation is easier to import and use from other programming
|
||||
languages than the default C++ library.
|
||||
</li>
|
||||
</ul>
|
||||
<p><b>2.3.1:</b></p>
|
||||
<ul>
|
||||
<li>Adjusted cmake build settings and header files that cmake installs</li>
|
||||
@@ -622,7 +639,7 @@
|
||||
window. This ensures that with zero tempo change the output will be same as input.
|
||||
</li>
|
||||
<li>Bugfix: Fix a bug in TDstrectch with too small initial skipFract value that occurred
|
||||
with certain processing parameter settings: Replace assert with assignment that
|
||||
with certain processing parameter settings: Replace assert with assignment that
|
||||
corrects the situation.
|
||||
</li>
|
||||
<li>Remove OpenMP "_init_threading" workaround from Android build as it's not needed with concurrent
|
||||
@@ -865,11 +882,14 @@
|
||||
<li> Initial release</li>
|
||||
</ul>
|
||||
<h3>5.2. SoundStretch application Change History </h3>
|
||||
<p><b>2.3.3:</b></p>
|
||||
<ul class="current_soundstretch">
|
||||
<li>Added support for Asian / non-latin filenames in Windows. Gnu platform has supported them already earlier.</li>
|
||||
</ul>
|
||||
<p><b>1.9:</b></p>
|
||||
<ul>
|
||||
<li>Added support for WAV file 'fact' information chunk.</li>
|
||||
</ul>
|
||||
|
||||
<p><b>1.7.0:</b></p>
|
||||
<ul>
|
||||
<li>Bugfixes in Wavfile: exception string formatting, avoid getLengthMs() integer
|
||||
@@ -966,6 +986,7 @@
|
||||
<li> Michael Pruett</li>
|
||||
<li> Rajeev Puran</li>
|
||||
<li> RJ Ryan</li>
|
||||
<li> Serge Sans Paille</li>
|
||||
<li> John Sheehy</li>
|
||||
<li> Tim Shuttleworth</li>
|
||||
<li> Albert Sirvent</li>
|
||||
|
||||
22
3rdparty/soundtouch/soundtouch/BPMDetect.h
vendored
22
3rdparty/soundtouch/soundtouch/BPMDetect.h
vendored
@@ -14,10 +14,10 @@
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -137,8 +137,8 @@ namespace soundtouch
|
||||
// 2nd order low-pass-filter
|
||||
IIR2_filter beat_lpf;
|
||||
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// Updates auto-correlation function for given number of decimated samples that
|
||||
/// are read from the internal 'buffer' pipe (samples aren't removed from the pipe
|
||||
/// though).
|
||||
void updateXCorr(int process_samples /// How many samples are processed.
|
||||
);
|
||||
@@ -175,9 +175,9 @@ namespace soundtouch
|
||||
|
||||
/// Inputs a block of samples for analyzing: Envelopes the samples and then
|
||||
/// updates the autocorrelation estimation. When whole song data has been input
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// in smaller blocks using this function, read the resulting bpm with 'getBpm'
|
||||
/// function.
|
||||
///
|
||||
/// Notice that data in 'samples' array can be disrupted in processing.
|
||||
void inputSamples(const soundtouch::SAMPLETYPE *samples, ///< Pointer to input/working data buffer
|
||||
int numSamples ///< Number of samples in buffer
|
||||
@@ -190,13 +190,13 @@ namespace soundtouch
|
||||
/// \return Beats-per-minute rate, or zero if detection failed.
|
||||
float getBpm();
|
||||
|
||||
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||
/// - "pos" receive array of beat positions
|
||||
/// - "values" receive array of beat detection strengths
|
||||
/// - max_num indicates max.size of "pos" and "values" array.
|
||||
/// - max_num indicates max.size of "pos" and "values" array.
|
||||
///
|
||||
/// You can query a suitable array sized by calling this with NULL in "pos" & "values".
|
||||
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||
///
|
||||
/// \return number of beats in the arrays.
|
||||
int getBeats(float *pos, float *strength, int max_num);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// output samples from the buffer as well as grows the storage size
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// output samples from the buffer as well as grows the storage size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -47,7 +47,7 @@ namespace soundtouch
|
||||
/// Sample buffer working in FIFO (first-in-first-out) principle. The class takes
|
||||
/// care of storage size adjustment and data moving during input/output operations.
|
||||
///
|
||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||
/// Notice that in case of stereo audio, one sample is considered to consist of
|
||||
/// both channel data.
|
||||
class FIFOSampleBuffer : public FIFOSamplePipe
|
||||
{
|
||||
@@ -68,12 +68,12 @@ private:
|
||||
/// Channels, 1=mono, 2=stereo.
|
||||
uint channels;
|
||||
|
||||
/// Current position pointer to the buffer. This pointer is increased when samples are
|
||||
/// Current position pointer to the buffer. This pointer is increased when samples are
|
||||
/// removed from the pipe so that it's necessary to actually rewind buffer (move data)
|
||||
/// only new data when is put to the pipe.
|
||||
uint bufferPos;
|
||||
|
||||
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
|
||||
/// Rewind the buffer by moving data from position pointed by 'bufferPos' to real
|
||||
/// beginning of the buffer.
|
||||
void rewind();
|
||||
|
||||
@@ -93,27 +93,27 @@ public:
|
||||
/// destructor
|
||||
~FIFOSampleBuffer() override;
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() override;
|
||||
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
/// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
/// where the new samples are to be inserted). This function may be used for
|
||||
/// inserting new samples into the sample buffer directly. Please be careful
|
||||
/// not corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function as means for inserting new samples, also remember
|
||||
/// to increase the sample count afterwards, by calling the
|
||||
/// When using this function as means for inserting new samples, also remember
|
||||
/// to increase the sample count afterwards, by calling the
|
||||
/// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *ptrEnd(
|
||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||
///< should be so that the caller can successfully insert the
|
||||
///< desired samples to the buffer. If necessary, the function
|
||||
uint slackCapacity ///< How much free capacity (in samples) there _at least_
|
||||
///< should be so that the caller can successfully insert the
|
||||
///< desired samples to the buffer. If necessary, the function
|
||||
///< grows the buffer size to comply with this requirement.
|
||||
);
|
||||
|
||||
@@ -123,17 +123,17 @@ public:
|
||||
uint numSamples ///< Number of samples to insert.
|
||||
) override;
|
||||
|
||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||
/// Adjusts the book-keeping to increase number of samples in the buffer without
|
||||
/// copying any actual samples.
|
||||
///
|
||||
/// This function is used to update the number of samples in the sample buffer
|
||||
/// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
/// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
/// careful though!
|
||||
virtual void putSamples(uint numSamples ///< Number of samples been inserted.
|
||||
);
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
@@ -141,8 +141,8 @@ public:
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
) override;
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
void setChannels(int numChannels);
|
||||
|
||||
/// Get number of channels
|
||||
int getChannels()
|
||||
int getChannels()
|
||||
{
|
||||
return channels;
|
||||
}
|
||||
|
||||
48
3rdparty/soundtouch/soundtouch/FIFOSamplePipe.h
vendored
48
3rdparty/soundtouch/soundtouch/FIFOSamplePipe.h
vendored
@@ -5,7 +5,7 @@
|
||||
/// into one end of the pipe with the 'putSamples' function, and the processed
|
||||
/// samples are received from the other end with the 'receiveSamples' function.
|
||||
///
|
||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||
/// 'FIFOProcessor' : A base class for classes the do signal processing with
|
||||
/// the samples while operating like a first-in-first-out pipe. When samples
|
||||
/// are input with the 'putSamples' function, the class processes them
|
||||
/// and moves the processed samples to the given 'output' pipe object, which
|
||||
@@ -68,12 +68,12 @@ public:
|
||||
virtual ~FIFOSamplePipe() {}
|
||||
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() = 0;
|
||||
|
||||
@@ -88,14 +88,14 @@ public:
|
||||
void moveSamples(FIFOSamplePipe &other ///< Other pipe instance where from the receive the data.
|
||||
)
|
||||
{
|
||||
int oNumSamples = other.numSamples();
|
||||
const uint oNumSamples = other.numSamples();
|
||||
|
||||
putSamples(other.ptrBegin(), oNumSamples);
|
||||
other.receiveSamples(oNumSamples);
|
||||
};
|
||||
}
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
@@ -103,8 +103,8 @@ public:
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
) = 0;
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
@@ -127,12 +127,12 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||
/// Base-class for sound processing routines working in FIFO principle. With this base
|
||||
/// class it's easy to implement sound processing stages that can be chained together,
|
||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||
/// so that samples that are fed into beginning of the pipe automatically go through
|
||||
/// all the processing stages.
|
||||
///
|
||||
/// When samples are input to this class, they're first processed and then put to
|
||||
/// When samples are input to this class, they're first processed and then put to
|
||||
/// the FIFO pipe that's defined as output of this class. This output pipe can be
|
||||
/// either other processing stage or a FIFO sample buffer.
|
||||
class FIFOProcessor :public FIFOSamplePipe
|
||||
@@ -144,16 +144,16 @@ protected:
|
||||
/// Sets output pipe.
|
||||
void setOutPipe(FIFOSamplePipe *pOutput)
|
||||
{
|
||||
assert(output == NULL);
|
||||
assert(pOutput != NULL);
|
||||
assert(output == nullptr);
|
||||
assert(pOutput != nullptr);
|
||||
output = pOutput;
|
||||
}
|
||||
|
||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||
/// Constructor. Doesn't define output pipe; it has to be set be
|
||||
/// 'setOutPipe' function.
|
||||
FIFOProcessor()
|
||||
{
|
||||
output = NULL;
|
||||
output = nullptr;
|
||||
}
|
||||
|
||||
/// Constructor. Configures output pipe.
|
||||
@@ -168,12 +168,12 @@ protected:
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Returns a pointer to the beginning of the output samples.
|
||||
/// This function is provided for accessing the output samples directly.
|
||||
/// Please be careful for not to corrupt the book-keeping!
|
||||
///
|
||||
/// When using this function to output samples, also remember to 'remove' the
|
||||
/// output samples from the buffer by calling the
|
||||
/// output samples from the buffer by calling the
|
||||
/// 'receiveSamples(numSamples)' function
|
||||
virtual SAMPLETYPE *ptrBegin() override
|
||||
{
|
||||
@@ -182,8 +182,8 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
@@ -194,8 +194,8 @@ public:
|
||||
return output->receiveSamples(outBuffer, maxSamples);
|
||||
}
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
|
||||
34
3rdparty/soundtouch/soundtouch/STTypes.h
vendored
34
3rdparty/soundtouch/soundtouch/STTypes.h
vendored
@@ -59,15 +59,15 @@ namespace soundtouch
|
||||
/// Max allowed number of channels
|
||||
#define SOUNDTOUCH_MAX_CHANNELS 16
|
||||
|
||||
/// Activate these undef's to overrule the possible sampletype
|
||||
/// Activate these undef's to overrule the possible sampletype
|
||||
/// setting inherited from some other header file:
|
||||
//#undef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
//#undef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
|
||||
/// If following flag is defined, always uses multichannel processing
|
||||
/// routines also for mono and stero sound. This is for routine testing
|
||||
/// purposes; output should be same with either routines, yet disabling
|
||||
/// the dedicated mono/stereo processing routines will result in slower
|
||||
/// If following flag is defined, always uses multichannel processing
|
||||
/// routines also for mono and stero sound. This is for routine testing
|
||||
/// purposes; output should be same with either routines, yet disabling
|
||||
/// the dedicated mono/stereo processing routines will result in slower
|
||||
/// runtime performance so recommendation is to keep this off.
|
||||
// #define USE_MULTICH_ALWAYS
|
||||
|
||||
@@ -79,31 +79,31 @@ namespace soundtouch
|
||||
#endif
|
||||
|
||||
#if !(SOUNDTOUCH_INTEGER_SAMPLES || SOUNDTOUCH_FLOAT_SAMPLES)
|
||||
|
||||
|
||||
/// Choose either 32bit floating point or 16bit integer sampletype
|
||||
/// by choosing one of the following defines, unless this selection
|
||||
/// by choosing one of the following defines, unless this selection
|
||||
/// has already been done in some other file.
|
||||
////
|
||||
/// Notes:
|
||||
/// - In Windows environment, choose the sample format with the
|
||||
/// following defines.
|
||||
/// - In GNU environment, the floating point samples are used by
|
||||
/// default, but integer samples can be chosen by giving the
|
||||
/// - In GNU environment, the floating point samples are used by
|
||||
/// default, but integer samples can be chosen by giving the
|
||||
/// following switch to the configure script:
|
||||
/// ./configure --enable-integer-samples
|
||||
/// However, if you still prefer to select the sample format here
|
||||
/// However, if you still prefer to select the sample format here
|
||||
/// also in GNU environment, then please #undef the INTEGER_SAMPLE
|
||||
/// and FLOAT_SAMPLE defines first as in comments above.
|
||||
//#define SOUNDTOUCH_INTEGER_SAMPLES 1 //< 16bit integer samples
|
||||
#define SOUNDTOUCH_FLOAT_SAMPLES 1 //< 32bit float samples
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#if (_M_IX86 || __i386__ || __x86_64__ || _M_X64)
|
||||
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
||||
/// Define this to allow X86-specific assembler/intrinsic optimizations.
|
||||
/// Notice that library contains also usual C++ versions of each of these
|
||||
/// these routines, so if you're having difficulties getting the optimized
|
||||
/// routines compiled for whatever reason, you may disable these optimizations
|
||||
/// these routines, so if you're having difficulties getting the optimized
|
||||
/// routines compiled for whatever reason, you may disable these optimizations
|
||||
/// to make the library compile.
|
||||
|
||||
#define SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS 1
|
||||
@@ -181,9 +181,9 @@ namespace soundtouch
|
||||
#define ST_THROW_RT_ERROR(x) {throw std::runtime_error(x);}
|
||||
#endif
|
||||
|
||||
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
|
||||
// parameter setting crosses from value <1 to >=1 or vice versa during processing.
|
||||
// Default is off as such crossover is untypical case and involves a slight sound
|
||||
// When this #define is active, eliminates a clicking sound when the "rate" or "pitch"
|
||||
// parameter setting crosses from value <1 to >=1 or vice versa during processing.
|
||||
// Default is off as such crossover is untypical case and involves a slight sound
|
||||
// quality compromise.
|
||||
//#define SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER 1
|
||||
|
||||
|
||||
118
3rdparty/soundtouch/soundtouch/SoundTouch.h
vendored
118
3rdparty/soundtouch/soundtouch/SoundTouch.h
vendored
@@ -1,27 +1,27 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
@@ -30,8 +30,8 @@
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
@@ -72,10 +72,10 @@ namespace soundtouch
|
||||
{
|
||||
|
||||
/// Soundtouch library version string
|
||||
#define SOUNDTOUCH_VERSION "2.3.1"
|
||||
#define SOUNDTOUCH_VERSION "2.3.3"
|
||||
|
||||
/// SoundTouch library version id
|
||||
#define SOUNDTOUCH_VERSION_ID (20301)
|
||||
#define SOUNDTOUCH_VERSION_ID (20303)
|
||||
|
||||
//
|
||||
// Available setting IDs for the 'setSetting' & 'get_setting' functions:
|
||||
@@ -91,55 +91,55 @@ namespace soundtouch
|
||||
/// quality compromising)
|
||||
#define SETTING_USE_QUICKSEEK 2
|
||||
|
||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
/// Time-stretch algorithm single processing sequence length in milliseconds. This determines
|
||||
/// to how long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEQUENCE_MS 3
|
||||
|
||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
||||
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||
/// Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the
|
||||
/// best possible overlapping location. This determines from how wide window the algorithm
|
||||
/// may look for an optimal joining location when mixing the sound sequences back together.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_SEEKWINDOW_MS 4
|
||||
|
||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
||||
/// how long period the two consecutive sequences are let to overlap each other.
|
||||
/// Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences
|
||||
/// are mixed back together, to form a continuous sound stream, this parameter defines over
|
||||
/// how long period the two consecutive sequences are let to overlap each other.
|
||||
/// See "STTypes.h" or README for more information.
|
||||
#define SETTING_OVERLAP_MS 5
|
||||
|
||||
|
||||
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
||||
/// This value gives approximate value of how many input samples you'll need to
|
||||
/// Call "getSetting" with this ID to query processing sequence size in samples.
|
||||
/// This value gives approximate value of how many input samples you'll need to
|
||||
/// feed into SoundTouch after initial buffering to get out a new batch of
|
||||
/// output samples.
|
||||
/// output samples.
|
||||
///
|
||||
/// This value does not include initial buffering at beginning of a new processing
|
||||
/// This value does not include initial buffering at beginning of a new processing
|
||||
/// stream, use SETTING_INITIAL_LATENCY to get the initial buffering size.
|
||||
///
|
||||
/// Notices:
|
||||
/// Notices:
|
||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||
/// - This parameter value is not constant but change depending on
|
||||
/// - This parameter value is not constant but change depending on
|
||||
/// tempo/pitch/rate/samplerate settings.
|
||||
#define SETTING_NOMINAL_INPUT_SEQUENCE 6
|
||||
|
||||
|
||||
/// Call "getSetting" with this ID to query nominal average processing output
|
||||
/// size in samples. This value tells approcimate value how many output samples
|
||||
/// Call "getSetting" with this ID to query nominal average processing output
|
||||
/// size in samples. This value tells approcimate value how many output samples
|
||||
/// SoundTouch outputs once it does DSP processing run for a batch of input samples.
|
||||
///
|
||||
/// Notices:
|
||||
/// Notices:
|
||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||
/// - This parameter value is not constant but change depending on
|
||||
/// - This parameter value is not constant but change depending on
|
||||
/// tempo/pitch/rate/samplerate settings.
|
||||
#define SETTING_NOMINAL_OUTPUT_SEQUENCE 7
|
||||
|
||||
|
||||
/// Call "getSetting" with this ID to query initial processing latency, i.e.
|
||||
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
||||
/// you can expect to get first batch of ready output samples out.
|
||||
/// approx. how many samples you'll need to enter to SoundTouch pipeline before
|
||||
/// you can expect to get first batch of ready output samples out.
|
||||
///
|
||||
/// After the first output batch, you can then expect to get approx.
|
||||
/// After the first output batch, you can then expect to get approx.
|
||||
/// SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every
|
||||
/// SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch.
|
||||
///
|
||||
@@ -149,18 +149,18 @@ namespace soundtouch
|
||||
/// input sequence = 4167 samples
|
||||
/// output sequence = 3969 samples
|
||||
///
|
||||
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
||||
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
||||
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
||||
/// Accordingly, you can expect to feed in approx. 5509 samples at beginning of
|
||||
/// the stream, and then you'll get out the first 3969 samples. After that, for
|
||||
/// every approx. 4167 samples that you'll put in, you'll receive again approx.
|
||||
/// 3969 samples out.
|
||||
///
|
||||
/// This also means that average latency during stream processing is
|
||||
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
||||
/// This also means that average latency during stream processing is
|
||||
/// INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2
|
||||
/// = 3524 samples
|
||||
///
|
||||
/// Notices:
|
||||
///
|
||||
/// Notices:
|
||||
/// - This is read-only parameter, i.e. setSetting ignores this parameter
|
||||
/// - This parameter value is not constant but change depending on
|
||||
/// - This parameter value is not constant but change depending on
|
||||
/// tempo/pitch/rate/samplerate settings.
|
||||
#define SETTING_INITIAL_LATENCY 8
|
||||
|
||||
@@ -193,7 +193,7 @@ private:
|
||||
/// Accumulator for how many samples in total have been read out from the processing so far
|
||||
long samplesOutput;
|
||||
|
||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||
/// Calculates effective rate & tempo valuescfrom 'virtualRate', 'virtualTempo' and
|
||||
/// 'virtualPitch' parameters.
|
||||
void calcEffectiveRateAndTempo();
|
||||
|
||||
@@ -237,7 +237,7 @@ public:
|
||||
/// represent lower pitches, larger values higher pitch.
|
||||
void setPitch(double newPitch);
|
||||
|
||||
/// Sets pitch change in octaves compared to the original pitch
|
||||
/// Sets pitch change in octaves compared to the original pitch
|
||||
/// (-1.00 .. +1.00)
|
||||
void setPitchOctaves(double newPitch);
|
||||
|
||||
@@ -253,20 +253,20 @@ public:
|
||||
void setSampleRate(uint srate);
|
||||
|
||||
/// Get ratio between input and output audio durations, useful for calculating
|
||||
/// processed output duration: if you'll process a stream of N samples, then
|
||||
/// processed output duration: if you'll process a stream of N samples, then
|
||||
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||
///
|
||||
/// This ratio will give accurate target duration ratio for a full audio track,
|
||||
/// This ratio will give accurate target duration ratio for a full audio track,
|
||||
/// given that the the whole track is processed with same processing parameters.
|
||||
///
|
||||
///
|
||||
/// If this ratio is applied to calculate intermediate offsets inside a processing
|
||||
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
||||
/// stream, then this ratio is approximate and can deviate +- some tens of milliseconds
|
||||
/// from ideal offset, yet by end of the audio stream the duration ratio will become
|
||||
/// exact.
|
||||
///
|
||||
/// Example: if processing with parameters "-tempo=15 -pitch=-3", the function
|
||||
/// will return value 0.8695652... Now, if processing an audio stream whose duration
|
||||
/// is exactly one million audio samples, then you can expect the processed
|
||||
/// is exactly one million audio samples, then you can expect the processed
|
||||
/// output duration be 0.869565 * 1000000 = 869565 samples.
|
||||
double getInputOutputSampleRatio();
|
||||
|
||||
@@ -289,8 +289,8 @@ public:
|
||||
///< contains data for both channels.
|
||||
) override;
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
@@ -298,8 +298,8 @@ public:
|
||||
uint maxSamples ///< How many samples to receive at max.
|
||||
) override;
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
@@ -312,7 +312,7 @@ public:
|
||||
|
||||
/// Changes a setting controlling the processing system behaviour. See the
|
||||
/// 'SETTING_...' defines for available setting ID's.
|
||||
///
|
||||
///
|
||||
/// \return 'true' if the setting was successfully changed
|
||||
bool setSetting(int settingId, ///< Setting ID number. see SETTING_... defines.
|
||||
int value ///< New setting value.
|
||||
@@ -338,7 +338,7 @@ public:
|
||||
/// classes 'FIFOProcessor' and 'FIFOSamplePipe')
|
||||
///
|
||||
/// - receiveSamples() : Use this function to receive 'ready' processed samples from SoundTouch.
|
||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||
/// - numSamples() : Get number of 'ready' samples that can be received with
|
||||
/// function 'receiveSamples()'
|
||||
/// - isEmpty() : Returns nonzero if there aren't any 'ready' samples.
|
||||
/// - clear() : Clears all samples from ready/processing buffers.
|
||||
|
||||
52
3rdparty/soundtouch/source/SoundStretch/SS_CharTypes.h
vendored
Normal file
52
3rdparty/soundtouch/source/SoundStretch/SS_CharTypes.h
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Char type for SoundStretch
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
/// Author e-mail : oparviai 'at' iki.fi
|
||||
/// SoundTouch WWW: http://www.surina.net/soundtouch
|
||||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// License :
|
||||
//
|
||||
// SoundTouch audio processing library
|
||||
// Copyright (c) Olli Parviainen
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef SS_CHARTYPE_H
|
||||
#define SS_CHARTYPE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace soundstretch
|
||||
{
|
||||
#if _WIN32
|
||||
// wide-char types for supporting non-latin file paths in Windows
|
||||
using CHARTYPE = wchar_t;
|
||||
using STRING = std::wstring;
|
||||
#define STRING_CONST(x) (L"" x)
|
||||
#else
|
||||
// gnu platform can natively support UTF-8 paths using "char*" set
|
||||
using CHARTYPE = char;
|
||||
using STRING = std::string;
|
||||
#define STRING_CONST(x) (x)
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif //SS_CHARTYPE_H
|
||||
576
3rdparty/soundtouch/source/SoundStretch/WavFile.cpp
vendored
576
3rdparty/soundtouch/source/SoundStretch/WavFile.cpp
vendored
@@ -1,12 +1,12 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Classes for easy reading & writing of WAV sound files.
|
||||
/// Classes for easy reading & writing of WAV sound files.
|
||||
///
|
||||
/// For big-endian CPU, define _BIG_ENDIAN_ during compile-time to correctly
|
||||
/// parse the WAV files with such processors.
|
||||
///
|
||||
///
|
||||
/// Admittingly, more complete WAV reader routines may exist in public domain,
|
||||
/// but the reason for 'yet another' one is that those generic WAV reader
|
||||
/// but the reason for 'yet another' one is that those generic WAV reader
|
||||
/// libraries are exhaustingly large and cumbersome! Wanted to have something
|
||||
/// simpler here, i.e. something that's not already larger than rest of the
|
||||
/// SoundTouch/SoundStretch program...
|
||||
@@ -42,91 +42,100 @@
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <cassert>
|
||||
#include <climits>
|
||||
|
||||
#include "WavFile.h"
|
||||
#include "STTypes.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace soundstretch
|
||||
{
|
||||
|
||||
#if _WIN32
|
||||
#define FOPEN(name, mode) _wfopen(name, STRING_CONST(mode))
|
||||
#else
|
||||
#define FOPEN(name, mode) fopen(name, mode)
|
||||
#endif
|
||||
|
||||
static const char riffStr[] = "RIFF";
|
||||
static const char waveStr[] = "WAVE";
|
||||
static const char fmtStr[] = "fmt ";
|
||||
static const char fmtStr[] = "fmt ";
|
||||
static const char factStr[] = "fact";
|
||||
static const char dataStr[] = "data";
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Helper functions for swapping byte order to correctly read/write WAV files
|
||||
// Helper functions for swapping byte order to correctly read/write WAV files
|
||||
// with big-endian CPU's: Define compile-time definition _BIG_ENDIAN_ to
|
||||
// turn-on the conversion if it appears necessary.
|
||||
// turn-on the conversion if it appears necessary.
|
||||
//
|
||||
// For example, Intel x86 is little-endian and doesn't require conversion,
|
||||
// while PowerPC of Mac's and many other RISC cpu's are big-endian.
|
||||
|
||||
#ifdef BYTE_ORDER
|
||||
// In gcc compiler detect the byte order automatically
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
// big-endian platform.
|
||||
#define _BIG_ENDIAN_
|
||||
#endif
|
||||
// In gcc compiler detect the byte order automatically
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
// big-endian platform.
|
||||
#define _BIG_ENDIAN_
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _BIG_ENDIAN_
|
||||
// big-endian CPU, swap bytes in 16 & 32 bit words
|
||||
// big-endian CPU, swap bytes in 16 & 32 bit words
|
||||
|
||||
// helper-function to swap byte-order of 32bit integer
|
||||
static inline int _swap32(int &dwData)
|
||||
{
|
||||
dwData = ((dwData >> 24) & 0x000000FF) |
|
||||
((dwData >> 8) & 0x0000FF00) |
|
||||
((dwData << 8) & 0x00FF0000) |
|
||||
((dwData << 24) & 0xFF000000);
|
||||
return dwData;
|
||||
}
|
||||
// helper-function to swap byte-order of 32bit integer
|
||||
static inline int _swap32(int& dwData)
|
||||
{
|
||||
dwData = ((dwData >> 24) & 0x000000FF) |
|
||||
((dwData >> 8) & 0x0000FF00) |
|
||||
((dwData << 8) & 0x00FF0000) |
|
||||
((dwData << 24) & 0xFF000000);
|
||||
return dwData;
|
||||
}
|
||||
|
||||
// helper-function to swap byte-order of 16bit integer
|
||||
static inline short _swap16(short &wData)
|
||||
// helper-function to swap byte-order of 16bit integer
|
||||
static inline short _swap16(short& wData)
|
||||
{
|
||||
wData = ((wData >> 8) & 0x00FF) |
|
||||
((wData << 8) & 0xFF00);
|
||||
return wData;
|
||||
}
|
||||
|
||||
// helper-function to swap byte-order of buffer of 16bit integers
|
||||
static inline void _swap16Buffer(short* pData, int numWords)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numWords; i++)
|
||||
{
|
||||
wData = ((wData >> 8) & 0x00FF) |
|
||||
((wData << 8) & 0xFF00);
|
||||
return wData;
|
||||
}
|
||||
|
||||
// helper-function to swap byte-order of buffer of 16bit integers
|
||||
static inline void _swap16Buffer(short *pData, int numWords)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numWords; i ++)
|
||||
{
|
||||
pData[i] = _swap16(pData[i]);
|
||||
}
|
||||
pData[i] = _swap16(pData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#else // BIG_ENDIAN
|
||||
// little-endian CPU, WAV file is ok as such
|
||||
// little-endian CPU, WAV file is ok as such
|
||||
|
||||
// dummy helper-function
|
||||
static inline int _swap32(int &dwData)
|
||||
{
|
||||
// do nothing
|
||||
return dwData;
|
||||
}
|
||||
// dummy helper-function
|
||||
static inline int _swap32(int& dwData)
|
||||
{
|
||||
// do nothing
|
||||
return dwData;
|
||||
}
|
||||
|
||||
// dummy helper-function
|
||||
static inline short _swap16(short &wData)
|
||||
{
|
||||
// do nothing
|
||||
return wData;
|
||||
}
|
||||
// dummy helper-function
|
||||
static inline short _swap16(short& wData)
|
||||
{
|
||||
// do nothing
|
||||
return wData;
|
||||
}
|
||||
|
||||
// dummy helper-function
|
||||
static inline void _swap16Buffer(short *pData, int numBytes)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
// dummy helper-function
|
||||
static inline void _swap16Buffer(short*, int)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
#endif // BIG_ENDIAN
|
||||
|
||||
@@ -138,7 +147,7 @@ static const char dataStr[] = "data";
|
||||
|
||||
WavFileBase::WavFileBase()
|
||||
{
|
||||
convBuff = NULL;
|
||||
convBuff = nullptr;
|
||||
convBuffSize = 0;
|
||||
}
|
||||
|
||||
@@ -151,7 +160,7 @@ WavFileBase::~WavFileBase()
|
||||
|
||||
|
||||
/// Get pointer to conversion buffer of at min. given size
|
||||
void *WavFileBase::getConvBuffer(int sizeBytes)
|
||||
void* WavFileBase::getConvBuffer(int sizeBytes)
|
||||
{
|
||||
if (convBuffSize < sizeBytes)
|
||||
{
|
||||
@@ -169,32 +178,26 @@ void *WavFileBase::getConvBuffer(int sizeBytes)
|
||||
// Class WavInFile
|
||||
//
|
||||
|
||||
WavInFile::WavInFile(const char *fileName)
|
||||
WavInFile::WavInFile(const STRING& fileName)
|
||||
{
|
||||
// Try to open the file for reading
|
||||
fptr = fopen(fileName, "rb");
|
||||
if (fptr == NULL)
|
||||
fptr = FOPEN(fileName.c_str(), "rb");
|
||||
if (fptr == nullptr)
|
||||
{
|
||||
// didn't succeed
|
||||
string msg = "Error : Unable to open file \"";
|
||||
msg += fileName;
|
||||
msg += "\" for reading.";
|
||||
ST_THROW_RT_ERROR(msg.c_str());
|
||||
ST_THROW_RT_ERROR("Error : Unable to open file for reading.");
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
WavInFile::WavInFile(FILE *file)
|
||||
WavInFile::WavInFile(FILE* file)
|
||||
{
|
||||
// Try to open the file for reading
|
||||
fptr = file;
|
||||
if (!file)
|
||||
if (!file)
|
||||
{
|
||||
// didn't succeed
|
||||
string msg = "Error : Unable to access input stream for reading";
|
||||
ST_THROW_RT_ERROR(msg.c_str());
|
||||
ST_THROW_RT_ERROR("Error : Unable to access input stream for reading");
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -211,19 +214,17 @@ void WavInFile::init()
|
||||
|
||||
// Read the file headers
|
||||
hdrsOk = readWavHeaders();
|
||||
if (hdrsOk != 0)
|
||||
if (hdrsOk != 0)
|
||||
{
|
||||
// Something didn't match in the wav file headers
|
||||
ST_THROW_RT_ERROR("Input file is corrupt or not a WAV file");
|
||||
}
|
||||
|
||||
// sanity check for format parameters
|
||||
if ((header.format.channel_number < 1) || (header.format.channel_number > 9) ||
|
||||
(header.format.sample_rate < 4000) || (header.format.sample_rate > 192000) ||
|
||||
if ((header.format.channel_number < 1) || (header.format.channel_number > 9) ||
|
||||
(header.format.sample_rate < 4000) || (header.format.sample_rate > 192000) ||
|
||||
(header.format.byte_per_sample < 1) || (header.format.byte_per_sample > 320) ||
|
||||
(header.format.bits_per_sample < 8) || (header.format.bits_per_sample > 32))
|
||||
{
|
||||
// Something didn't match in the wav file headers
|
||||
ST_THROW_RT_ERROR("Error: Illegal wav file header format parameters.");
|
||||
}
|
||||
|
||||
@@ -234,7 +235,7 @@ void WavInFile::init()
|
||||
WavInFile::~WavInFile()
|
||||
{
|
||||
if (fptr) fclose(fptr);
|
||||
fptr = NULL;
|
||||
fptr = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -260,7 +261,7 @@ int WavInFile::checkCharTags() const
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::read(unsigned char *buffer, int maxElems)
|
||||
int WavInFile::read(unsigned char* buffer, int maxElems)
|
||||
{
|
||||
int numBytes;
|
||||
uint afterDataRead;
|
||||
@@ -274,7 +275,7 @@ int WavInFile::read(unsigned char *buffer, int maxElems)
|
||||
|
||||
numBytes = maxElems;
|
||||
afterDataRead = dataRead + numBytes;
|
||||
if (afterDataRead > header.data.data_len)
|
||||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||
@@ -289,7 +290,7 @@ int WavInFile::read(unsigned char *buffer, int maxElems)
|
||||
}
|
||||
|
||||
|
||||
int WavInFile::read(short *buffer, int maxElems)
|
||||
int WavInFile::read(short* buffer, int maxElems)
|
||||
{
|
||||
unsigned int afterDataRead;
|
||||
int numBytes;
|
||||
@@ -298,62 +299,62 @@ int WavInFile::read(short *buffer, int maxElems)
|
||||
assert(buffer);
|
||||
switch (header.format.bits_per_sample)
|
||||
{
|
||||
case 8:
|
||||
{
|
||||
// 8 bit format
|
||||
unsigned char *temp = (unsigned char*)getConvBuffer(maxElems);
|
||||
int i;
|
||||
case 8:
|
||||
{
|
||||
// 8 bit format
|
||||
unsigned char* temp = (unsigned char*)getConvBuffer(maxElems);
|
||||
int i;
|
||||
|
||||
numElems = read(temp, maxElems);
|
||||
// convert from 8 to 16 bit
|
||||
for (i = 0; i < numElems; i ++)
|
||||
{
|
||||
buffer[i] = (short)(((short)temp[i] - 128) * 256);
|
||||
}
|
||||
break;
|
||||
numElems = read(temp, maxElems);
|
||||
// convert from 8 to 16 bit
|
||||
for (i = 0; i < numElems; i++)
|
||||
{
|
||||
buffer[i] = (short)(((short)temp[i] - 128) * 256);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 16:
|
||||
{
|
||||
// 16 bit format
|
||||
|
||||
assert(sizeof(short) == 2);
|
||||
|
||||
numBytes = maxElems * 2;
|
||||
afterDataRead = dataRead + numBytes;
|
||||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||
assert(numBytes >= 0);
|
||||
}
|
||||
|
||||
case 16:
|
||||
{
|
||||
// 16 bit format
|
||||
numBytes = (int)fread(buffer, 1, numBytes, fptr);
|
||||
dataRead += numBytes;
|
||||
numElems = numBytes / 2;
|
||||
|
||||
assert(sizeof(short) == 2);
|
||||
// 16bit samples, swap byte order if necessary
|
||||
_swap16Buffer((short*)buffer, numElems);
|
||||
break;
|
||||
}
|
||||
|
||||
numBytes = maxElems * 2;
|
||||
afterDataRead = dataRead + numBytes;
|
||||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||
assert(numBytes >= 0);
|
||||
}
|
||||
|
||||
numBytes = (int)fread(buffer, 1, numBytes, fptr);
|
||||
dataRead += numBytes;
|
||||
numElems = numBytes / 2;
|
||||
|
||||
// 16bit samples, swap byte order if necessary
|
||||
_swap16Buffer((short *)buffer, numElems);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
||||
ss << (int)header.format.bits_per_sample;
|
||||
ss << " bit sample format. ";
|
||||
ST_THROW_RT_ERROR(ss.str().c_str());
|
||||
}
|
||||
default:
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
||||
ss << (int)header.format.bits_per_sample;
|
||||
ss << " bit sample format. ";
|
||||
ST_THROW_RT_ERROR(ss.str().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
return numElems;
|
||||
}
|
||||
|
||||
|
||||
/// Read data in float format. Notice that when reading in float format
|
||||
/// Read data in float format. Notice that when reading in float format
|
||||
/// 8/16/24/32 bit sample formats are supported
|
||||
int WavInFile::read(float *buffer, int maxElems)
|
||||
int WavInFile::read(float* buffer, int maxElems)
|
||||
{
|
||||
unsigned int afterDataRead;
|
||||
int numBytes;
|
||||
@@ -374,7 +375,7 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||
|
||||
numBytes = maxElems * bytesPerSample;
|
||||
afterDataRead = dataRead + numBytes;
|
||||
if (afterDataRead > header.data.data_len)
|
||||
if (afterDataRead > header.data.data_len)
|
||||
{
|
||||
// Don't read more samples than are marked available in header
|
||||
numBytes = (int)header.data.data_len - (int)dataRead;
|
||||
@@ -382,7 +383,7 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||
}
|
||||
|
||||
// read raw data into temporary buffer
|
||||
char *temp = (char*)getConvBuffer(numBytes);
|
||||
char* temp = (char*)getConvBuffer(numBytes);
|
||||
numBytes = (int)fread(temp, 1, numBytes, fptr);
|
||||
dataRead += numBytes;
|
||||
|
||||
@@ -391,56 +392,56 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||
// swap byte ordert & convert to float, depending on sample format
|
||||
switch (bytesPerSample)
|
||||
{
|
||||
case 1:
|
||||
case 1:
|
||||
{
|
||||
unsigned char* temp2 = (unsigned char*)temp;
|
||||
double conv = 1.0 / 128.0;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
unsigned char *temp2 = (unsigned char*)temp;
|
||||
double conv = 1.0 / 128.0;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
buffer[i] = (float)(temp2[i] * conv - 1.0);
|
||||
}
|
||||
break;
|
||||
buffer[i] = (float)(temp2[i] * conv - 1.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 2:
|
||||
{
|
||||
short* temp2 = (short*)temp;
|
||||
double conv = 1.0 / 32768.0;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
short *temp2 = (short*)temp;
|
||||
double conv = 1.0 / 32768.0;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
short value = temp2[i];
|
||||
buffer[i] = (float)(_swap16(value) * conv);
|
||||
}
|
||||
break;
|
||||
short value = temp2[i];
|
||||
buffer[i] = (float)(_swap16(value) * conv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
case 3:
|
||||
{
|
||||
char* temp2 = (char*)temp;
|
||||
double conv = 1.0 / 8388608.0;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
char *temp2 = (char *)temp;
|
||||
double conv = 1.0 / 8388608.0;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
int value = *((int*)temp2);
|
||||
value = _swap32(value) & 0x00ffffff; // take 24 bits
|
||||
value |= (value & 0x00800000) ? 0xff000000 : 0; // extend minus sign bits
|
||||
buffer[i] = (float)(value * conv);
|
||||
temp2 += 3;
|
||||
}
|
||||
break;
|
||||
int value = *((int*)temp2);
|
||||
value = _swap32(value) & 0x00ffffff; // take 24 bits
|
||||
value |= (value & 0x00800000) ? 0xff000000 : 0; // extend minus sign bits
|
||||
buffer[i] = (float)(value * conv);
|
||||
temp2 += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
case 4:
|
||||
{
|
||||
int* temp2 = (int*)temp;
|
||||
double conv = 1.0 / 2147483648.0;
|
||||
assert(sizeof(int) == 4);
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
int *temp2 = (int *)temp;
|
||||
double conv = 1.0 / 2147483648.0;
|
||||
assert(sizeof(int) == 4);
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
int value = temp2[i];
|
||||
buffer[i] = (float)(_swap32(value) * conv);
|
||||
}
|
||||
break;
|
||||
int value = temp2[i];
|
||||
buffer[i] = (float)(_swap32(value) * conv);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return numElems;
|
||||
@@ -450,7 +451,7 @@ int WavInFile::read(float *buffer, int maxElems)
|
||||
int WavInFile::eof() const
|
||||
{
|
||||
// return true if all data has been read or file eof has reached
|
||||
return (dataRead == header.data.data_len || feof(fptr));
|
||||
return ((uint)dataRead == header.data.data_len || feof(fptr));
|
||||
}
|
||||
|
||||
|
||||
@@ -462,15 +463,15 @@ static int isAlpha(char c)
|
||||
|
||||
|
||||
// test if all characters are between a white space ' ' and little 'z'
|
||||
static int isAlphaStr(const char *str)
|
||||
static int isAlphaStr(const char* str)
|
||||
{
|
||||
char c;
|
||||
|
||||
c = str[0];
|
||||
while (c)
|
||||
while (c)
|
||||
{
|
||||
if (isAlpha(c) == 0) return 0;
|
||||
str ++;
|
||||
str++;
|
||||
c = str[0];
|
||||
}
|
||||
|
||||
@@ -483,7 +484,7 @@ int WavInFile::readRIFFBlock()
|
||||
if (fread(&(header.riff), sizeof(WavRiff), 1, fptr) != 1) return -1;
|
||||
|
||||
// swap 32bit data byte order if necessary
|
||||
_swap32((int &)header.riff.package_len);
|
||||
_swap32((int&)header.riff.package_len);
|
||||
|
||||
// header.riff.riff_char should equal to 'RIFF');
|
||||
if (memcmp(riffStr, header.riff.riff_char, 4) != 0) return -1;
|
||||
@@ -500,7 +501,7 @@ int WavInFile::readHeaderBlock()
|
||||
string sLabel;
|
||||
|
||||
// lead label string
|
||||
if (fread(label, 1, 4, fptr) !=4) return -1;
|
||||
if (fread(label, 1, 4, fptr) != 4) return -1;
|
||||
label[4] = 0;
|
||||
|
||||
if (isAlphaStr(label) == 0) return -1; // not a valid label
|
||||
@@ -510,7 +511,7 @@ int WavInFile::readHeaderBlock()
|
||||
{
|
||||
int nLen, nDump;
|
||||
|
||||
// 'fmt ' block
|
||||
// 'fmt ' block
|
||||
memcpy(header.format.fmt, fmtStr, 4);
|
||||
|
||||
// read length of the format field
|
||||
@@ -518,7 +519,7 @@ int WavInFile::readHeaderBlock()
|
||||
// swap byte order if necessary
|
||||
_swap32(nLen);
|
||||
|
||||
// calculate how much length differs from expected
|
||||
// calculate how much length differs from expected
|
||||
nDump = nLen - ((int)sizeof(header.format) - 8);
|
||||
|
||||
// verify that header length isn't smaller than expected structure
|
||||
@@ -536,12 +537,12 @@ int WavInFile::readHeaderBlock()
|
||||
if (fread(&(header.format.fixed), nLen, 1, fptr) != 1) return -1;
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap16((short &)header.format.fixed); // short int fixed;
|
||||
_swap16((short &)header.format.channel_number); // short int channel_number;
|
||||
_swap32((int &)header.format.sample_rate); // int sample_rate;
|
||||
_swap32((int &)header.format.byte_rate); // int byte_rate;
|
||||
_swap16((short &)header.format.byte_per_sample); // short int byte_per_sample;
|
||||
_swap16((short &)header.format.bits_per_sample); // short int bits_per_sample;
|
||||
_swap16((short&)header.format.fixed); // short int fixed;
|
||||
_swap16((short&)header.format.channel_number); // short int channel_number;
|
||||
_swap32((int&)header.format.sample_rate); // int sample_rate;
|
||||
_swap32((int&)header.format.byte_rate); // int byte_rate;
|
||||
_swap16((short&)header.format.byte_per_sample); // short int byte_per_sample;
|
||||
_swap16((short&)header.format.bits_per_sample); // short int bits_per_sample;
|
||||
|
||||
// if format_len is larger than expected, skip the extra data
|
||||
if (nDump > 0)
|
||||
@@ -555,7 +556,7 @@ int WavInFile::readHeaderBlock()
|
||||
{
|
||||
int nLen, nDump;
|
||||
|
||||
// 'fact' block
|
||||
// 'fact' block
|
||||
memcpy(header.fact.fact_field, factStr, 4);
|
||||
|
||||
// read length of the fact field
|
||||
@@ -581,7 +582,7 @@ int WavInFile::readHeaderBlock()
|
||||
if (fread(&(header.fact.fact_sample_len), nLen, 1, fptr) != 1) return -1;
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap32((int &)header.fact.fact_sample_len); // int sample_length;
|
||||
_swap32((int&)header.fact.fact_sample_len); // int sample_length;
|
||||
|
||||
// if fact_len is larger than expected, skip the extra data
|
||||
if (nDump > 0)
|
||||
@@ -598,7 +599,7 @@ int WavInFile::readHeaderBlock()
|
||||
if (fread(&(header.data.data_len), sizeof(uint), 1, fptr) != 1) return -1;
|
||||
|
||||
// swap byte order if necessary
|
||||
_swap32((int &)header.data.data_len);
|
||||
_swap32((int&)header.data.data_len);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -611,7 +612,7 @@ int WavInFile::readHeaderBlock()
|
||||
// read length
|
||||
if (fread(&len, sizeof(len), 1, fptr) != 1) return -1;
|
||||
// scan through the block
|
||||
for (i = 0; i < len; i ++)
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (fread(&temp, 1, 1, fptr) != 1) return -1;
|
||||
if (feof(fptr)) return -1; // unexpected eof
|
||||
@@ -703,17 +704,13 @@ uint WavInFile::getElapsedMS() const
|
||||
// Class WavOutFile
|
||||
//
|
||||
|
||||
WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int channels)
|
||||
WavOutFile::WavOutFile(const STRING& fileName, int sampleRate, int bits, int channels)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
fptr = fopen(fileName, "wb");
|
||||
if (fptr == NULL)
|
||||
fptr = FOPEN(fileName.c_str(), "wb");
|
||||
if (fptr == nullptr)
|
||||
{
|
||||
string msg = "Error : Unable to open file \"";
|
||||
msg += fileName;
|
||||
msg += "\" for writing.";
|
||||
//pmsg = msg.c_str;
|
||||
ST_THROW_RT_ERROR(msg.c_str());
|
||||
ST_THROW_RT_ERROR("Error : Unable to open file for writing.");
|
||||
}
|
||||
|
||||
fillInHeader(sampleRate, bits, channels);
|
||||
@@ -721,14 +718,13 @@ WavOutFile::WavOutFile(const char *fileName, int sampleRate, int bits, int chann
|
||||
}
|
||||
|
||||
|
||||
WavOutFile::WavOutFile(FILE *file, int sampleRate, int bits, int channels)
|
||||
WavOutFile::WavOutFile(FILE* file, int sampleRate, int bits, int channels)
|
||||
{
|
||||
bytesWritten = 0;
|
||||
fptr = file;
|
||||
if (fptr == NULL)
|
||||
if (fptr == nullptr)
|
||||
{
|
||||
string msg = "Error : Unable to access output file stream.";
|
||||
ST_THROW_RT_ERROR(msg.c_str());
|
||||
ST_THROW_RT_ERROR("Error : Unable to access output file stream.");
|
||||
}
|
||||
|
||||
fillInHeader(sampleRate, bits, channels);
|
||||
@@ -740,7 +736,7 @@ WavOutFile::~WavOutFile()
|
||||
{
|
||||
finishHeader();
|
||||
if (fptr) fclose(fptr);
|
||||
fptr = NULL;
|
||||
fptr = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -788,8 +784,8 @@ void WavOutFile::finishHeader()
|
||||
// supplement the file length into the header structure
|
||||
header.riff.package_len = bytesWritten + sizeof(WavHeader) - sizeof(WavRiff) + 4;
|
||||
header.data.data_len = bytesWritten;
|
||||
header.fact.fact_sample_len = bytesWritten / header.format.byte_per_sample;
|
||||
|
||||
header.fact.fact_sample_len = bytesWritten / header.format.byte_per_sample;
|
||||
|
||||
writeHeader();
|
||||
}
|
||||
|
||||
@@ -801,18 +797,18 @@ void WavOutFile::writeHeader()
|
||||
|
||||
// swap byte order if necessary
|
||||
hdrTemp = header;
|
||||
_swap32((int &)hdrTemp.riff.package_len);
|
||||
_swap32((int &)hdrTemp.format.format_len);
|
||||
_swap16((short &)hdrTemp.format.fixed);
|
||||
_swap16((short &)hdrTemp.format.channel_number);
|
||||
_swap32((int &)hdrTemp.format.sample_rate);
|
||||
_swap32((int &)hdrTemp.format.byte_rate);
|
||||
_swap16((short &)hdrTemp.format.byte_per_sample);
|
||||
_swap16((short &)hdrTemp.format.bits_per_sample);
|
||||
_swap32((int &)hdrTemp.data.data_len);
|
||||
_swap32((int &)hdrTemp.fact.fact_len);
|
||||
_swap32((int &)hdrTemp.fact.fact_sample_len);
|
||||
|
||||
_swap32((int&)hdrTemp.riff.package_len);
|
||||
_swap32((int&)hdrTemp.format.format_len);
|
||||
_swap16((short&)hdrTemp.format.fixed);
|
||||
_swap16((short&)hdrTemp.format.channel_number);
|
||||
_swap32((int&)hdrTemp.format.sample_rate);
|
||||
_swap32((int&)hdrTemp.format.byte_rate);
|
||||
_swap16((short&)hdrTemp.format.byte_per_sample);
|
||||
_swap16((short&)hdrTemp.format.bits_per_sample);
|
||||
_swap32((int&)hdrTemp.data.data_len);
|
||||
_swap32((int&)hdrTemp.fact.fact_len);
|
||||
_swap32((int&)hdrTemp.fact.fact_sample_len);
|
||||
|
||||
// write the supplemented header in the beginning of the file
|
||||
fseek(fptr, 0, SEEK_SET);
|
||||
res = (int)fwrite(&hdrTemp, sizeof(hdrTemp), 1, fptr);
|
||||
@@ -826,7 +822,7 @@ void WavOutFile::writeHeader()
|
||||
}
|
||||
|
||||
|
||||
void WavOutFile::write(const unsigned char *buffer, int numElems)
|
||||
void WavOutFile::write(const unsigned char* buffer, int numElems)
|
||||
{
|
||||
int res;
|
||||
|
||||
@@ -837,7 +833,7 @@ void WavOutFile::write(const unsigned char *buffer, int numElems)
|
||||
assert(sizeof(char) == 1);
|
||||
|
||||
res = (int)fwrite(buffer, 1, numElems, fptr);
|
||||
if (res != numElems)
|
||||
if (res != numElems)
|
||||
{
|
||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||
}
|
||||
@@ -846,7 +842,7 @@ void WavOutFile::write(const unsigned char *buffer, int numElems)
|
||||
}
|
||||
|
||||
|
||||
void WavOutFile::write(const short *buffer, int numElems)
|
||||
void WavOutFile::write(const short* buffer, int numElems)
|
||||
{
|
||||
int res;
|
||||
|
||||
@@ -855,47 +851,47 @@ void WavOutFile::write(const short *buffer, int numElems)
|
||||
|
||||
switch (header.format.bits_per_sample)
|
||||
{
|
||||
case 8:
|
||||
case 8:
|
||||
{
|
||||
int i;
|
||||
unsigned char* temp = (unsigned char*)getConvBuffer(numElems);
|
||||
// convert from 16bit format to 8bit format
|
||||
for (i = 0; i < numElems; i++)
|
||||
{
|
||||
int i;
|
||||
unsigned char *temp = (unsigned char *)getConvBuffer(numElems);
|
||||
// convert from 16bit format to 8bit format
|
||||
for (i = 0; i < numElems; i ++)
|
||||
{
|
||||
temp[i] = (unsigned char)(buffer[i] / 256 + 128);
|
||||
}
|
||||
// write in 8bit format
|
||||
write(temp, numElems);
|
||||
break;
|
||||
temp[i] = (unsigned char)(buffer[i] / 256 + 128);
|
||||
}
|
||||
// write in 8bit format
|
||||
write(temp, numElems);
|
||||
break;
|
||||
}
|
||||
|
||||
case 16:
|
||||
case 16:
|
||||
{
|
||||
// 16bit format
|
||||
|
||||
// use temp buffer to swap byte order if necessary
|
||||
short* pTemp = (short*)getConvBuffer(numElems * sizeof(short));
|
||||
memcpy(pTemp, buffer, (size_t)numElems * 2L);
|
||||
_swap16Buffer(pTemp, numElems);
|
||||
|
||||
res = (int)fwrite(pTemp, 2, numElems, fptr);
|
||||
|
||||
if (res != numElems)
|
||||
{
|
||||
// 16bit format
|
||||
|
||||
// use temp buffer to swap byte order if necessary
|
||||
short *pTemp = (short *)getConvBuffer(numElems * sizeof(short));
|
||||
memcpy(pTemp, buffer, numElems * 2);
|
||||
_swap16Buffer(pTemp, numElems);
|
||||
|
||||
res = (int)fwrite(pTemp, 2, numElems, fptr);
|
||||
|
||||
if (res != numElems)
|
||||
{
|
||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||
}
|
||||
bytesWritten += 2 * numElems;
|
||||
break;
|
||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||
}
|
||||
bytesWritten += 2 * numElems;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
||||
ss << (int)header.format.bits_per_sample;
|
||||
ss << " bit sample format. ";
|
||||
ST_THROW_RT_ERROR(ss.str().c_str());
|
||||
}
|
||||
default:
|
||||
{
|
||||
stringstream ss;
|
||||
ss << "\nOnly 8/16 bit sample WAV files supported in integer compilation. Can't open WAV file with ";
|
||||
ss << (int)header.format.bits_per_sample;
|
||||
ss << " bit sample format. ";
|
||||
ST_THROW_RT_ERROR(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,10 +899,10 @@ void WavOutFile::write(const short *buffer, int numElems)
|
||||
/// Convert from float to integer and saturate
|
||||
inline int saturate(float fvalue, float minval, float maxval)
|
||||
{
|
||||
if (fvalue > maxval)
|
||||
if (fvalue > maxval)
|
||||
{
|
||||
fvalue = maxval;
|
||||
}
|
||||
}
|
||||
else if (fvalue < minval)
|
||||
{
|
||||
fvalue = minval;
|
||||
@@ -915,7 +911,7 @@ inline int saturate(float fvalue, float minval, float maxval)
|
||||
}
|
||||
|
||||
|
||||
void WavOutFile::write(const float *buffer, int numElems)
|
||||
void WavOutFile::write(const float* buffer, int numElems)
|
||||
{
|
||||
int numBytes;
|
||||
int bytesPerSample;
|
||||
@@ -924,63 +920,65 @@ void WavOutFile::write(const float *buffer, int numElems)
|
||||
|
||||
bytesPerSample = header.format.bits_per_sample / 8;
|
||||
numBytes = numElems * bytesPerSample;
|
||||
void *temp = getConvBuffer(numBytes + 7); // round bit up to avoid buffer overrun with 24bit-value assignment
|
||||
void* temp = getConvBuffer(numBytes + 7); // round bit up to avoid buffer overrun with 24bit-value assignment
|
||||
|
||||
switch (bytesPerSample)
|
||||
{
|
||||
case 1:
|
||||
case 1:
|
||||
{
|
||||
unsigned char* temp2 = (unsigned char*)temp;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
unsigned char *temp2 = (unsigned char *)temp;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
temp2[i] = (unsigned char)saturate(buffer[i] * 128.0f + 128.0f, 0.0f, 255.0f);
|
||||
}
|
||||
break;
|
||||
temp2[i] = (unsigned char)saturate(buffer[i] * 128.0f + 128.0f, 0.0f, 255.0f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 2:
|
||||
{
|
||||
short* temp2 = (short*)temp;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
short *temp2 = (short *)temp;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
short value = (short)saturate(buffer[i] * 32768.0f, -32768.0f, 32767.0f);
|
||||
temp2[i] = _swap16(value);
|
||||
}
|
||||
break;
|
||||
short value = (short)saturate(buffer[i] * 32768.0f, -32768.0f, 32767.0f);
|
||||
temp2[i] = _swap16(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
case 3:
|
||||
{
|
||||
char* temp2 = (char*)temp;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
char *temp2 = (char *)temp;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
int value = saturate(buffer[i] * 8388608.0f, -8388608.0f, 8388607.0f);
|
||||
*((int*)temp2) = _swap32(value);
|
||||
temp2 += 3;
|
||||
}
|
||||
break;
|
||||
int value = saturate(buffer[i] * 8388608.0f, -8388608.0f, 8388607.0f);
|
||||
*((int*)temp2) = _swap32(value);
|
||||
temp2 += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
case 4:
|
||||
{
|
||||
int* temp2 = (int*)temp;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
int *temp2 = (int *)temp;
|
||||
for (int i = 0; i < numElems; i ++)
|
||||
{
|
||||
int value = saturate(buffer[i] * 2147483648.0f, -2147483648.0f, 2147483647.0f);
|
||||
temp2[i] = _swap32(value);
|
||||
}
|
||||
break;
|
||||
int value = saturate(buffer[i] * 2147483648.0f, -2147483648.0f, 2147483647.0f);
|
||||
temp2[i] = _swap32(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
int res = (int)fwrite(temp, 1, numBytes, fptr);
|
||||
|
||||
if (res != numBytes)
|
||||
if (res != numBytes)
|
||||
{
|
||||
ST_THROW_RT_ERROR("Error while writing to a wav file.");
|
||||
}
|
||||
bytesWritten += numBytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
///
|
||||
/// For big-endian CPU, define BIG_ENDIAN during compile-time to correctly
|
||||
/// parse the WAV files with such processors.
|
||||
///
|
||||
/// Admittingly, more complete WAV reader routines may exist in public domain, but
|
||||
///
|
||||
/// Admittingly, more complete WAV reader routines may exist in public domain, but
|
||||
/// the reason for 'yet another' one is that those generic WAV reader libraries are
|
||||
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
|
||||
/// exhaustingly large and cumbersome! Wanted to have something simpler here, i.e.
|
||||
/// something that's not already larger than rest of the SoundTouch/SoundStretch program...
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -40,15 +40,20 @@
|
||||
#ifndef WAVFILE_H
|
||||
#define WAVFILE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include "SS_CharTypes.h"
|
||||
|
||||
namespace soundstretch
|
||||
{
|
||||
|
||||
#ifndef uint
|
||||
typedef unsigned int uint;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/// WAV audio file 'riff' section header
|
||||
typedef struct
|
||||
typedef struct
|
||||
{
|
||||
char riff_char[4];
|
||||
uint package_len;
|
||||
@@ -56,7 +61,7 @@ typedef struct
|
||||
} WavRiff;
|
||||
|
||||
/// WAV audio file 'format' section header
|
||||
typedef struct
|
||||
typedef struct
|
||||
{
|
||||
char fmt[4];
|
||||
unsigned int format_len;
|
||||
@@ -69,7 +74,7 @@ typedef struct
|
||||
} WavFormat;
|
||||
|
||||
/// WAV audio file 'fact' section header
|
||||
typedef struct
|
||||
typedef struct
|
||||
{
|
||||
char fact_field[4];
|
||||
uint fact_len;
|
||||
@@ -77,7 +82,7 @@ typedef struct
|
||||
} WavFact;
|
||||
|
||||
/// WAV audio file 'data' section header
|
||||
typedef struct
|
||||
typedef struct
|
||||
{
|
||||
char data_field[4];
|
||||
uint data_len;
|
||||
@@ -85,7 +90,7 @@ typedef struct
|
||||
|
||||
|
||||
/// WAV audio file header
|
||||
typedef struct
|
||||
typedef struct
|
||||
{
|
||||
WavRiff riff;
|
||||
WavFormat format;
|
||||
@@ -118,9 +123,6 @@ private:
|
||||
/// File pointer.
|
||||
FILE *fptr;
|
||||
|
||||
/// Position within the audio stream
|
||||
long position;
|
||||
|
||||
/// Counter of how many bytes of sample data have been read from the file.
|
||||
long dataRead;
|
||||
|
||||
@@ -148,7 +150,7 @@ private:
|
||||
public:
|
||||
/// Constructor: Opens the given WAV file. If the file can't be opened,
|
||||
/// throws 'runtime_error' exception.
|
||||
WavInFile(const char *filename);
|
||||
WavInFile(const STRING& filename);
|
||||
|
||||
WavInFile(FILE *file);
|
||||
|
||||
@@ -164,7 +166,7 @@ public:
|
||||
/// Get number of bits per sample, i.e. 8 or 16.
|
||||
uint getNumBits() const;
|
||||
|
||||
/// Get sample data size in bytes. Ahem, this should return same information as
|
||||
/// Get sample data size in bytes. Ahem, this should return same information as
|
||||
/// 'getBytesPerSample'...
|
||||
uint getDataSizeInBytes() const;
|
||||
|
||||
@@ -173,7 +175,7 @@ public:
|
||||
|
||||
/// Get number of bytes per audio sample (e.g. 16bit stereo = 4 bytes/sample)
|
||||
uint getBytesPerSample() const;
|
||||
|
||||
|
||||
/// Get number of audio channels in the file (1=mono, 2=stereo)
|
||||
uint getNumChannels() const;
|
||||
|
||||
@@ -186,14 +188,14 @@ public:
|
||||
uint getElapsedMS() const;
|
||||
|
||||
/// Reads audio samples from the WAV file. This routine works only for 8 bit samples.
|
||||
/// Reads given number of elements from the file or if end-of-file reached, as many
|
||||
/// Reads given number of elements from the file or if end-of-file reached, as many
|
||||
/// elements as are left in the file.
|
||||
///
|
||||
/// \return Number of 8-bit integers read from the file.
|
||||
int read(unsigned char *buffer, int maxElems);
|
||||
|
||||
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
|
||||
/// of elements from the file or if end-of-file reached, as many elements as are
|
||||
/// Reads audio samples from the WAV file to 16 bit integer format. Reads given number
|
||||
/// of elements from the file or if end-of-file reached, as many elements as are
|
||||
/// left in the file.
|
||||
///
|
||||
/// \return Number of 16-bit integers read from the file.
|
||||
@@ -201,7 +203,7 @@ public:
|
||||
int maxElems ///< Size of 'buffer' array (number of array elements).
|
||||
);
|
||||
|
||||
/// Reads audio samples from the WAV file to floating point format, converting
|
||||
/// Reads audio samples from the WAV file to floating point format, converting
|
||||
/// sample values to range [-1,1[. Reads given number of elements from the file
|
||||
/// or if end-of-file reached, as many elements as are left in the file.
|
||||
/// Notice that reading in float format supports 8/16/24/32bit sample formats.
|
||||
@@ -242,9 +244,9 @@ private:
|
||||
void writeHeader();
|
||||
|
||||
public:
|
||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||
/// Constructor: Creates a new WAV file. Throws a 'runtime_error' exception
|
||||
/// if file creation fails.
|
||||
WavOutFile(const char *fileName, ///< Filename
|
||||
WavOutFile(const STRING& fileName, ///< Filename
|
||||
int sampleRate, ///< Sample rate (e.g. 44100 etc)
|
||||
int bits, ///< Bits per sample (8 or 16 bits)
|
||||
int channels ///< Number of channels (1=mono, 2=stereo)
|
||||
@@ -255,7 +257,7 @@ public:
|
||||
/// Destructor: Finalizes & closes the WAV file.
|
||||
~WavOutFile();
|
||||
|
||||
/// Write data to WAV file. This function works only with 8bit samples.
|
||||
/// Write data to WAV file. This function works only with 8bit samples.
|
||||
/// Throws a 'runtime_error' exception if writing to file fails.
|
||||
void write(const unsigned char *buffer, ///< Pointer to sample data buffer.
|
||||
int numElems ///< How many array items are to be written to file.
|
||||
@@ -274,4 +276,6 @@ public:
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// FIR low-pass (anti-alias) filter with filter coefficient design routine and
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// MMX optimization.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -54,7 +54,7 @@ using namespace soundtouch;
|
||||
static void _DEBUG_SAVE_AAFIR_COEFFS(SAMPLETYPE *coeffs, int len)
|
||||
{
|
||||
FILE *fptr = fopen("aa_filter_coeffs.txt", "wt");
|
||||
if (fptr == NULL) return;
|
||||
if (fptr == nullptr) return;
|
||||
|
||||
for (int i = 0; i < len; i ++)
|
||||
{
|
||||
@@ -128,16 +128,16 @@ void AAFilter::calculateCoeffs()
|
||||
tempCoeff = TWOPI / (double)length;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < length; i ++)
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
cntTemp = (double)i - (double)(length / 2);
|
||||
|
||||
temp = cntTemp * wc;
|
||||
if (temp != 0)
|
||||
if (temp != 0)
|
||||
{
|
||||
h = sin(temp) / temp; // sinc function
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 1.0;
|
||||
}
|
||||
@@ -146,7 +146,7 @@ void AAFilter::calculateCoeffs()
|
||||
temp = w * h;
|
||||
work[i] = temp;
|
||||
|
||||
// calc net sum of coefficients
|
||||
// calc net sum of coefficients
|
||||
sum += temp;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ void AAFilter::calculateCoeffs()
|
||||
// divided by 16384
|
||||
scaleCoeff = 16384.0f / sum;
|
||||
|
||||
for (i = 0; i < length; i ++)
|
||||
for (i = 0; i < length; i ++)
|
||||
{
|
||||
temp = work[i] * scaleCoeff;
|
||||
// scale & round to nearest integer
|
||||
@@ -182,8 +182,8 @@ void AAFilter::calculateCoeffs()
|
||||
}
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Note : The amount of outputted samples is by value of 'filter length'
|
||||
// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels) const
|
||||
{
|
||||
@@ -192,8 +192,8 @@ uint AAFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples
|
||||
|
||||
|
||||
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||
/// samples get removed from src, and produced amount added to dest
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// samples get removed from src, and produced amount added to dest
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// smaller than the amount of input samples.
|
||||
uint AAFilter::evaluate(FIFOSampleBuffer &dest, FIFOSampleBuffer &src) const
|
||||
{
|
||||
|
||||
26
3rdparty/soundtouch/source/SoundTouch/AAFilter.h
vendored
26
3rdparty/soundtouch/source/SoundTouch/AAFilter.h
vendored
@@ -1,10 +1,10 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// Anti-alias filter is used to prevent folding of high frequencies when
|
||||
/// transposing the sample rate with interpolation.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -61,8 +61,8 @@ public:
|
||||
|
||||
~AAFilter();
|
||||
|
||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||
/// Sets new anti-alias filter cut-off edge frequency, scaled to sampling
|
||||
/// frequency (nyquist frequency = 0.5). The filter will cut off the
|
||||
/// frequencies than that.
|
||||
void setCutoffFreq(double newCutoffFreq);
|
||||
|
||||
@@ -71,19 +71,19 @@ public:
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// smaller than the amount of input samples.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels) const;
|
||||
|
||||
/// Applies the filter to the given src & dest pipes, so that processed amount of
|
||||
/// samples get removed from src, and produced amount added to dest
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// samples get removed from src, and produced amount added to dest
|
||||
/// Note : The amount of outputted samples is by value of 'filter length'
|
||||
/// smaller than the amount of input samples.
|
||||
uint evaluate(FIFOSampleBuffer &dest,
|
||||
uint evaluate(FIFOSampleBuffer &dest,
|
||||
FIFOSampleBuffer &src) const;
|
||||
|
||||
};
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
/// taking absolute value that's smoothed by sliding average. Signal levels that
|
||||
/// are below a couple of times the general RMS amplitude level are cut away to
|
||||
/// leave only notable peaks there.
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// - Repeating sound patterns (e.g. beats) are detected by calculating short-term
|
||||
/// autocorrelation function of the enveloped signal.
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// - After whole sound data file has been analyzed as above, the bpm level is
|
||||
/// detected by function 'getBpm' that finds the highest peak of the autocorrelation
|
||||
/// function, calculates it's precise location and converts this reading to bpm's.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -76,8 +76,8 @@ static const int XCORR_UPDATE_SEQUENCE = (int)(TARGET_SRATE / 5);
|
||||
static const int MOVING_AVERAGE_N = 15;
|
||||
|
||||
/// XCorr decay time constant, decay to half in 30 seconds
|
||||
/// If it's desired to have the system adapt quicker to beat rate
|
||||
/// changes within a continuing music stream, then the
|
||||
/// If it's desired to have the system adapt quicker to beat rate
|
||||
/// changes within a continuing music stream, then the
|
||||
/// 'xcorr_decay_time_constant' value can be reduced, yet that
|
||||
/// can increase possibility of glitches in bpm detection.
|
||||
static const double XCORR_DECAY_TIME_CONSTANT = 30.0;
|
||||
@@ -233,16 +233,16 @@ BPMDetect::~BPMDetect()
|
||||
}
|
||||
|
||||
|
||||
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
||||
/// convert to mono, low-pass filter & decimate to about 500 Hz.
|
||||
/// return number of outputted samples.
|
||||
///
|
||||
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
||||
/// the amount of data needed to be processed as calculating autocorrelation
|
||||
/// Decimation is used to remove the unnecessary frequencies and thus to reduce
|
||||
/// the amount of data needed to be processed as calculating autocorrelation
|
||||
/// function is a very-very heavy operation.
|
||||
///
|
||||
/// Anti-alias filtering is done simply by averaging the samples. This is really a
|
||||
/// Anti-alias filtering is done simply by averaging the samples. This is really a
|
||||
/// poor-man's anti-alias filtering, but it's not so critical in this kind of application
|
||||
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
||||
/// (it'd also be difficult to design a high-quality filter with steep cut-off at very
|
||||
/// narrow band)
|
||||
int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
{
|
||||
@@ -252,7 +252,7 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
assert(channels > 0);
|
||||
assert(decimateBy > 0);
|
||||
outcount = 0;
|
||||
for (count = 0; count < numsamples; count ++)
|
||||
for (count = 0; count < numsamples; count ++)
|
||||
{
|
||||
int j;
|
||||
|
||||
@@ -264,7 +264,7 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
src += j;
|
||||
|
||||
decimateCount ++;
|
||||
if (decimateCount >= decimateBy)
|
||||
if (decimateCount >= decimateBy)
|
||||
{
|
||||
// Store every Nth sample only
|
||||
out = (LONG_SAMPLETYPE)(decimateSum / (decimateBy * channels));
|
||||
@@ -272,11 +272,11 @@ int BPMDetect::decimate(SAMPLETYPE *dest, const SAMPLETYPE *src, int numsamples)
|
||||
decimateCount = 0;
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
// check ranges for sure (shouldn't actually be necessary)
|
||||
if (out > 32767)
|
||||
if (out > 32767)
|
||||
{
|
||||
out = 32767;
|
||||
}
|
||||
else if (out < -32768)
|
||||
}
|
||||
else if (out < -32768)
|
||||
{
|
||||
out = -32768;
|
||||
}
|
||||
@@ -294,7 +294,7 @@ void BPMDetect::updateXCorr(int process_samples)
|
||||
{
|
||||
int offs;
|
||||
SAMPLETYPE *pBuffer;
|
||||
|
||||
|
||||
assert(buffer->numSamples() >= (uint)(process_samples + windowLen));
|
||||
assert(process_samples == XCORR_UPDATE_SEQUENCE);
|
||||
|
||||
@@ -311,13 +311,13 @@ void BPMDetect::updateXCorr(int process_samples)
|
||||
}
|
||||
|
||||
#pragma omp parallel for
|
||||
for (offs = windowStart; offs < windowLen; offs ++)
|
||||
for (offs = windowStart; offs < windowLen; offs ++)
|
||||
{
|
||||
float sum;
|
||||
int i;
|
||||
|
||||
sum = 0;
|
||||
for (i = 0; i < process_samples; i ++)
|
||||
for (i = 0; i < process_samples; i ++)
|
||||
{
|
||||
sum += tmp[i] * pBuffer[i + offs]; // scaling the sub-result shouldn't be necessary
|
||||
}
|
||||
@@ -376,8 +376,6 @@ void BPMDetect::updateBeatPos(int process_samples)
|
||||
// detect beats
|
||||
for (int i = 0; i < skipstep; i++)
|
||||
{
|
||||
LONG_SAMPLETYPE max = 0;
|
||||
|
||||
float sum = beatcorr_ringbuff[beatcorr_ringbuffpos];
|
||||
sum -= beat_lpf.update(sum);
|
||||
|
||||
@@ -433,7 +431,7 @@ void BPMDetect::inputSamples(const SAMPLETYPE *samples, int numSamples)
|
||||
|
||||
// when the buffer has enough samples for processing...
|
||||
int req = max(windowLen + XCORR_UPDATE_SEQUENCE, 2 * XCORR_UPDATE_SEQUENCE);
|
||||
while ((int)buffer->numSamples() >= req)
|
||||
while ((int)buffer->numSamples() >= req)
|
||||
{
|
||||
// ... update autocorrelations...
|
||||
updateXCorr(XCORR_UPDATE_SEQUENCE);
|
||||
@@ -504,7 +502,7 @@ void MAFilter(float *dest, const float *source, int start, int end, int N)
|
||||
|
||||
double sum = 0;
|
||||
for (int j = i1; j < i2; j ++)
|
||||
{
|
||||
{
|
||||
sum += source[j];
|
||||
}
|
||||
dest[i] = (float)(sum / (i2 - i1));
|
||||
@@ -550,19 +548,19 @@ float BPMDetect::getBpm()
|
||||
}
|
||||
|
||||
|
||||
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||
/// Get beat position arrays. Note: The array includes also really low beat detection values
|
||||
/// in absence of clear strong beats. Consumer may wish to filter low values away.
|
||||
/// - "pos" receive array of beat positions
|
||||
/// - "values" receive array of beat detection strengths
|
||||
/// - max_num indicates max.size of "pos" and "values" array.
|
||||
/// - max_num indicates max.size of "pos" and "values" array.
|
||||
///
|
||||
/// You can query a suitable array sized by calling this with NULL in "pos" & "values".
|
||||
/// You can query a suitable array sized by calling this with nullptr in "pos" & "values".
|
||||
///
|
||||
/// \return number of beats in the arrays.
|
||||
int BPMDetect::getBeats(float *pos, float *values, int max_num)
|
||||
{
|
||||
int num = (int)beats.size();
|
||||
if ((!pos) || (!values)) return num; // pos or values NULL, return just size
|
||||
if ((!pos) || (!values)) return num; // pos or values nullptr, return just size
|
||||
|
||||
for (int i = 0; (i < num) && (i < max_num); i++)
|
||||
{
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// A buffer class for temporarily storaging sound samples, operates as a
|
||||
/// first-in-first-out pipe.
|
||||
///
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// Samples are added to the end of the sample buffer with the 'putSamples'
|
||||
/// function, and are received from the beginning of the buffer by calling
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// the 'receiveSamples' function. The class automatically removes the
|
||||
/// outputted samples from the buffer, as well as grows the buffer size
|
||||
/// whenever necessary.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -50,12 +50,12 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||
{
|
||||
assert(numChannels > 0);
|
||||
sizeInBytes = 0; // reasonable initial value
|
||||
buffer = NULL;
|
||||
bufferUnaligned = NULL;
|
||||
buffer = nullptr;
|
||||
bufferUnaligned = nullptr;
|
||||
samplesInBuffer = 0;
|
||||
bufferPos = 0;
|
||||
channels = (uint)numChannels;
|
||||
ensureCapacity(32); // allocate initial capacity
|
||||
ensureCapacity(32); // allocate initial capacity
|
||||
}
|
||||
|
||||
|
||||
@@ -63,8 +63,8 @@ FIFOSampleBuffer::FIFOSampleBuffer(int numChannels)
|
||||
FIFOSampleBuffer::~FIFOSampleBuffer()
|
||||
{
|
||||
delete[] bufferUnaligned;
|
||||
bufferUnaligned = NULL;
|
||||
buffer = NULL;
|
||||
bufferUnaligned = nullptr;
|
||||
buffer = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -82,11 +82,11 @@ void FIFOSampleBuffer::setChannels(int numChannels)
|
||||
|
||||
|
||||
// if output location pointer 'bufferPos' isn't zero, 'rewinds' the buffer and
|
||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||
// zeroes this pointer by copying samples from the 'bufferPos' pointer
|
||||
// location on to the beginning of the buffer.
|
||||
void FIFOSampleBuffer::rewind()
|
||||
{
|
||||
if (buffer && bufferPos)
|
||||
if (buffer && bufferPos)
|
||||
{
|
||||
memmove(buffer, ptrBegin(), sizeof(SAMPLETYPE) * channels * samplesInBuffer);
|
||||
bufferPos = 0;
|
||||
@@ -94,7 +94,7 @@ void FIFOSampleBuffer::rewind()
|
||||
}
|
||||
|
||||
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// Adds 'numSamples' pcs of samples from the 'samples' memory position to
|
||||
// the sample buffer.
|
||||
void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
@@ -107,7 +107,7 @@ void FIFOSampleBuffer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
// samples.
|
||||
//
|
||||
// This function is used to update the number of samples in the sample buffer
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// when accessing the buffer directly with 'ptrEnd' function. Please be
|
||||
// careful though!
|
||||
void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||
{
|
||||
@@ -119,31 +119,31 @@ void FIFOSampleBuffer::putSamples(uint nSamples)
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
// Returns a pointer to the end of the used part of the sample buffer (i.e.
|
||||
// where the new samples are to be inserted). This function may be used for
|
||||
// inserting new samples into the sample buffer directly. Please be careful!
|
||||
//
|
||||
// Parameter 'slackCapacity' tells the function how much free capacity (in
|
||||
// terms of samples) there _at least_ should be, in order to the caller to
|
||||
// successfully insert all the required samples to the buffer. When necessary,
|
||||
// successfully insert all the required samples to the buffer. When necessary,
|
||||
// the function grows the buffer size to comply with this requirement.
|
||||
//
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// When using this function as means for inserting new samples, also remember
|
||||
// to increase the sample count afterwards, by calling the
|
||||
// 'putSamples(numSamples)' function.
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrEnd(uint slackCapacity)
|
||||
{
|
||||
ensureCapacity(samplesInBuffer + slackCapacity);
|
||||
return buffer + samplesInBuffer * channels;
|
||||
}
|
||||
|
||||
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Returns a pointer to the beginning of the currently non-outputted samples.
|
||||
// This function is provided for accessing the output samples directly.
|
||||
// Please be careful!
|
||||
//
|
||||
// When using this function to output samples, also remember to 'remove' the
|
||||
// outputted samples from the buffer by calling the
|
||||
// outputted samples from the buffer by calling the
|
||||
// 'receiveSamples(numSamples)' function
|
||||
SAMPLETYPE *FIFOSampleBuffer::ptrBegin()
|
||||
{
|
||||
@@ -160,13 +160,13 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||
{
|
||||
SAMPLETYPE *tempUnaligned, *temp;
|
||||
|
||||
if (capacityRequirement > getCapacity())
|
||||
if (capacityRequirement > getCapacity())
|
||||
{
|
||||
// enlarge the buffer in 4kbyte steps (round up to next 4k boundary)
|
||||
sizeInBytes = (capacityRequirement * channels * sizeof(SAMPLETYPE) + 4095) & (uint)-4096;
|
||||
assert(sizeInBytes % 2 == 0);
|
||||
tempUnaligned = new SAMPLETYPE[sizeInBytes / sizeof(SAMPLETYPE) + 16 / sizeof(SAMPLETYPE)];
|
||||
if (tempUnaligned == NULL)
|
||||
if (tempUnaligned == nullptr)
|
||||
{
|
||||
ST_THROW_RT_ERROR("Couldn't allocate memory!\n");
|
||||
}
|
||||
@@ -180,8 +180,8 @@ void FIFOSampleBuffer::ensureCapacity(uint capacityRequirement)
|
||||
buffer = temp;
|
||||
bufferUnaligned = tempUnaligned;
|
||||
bufferPos = 0;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// simply rewind the buffer (if necessary)
|
||||
rewind();
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// Notes : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||
/// in parallel. See the following article link for more detailed discussion
|
||||
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||
/// in parallel. See the following article link for more detailed discussion
|
||||
/// about SoundTouch OpenMP optimizations:
|
||||
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||
///
|
||||
@@ -59,8 +59,8 @@ FIRFilter::FIRFilter()
|
||||
resultDivider = 0;
|
||||
length = 0;
|
||||
lengthDiv8 = 0;
|
||||
filterCoeffs = NULL;
|
||||
filterCoeffsStereo = NULL;
|
||||
filterCoeffs = nullptr;
|
||||
filterCoeffsStereo = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,20 +75,16 @@ FIRFilter::~FIRFilter()
|
||||
uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
int j, end;
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
// hint compiler autovectorization that loop length is divisible by 8
|
||||
int ilength = length & -8;
|
||||
uint ilength = length & -8;
|
||||
|
||||
assert((length != 0) && (length == ilength) && (src != NULL) && (dest != NULL) && (filterCoeffs != NULL));
|
||||
assert((length != 0) && (length == ilength) && (src != nullptr) && (dest != nullptr) && (filterCoeffs != nullptr));
|
||||
assert(numSamples > ilength);
|
||||
|
||||
end = 2 * (numSamples - ilength);
|
||||
|
||||
#pragma omp parallel for
|
||||
for (j = 0; j < end; j += 2)
|
||||
for (j = 0; j < end; j += 2)
|
||||
{
|
||||
const SAMPLETYPE *ptr;
|
||||
LONG_SAMPLETYPE suml, sumr;
|
||||
@@ -96,7 +92,7 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||
suml = sumr = 0;
|
||||
ptr = src + j;
|
||||
|
||||
for (int i = 0; i < ilength; i ++)
|
||||
for (uint i = 0; i < ilength; i ++)
|
||||
{
|
||||
suml += ptr[2 * i] * filterCoeffsStereo[2 * i];
|
||||
sumr += ptr[2 * i + 1] * filterCoeffsStereo[2 * i + 1];
|
||||
@@ -121,11 +117,6 @@ uint FIRFilter::evaluateFilterStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, ui
|
||||
uint FIRFilter::evaluateFilterMono(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples) const
|
||||
{
|
||||
int j, end;
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
// hint compiler autovectorization that loop length is divisible by 8
|
||||
int ilength = length & -8;
|
||||
@@ -160,16 +151,10 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||
{
|
||||
int j, end;
|
||||
|
||||
#ifdef SOUNDTOUCH_FLOAT_SAMPLES
|
||||
// when using floating point samples, use a scaler instead of a divider
|
||||
// because division is much slower operation than multiplying.
|
||||
double dScaler = 1.0 / (double)resultDivider;
|
||||
#endif
|
||||
|
||||
assert(length != 0);
|
||||
assert(src != NULL);
|
||||
assert(dest != NULL);
|
||||
assert(filterCoeffs != NULL);
|
||||
assert(src != nullptr);
|
||||
assert(dest != nullptr);
|
||||
assert(filterCoeffs != nullptr);
|
||||
assert(numChannels < 16);
|
||||
|
||||
// hint compiler autovectorization that loop length is divisible by 8
|
||||
@@ -201,7 +186,7 @@ uint FIRFilter::evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uin
|
||||
ptr ++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (c = 0; c < numChannels; c ++)
|
||||
{
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
@@ -257,11 +242,11 @@ uint FIRFilter::getLength() const
|
||||
}
|
||||
|
||||
|
||||
// Applies the filter to the given sequence of samples.
|
||||
// Applies the filter to the given sequence of samples.
|
||||
//
|
||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
// smaller than the amount of input samples.
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||
uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels)
|
||||
{
|
||||
assert(length > 0);
|
||||
assert(lengthDiv8 * 8 == length);
|
||||
@@ -272,7 +257,7 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
||||
if (numChannels == 1)
|
||||
{
|
||||
return evaluateFilterMono(dest, src, numSamples);
|
||||
}
|
||||
}
|
||||
else if (numChannels == 2)
|
||||
{
|
||||
return evaluateFilterStereo(dest, src, numSamples);
|
||||
@@ -286,9 +271,9 @@ uint FIRFilter::evaluate(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSample
|
||||
}
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX-capable CPU available or not.
|
||||
void * FIRFilter::operator new(size_t s)
|
||||
void * FIRFilter::operator new(size_t)
|
||||
{
|
||||
// Notice! don't use "new FIRFilter" directly, use "newInstance" to create a new instance instead!
|
||||
ST_THROW_RT_ERROR("Error in FIRFilter::new: Don't use 'new FIRFilter', use 'newInstance' member instead!");
|
||||
@@ -301,6 +286,7 @@ FIRFilter * FIRFilter::newInstance()
|
||||
uint uExtensions;
|
||||
|
||||
uExtensions = detectCPUextensions();
|
||||
(void)uExtensions;
|
||||
|
||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
/// General FIR digital filter routines with MMX optimization.
|
||||
///
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// Note : MMX optimized functions reside in a separate, platform-specific file,
|
||||
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -41,11 +41,11 @@
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class FIRFilter
|
||||
class FIRFilter
|
||||
{
|
||||
protected:
|
||||
// Number of FIR filter taps
|
||||
uint length;
|
||||
uint length;
|
||||
// Number of FIR filter taps divided by 8
|
||||
uint lengthDiv8;
|
||||
|
||||
@@ -59,11 +59,11 @@ protected:
|
||||
SAMPLETYPE *filterCoeffs;
|
||||
SAMPLETYPE *filterCoeffsStereo;
|
||||
|
||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint evaluateFilterStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual uint evaluateFilterMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples) const;
|
||||
virtual uint evaluateFilterMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, uint numSamples, uint numChannels);
|
||||
|
||||
@@ -71,26 +71,26 @@ public:
|
||||
FIRFilter();
|
||||
virtual ~FIRFilter();
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX-capable CPU available or not.
|
||||
static void * operator new(size_t s);
|
||||
|
||||
static FIRFilter *newInstance();
|
||||
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
/// Applies the filter to the given sequence of samples.
|
||||
/// Note : The amount of outputted samples is by value of 'filter_length'
|
||||
/// smaller than the amount of input samples.
|
||||
///
|
||||
/// \return Number of samples copied to 'dest'.
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint evaluate(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
uint numSamples,
|
||||
uint numChannels);
|
||||
|
||||
uint getLength() const;
|
||||
|
||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||
uint newLength,
|
||||
virtual void setCoefficients(const SAMPLETYPE *coeffs,
|
||||
uint newLength,
|
||||
uint uResultDivFactor);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
/// Cubic interpolation routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -37,7 +37,7 @@
|
||||
using namespace soundtouch;
|
||||
|
||||
// cubic interpolation coefficients
|
||||
static const float _coeffs[]=
|
||||
static const float _coeffs[]=
|
||||
{ -0.5f, 1.0f, -0.5f, 0.0f,
|
||||
1.5f, -2.5f, 0.0f, 1.0f,
|
||||
-1.5f, 2.0f, 0.5f, 0.0f,
|
||||
@@ -56,10 +56,10 @@ void InterpolateCubic::resetRegisters()
|
||||
}
|
||||
|
||||
|
||||
/// Transpose mono audio. Returns number of produced output samples, and
|
||||
/// Transpose mono audio. Returns number of produced output samples, and
|
||||
/// updates "srcSamples" to amount of consumed source samples
|
||||
int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
{
|
||||
int i;
|
||||
@@ -101,10 +101,10 @@ int InterpolateCubic::transposeMono(SAMPLETYPE *pdest,
|
||||
}
|
||||
|
||||
|
||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||
/// updates "srcSamples" to amount of consumed source samples
|
||||
int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
{
|
||||
int i;
|
||||
@@ -148,10 +148,10 @@ int InterpolateCubic::transposeStereo(SAMPLETYPE *pdest,
|
||||
}
|
||||
|
||||
|
||||
/// Transpose multi-channel audio. Returns number of produced output samples, and
|
||||
/// Transpose multi-channel audio. Returns number of produced output samples, and
|
||||
/// updates "srcSamples" to amount of consumed source samples
|
||||
int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int InterpolateCubic::transposeMulti(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
/// Cubic interpolation routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -38,17 +38,17 @@
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class InterpolateCubic final : public TransposerBase
|
||||
class InterpolateCubic : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
|
||||
double fract;
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
|
||||
virtual void resetRegisters() override;
|
||||
|
||||
int getLatency() const override
|
||||
virtual int getLatency() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
/// Linear interpolation algorithm.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -38,7 +38,7 @@ using namespace soundtouch;
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// InterpolateLinearInteger - integer arithmetic implementation
|
||||
//
|
||||
//
|
||||
|
||||
/// fixed-point interpolation routine precision
|
||||
#define SCALE 65536
|
||||
@@ -47,7 +47,7 @@ using namespace soundtouch;
|
||||
// Constructor
|
||||
InterpolateLinearInteger::InterpolateLinearInteger() : TransposerBase()
|
||||
{
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||
resetRegisters();
|
||||
setRate(1.0f);
|
||||
@@ -60,8 +60,8 @@ void InterpolateLinearInteger::resetRegisters()
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||
{
|
||||
@@ -73,7 +73,7 @@ int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *
|
||||
while (srcCount < srcSampleEnd)
|
||||
{
|
||||
LONG_SAMPLETYPE temp;
|
||||
|
||||
|
||||
assert(iFract < SCALE);
|
||||
|
||||
temp = (SCALE - iFract) * src[0] + iFract * src[1];
|
||||
@@ -93,8 +93,8 @@ int InterpolateLinearInteger::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Stereo' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||
{
|
||||
@@ -107,7 +107,7 @@ int InterpolateLinearInteger::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE
|
||||
{
|
||||
LONG_SAMPLETYPE temp0;
|
||||
LONG_SAMPLETYPE temp1;
|
||||
|
||||
|
||||
assert(iFract < SCALE);
|
||||
|
||||
temp0 = (SCALE - iFract) * src[0] + iFract * src[2];
|
||||
@@ -140,7 +140,7 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||
while (srcCount < srcSampleEnd)
|
||||
{
|
||||
LONG_SAMPLETYPE temp, vol1;
|
||||
|
||||
|
||||
assert(iFract < SCALE);
|
||||
vol1 = (LONG_SAMPLETYPE)(SCALE - iFract);
|
||||
for (int c = 0; c < numChannels; c ++)
|
||||
@@ -164,7 +164,7 @@ int InterpolateLinearInteger::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE
|
||||
}
|
||||
|
||||
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// iRate, larger faster iRates.
|
||||
void InterpolateLinearInteger::setRate(double newRate)
|
||||
{
|
||||
@@ -176,14 +176,14 @@ void InterpolateLinearInteger::setRate(double newRate)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// InterpolateLinearFloat - floating point arithmetic implementation
|
||||
//
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Constructor
|
||||
InterpolateLinearFloat::InterpolateLinearFloat() : TransposerBase()
|
||||
{
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// Notice: use local function calling syntax for sake of clarity,
|
||||
// to indicate the fact that C++ constructor can't call virtual functions.
|
||||
resetRegisters();
|
||||
setRate(1.0);
|
||||
@@ -196,8 +196,8 @@ void InterpolateLinearFloat::resetRegisters()
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||
{
|
||||
@@ -228,8 +228,8 @@ int InterpolateLinearFloat::transposeMono(SAMPLETYPE *dest, const SAMPLETYPE *sr
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// 'Mono' version of the routine. Returns the number of samples returned in
|
||||
// the "dest" buffer
|
||||
int InterpolateLinearFloat::transposeStereo(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples)
|
||||
{
|
||||
@@ -272,7 +272,7 @@ int InterpolateLinearFloat::transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *s
|
||||
while (srcCount < srcSampleEnd)
|
||||
{
|
||||
float temp, vol1, fract_float;
|
||||
|
||||
|
||||
vol1 = (float)(1.0 - fract);
|
||||
fract_float = (float)fract;
|
||||
for (int c = 0; c < numChannels; c ++)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
///
|
||||
/// Linear interpolation routine.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -39,29 +39,29 @@ namespace soundtouch
|
||||
{
|
||||
|
||||
/// Linear transposer class that uses integer arithmetic
|
||||
class InterpolateLinearInteger final : public TransposerBase
|
||||
class InterpolateLinearInteger : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
int iFract;
|
||||
int iRate;
|
||||
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
||||
public:
|
||||
InterpolateLinearInteger();
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(double newRate) override;
|
||||
|
||||
virtual void resetRegisters() override;
|
||||
|
||||
int getLatency() const override
|
||||
virtual int getLatency() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -69,25 +69,25 @@ public:
|
||||
|
||||
|
||||
/// Linear transposer class that uses floating point arithmetic
|
||||
class InterpolateLinearFloat final : public TransposerBase
|
||||
class InterpolateLinearFloat : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
double fract;
|
||||
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples) override;
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples);
|
||||
virtual int transposeMulti(SAMPLETYPE *dest, const SAMPLETYPE *src, int &srcSamples);
|
||||
|
||||
public:
|
||||
InterpolateLinearFloat();
|
||||
|
||||
void resetRegisters() override;
|
||||
virtual void resetRegisters();
|
||||
|
||||
int getLatency() const override
|
||||
int getLatency() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||
///
|
||||
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||
/// with kaiser window.
|
||||
///
|
||||
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||
@@ -43,7 +43,7 @@ using namespace soundtouch;
|
||||
|
||||
/// Kaiser window with beta = 2.0
|
||||
/// Values scaled down by 5% to avoid overflows
|
||||
static const double _kaiser8[8] =
|
||||
static const double _kaiser8[8] =
|
||||
{
|
||||
0.41778693317814,
|
||||
0.64888025049173,
|
||||
@@ -71,10 +71,10 @@ void InterpolateShannon::resetRegisters()
|
||||
#define PI 3.1415926536
|
||||
#define sinc(x) (sin(PI * (x)) / (PI * (x)))
|
||||
|
||||
/// Transpose mono audio. Returns number of produced output samples, and
|
||||
/// Transpose mono audio. Returns number of produced output samples, and
|
||||
/// updates "srcSamples" to amount of consumed source samples
|
||||
int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
{
|
||||
int i;
|
||||
@@ -119,10 +119,10 @@ int InterpolateShannon::transposeMono(SAMPLETYPE *pdest,
|
||||
}
|
||||
|
||||
|
||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||
/// updates "srcSamples" to amount of consumed source samples
|
||||
int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
{
|
||||
int i;
|
||||
@@ -169,11 +169,11 @@ int InterpolateShannon::transposeStereo(SAMPLETYPE *pdest,
|
||||
}
|
||||
|
||||
|
||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||
/// Transpose stereo audio. Returns number of produced output samples, and
|
||||
/// updates "srcSamples" to amount of consumed source samples
|
||||
int InterpolateShannon::transposeMulti(SAMPLETYPE *pdest,
|
||||
const SAMPLETYPE *psrc,
|
||||
int &srcSamples)
|
||||
int InterpolateShannon::transposeMulti(SAMPLETYPE *,
|
||||
const SAMPLETYPE *,
|
||||
int &)
|
||||
{
|
||||
// not implemented
|
||||
assert(false);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||
///
|
||||
/// Sample interpolation routine using 8-tap band-limited Shannon interpolation
|
||||
/// with kaiser window.
|
||||
///
|
||||
/// Notice. This algorithm is remarkably much heavier than linear or cubic
|
||||
@@ -43,17 +43,17 @@
|
||||
namespace soundtouch
|
||||
{
|
||||
|
||||
class InterpolateShannon final : public TransposerBase
|
||||
class InterpolateShannon : public TransposerBase
|
||||
{
|
||||
protected:
|
||||
int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) override;
|
||||
|
||||
double fract;
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
void resetRegisters() override;
|
||||
|
||||
int getLatency() const override
|
||||
virtual int getLatency() const override
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Peak detection routine.
|
||||
/// Peak detection routine.
|
||||
///
|
||||
/// The routine detects highest value on an array of values and calculates the
|
||||
/// The routine detects highest value on an array of values and calculates the
|
||||
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -80,7 +80,7 @@ int PeakFinder::findTop(const float *data, int peakpos) const
|
||||
|
||||
|
||||
// Finds 'ground level' of a peak hump by starting from 'peakpos' and proceeding
|
||||
// to direction defined by 'direction' until next 'hump' after minimum value will
|
||||
// to direction defined by 'direction' until next 'hump' after minimum value will
|
||||
// begin
|
||||
int PeakFinder::findGround(const float *data, int peakpos, int direction) const
|
||||
{
|
||||
@@ -186,7 +186,7 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||
|
||||
peakLevel = data[peakpos];
|
||||
|
||||
if (gp1 == gp2)
|
||||
if (gp1 == gp2)
|
||||
{
|
||||
// avoid rounding errors when all are equal
|
||||
assert(gp1 == peakpos);
|
||||
@@ -210,7 +210,7 @@ double PeakFinder::getPeakCenter(const float *data, int peakpos) const
|
||||
}
|
||||
|
||||
|
||||
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||
double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||
{
|
||||
|
||||
int i;
|
||||
@@ -225,19 +225,19 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||
peak = data[minPos];
|
||||
for (i = minPos + 1; i < maxPos; i ++)
|
||||
{
|
||||
if (data[i] > peak)
|
||||
if (data[i] > peak)
|
||||
{
|
||||
peak = data[i];
|
||||
peakpos = i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Calculate exact location of the highest peak mass center
|
||||
highPeak = getPeakCenter(data, peakpos);
|
||||
peak = highPeak;
|
||||
|
||||
// Now check if the highest peak were in fact harmonic of the true base beat peak
|
||||
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
||||
// Now check if the highest peak were in fact harmonic of the true base beat peak
|
||||
// - sometimes the highest peak can be Nth harmonic of the true base peak yet
|
||||
// just a slightly higher than the true base
|
||||
|
||||
for (i = 1; i < 3; i ++)
|
||||
@@ -254,7 +254,7 @@ double PeakFinder::detectPeak(const float *data, int aminPos, int amaxPos)
|
||||
// calculate mass-center of possible harmonic peak
|
||||
peaktmp = getPeakCenter(data, peakpos);
|
||||
|
||||
// accept harmonic peak if
|
||||
// accept harmonic peak if
|
||||
// (a) it is found
|
||||
// (b) is within ±4% of the expected harmonic interval
|
||||
// (c) has at least half x-corr value of the max. peak
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// The routine detects highest value on an array of values and calculates the
|
||||
/// The routine detects highest value on an array of values and calculates the
|
||||
/// precise peak location as a mass-center of the 'hump' around the peak value.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -60,7 +60,7 @@ protected:
|
||||
int findTop(const float *data, int peakpos) const;
|
||||
|
||||
|
||||
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
|
||||
/// Finds the 'ground' level, i.e. smallest level between two neighbouring peaks, to right-
|
||||
/// or left-hand side of the given peak position.
|
||||
int findGround(const float *data, /// Data vector.
|
||||
int peakpos, /// Peak position index within the data vector.
|
||||
@@ -71,7 +71,7 @@ protected:
|
||||
double getPeakCenter(const float *data, int peakpos) const;
|
||||
|
||||
public:
|
||||
/// Constructor.
|
||||
/// Constructor.
|
||||
PeakFinder();
|
||||
|
||||
/// Detect exact peak position of the data vector by finding the largest peak 'hump'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
/// together with anti-alias filtering (first order interpolation with anti-
|
||||
/// alias filtering should be quite adequate for this application)
|
||||
///
|
||||
@@ -50,7 +50,7 @@ TransposerBase::ALGORITHM TransposerBase::algorithm = TransposerBase::CUBIC;
|
||||
// Constructor
|
||||
RateTransposer::RateTransposer() : FIFOProcessor(&outputBuffer)
|
||||
{
|
||||
bUseAAFilter =
|
||||
bUseAAFilter =
|
||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
true;
|
||||
#else
|
||||
@@ -96,7 +96,7 @@ AAFilter *RateTransposer::getAAFilter()
|
||||
}
|
||||
|
||||
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// Sets new target iRate. Normal iRate = 1.0, smaller values represent slower
|
||||
// iRate, larger faster iRates.
|
||||
void RateTransposer::setRate(double newRate)
|
||||
{
|
||||
@@ -105,11 +105,11 @@ void RateTransposer::setRate(double newRate)
|
||||
pTransposer->setRate(newRate);
|
||||
|
||||
// design a new anti-alias filter
|
||||
if (newRate > 1.0)
|
||||
if (newRate > 1.0)
|
||||
{
|
||||
fCutoff = 0.5 / newRate;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
fCutoff = 0.5 * newRate;
|
||||
}
|
||||
@@ -125,14 +125,12 @@ void RateTransposer::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
}
|
||||
|
||||
|
||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
// Returns amount of samples returned in the "dest" buffer.
|
||||
// The maximum amount of samples that can be returned at a time is set by
|
||||
// the 'set_returnBuffer_size' function.
|
||||
void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||
{
|
||||
uint count;
|
||||
|
||||
if (nSamples == 0) return;
|
||||
|
||||
// Store samples to input buffer
|
||||
@@ -140,16 +138,16 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||
|
||||
// If anti-alias filter is turned off, simply transpose without applying
|
||||
// the filter
|
||||
if (bUseAAFilter == false)
|
||||
if (bUseAAFilter == false)
|
||||
{
|
||||
count = pTransposer->transpose(outputBuffer, inputBuffer);
|
||||
(void)pTransposer->transpose(outputBuffer, inputBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(pAAFilter);
|
||||
|
||||
// Transpose with anti-alias filter
|
||||
if (pTransposer->rate < 1.0f)
|
||||
if (pTransposer->rate < 1.0f)
|
||||
{
|
||||
// If the parameter 'Rate' value is smaller than 1, first transpose
|
||||
// the samples and then apply the anti-alias filter to remove aliasing.
|
||||
@@ -159,8 +157,8 @@ void RateTransposer::processSamples(const SAMPLETYPE *src, uint nSamples)
|
||||
|
||||
// Apply the anti-alias filter for transposed samples in midBuffer
|
||||
pAAFilter->evaluate(outputBuffer, midBuffer);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the parameter 'Rate' value is larger than 1, first apply the
|
||||
// anti-alias filter to remove high frequencies (prevent them from folding
|
||||
@@ -224,7 +222,7 @@ int RateTransposer::getLatency() const
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TransposerBase - Base class for interpolation
|
||||
//
|
||||
//
|
||||
|
||||
// static function to set interpolation algorithm
|
||||
void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||
@@ -233,7 +231,7 @@ void TransposerBase::setAlgorithm(TransposerBase::ALGORITHM a)
|
||||
}
|
||||
|
||||
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Transposes the sample rate of the given samples using linear interpolation.
|
||||
// Returns the number of samples returned in the "dest" buffer
|
||||
int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||
{
|
||||
@@ -248,11 +246,11 @@ int TransposerBase::transpose(FIFOSampleBuffer &dest, FIFOSampleBuffer &src)
|
||||
{
|
||||
numOutput = transposeMono(pdest, psrc, numSrcSamples);
|
||||
}
|
||||
else if (numChannels == 2)
|
||||
else if (numChannels == 2)
|
||||
{
|
||||
numOutput = transposeStereo(pdest, psrc, numSrcSamples);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
#endif // USE_MULTICH_ALWAYS
|
||||
{
|
||||
assert(numChannels > 0);
|
||||
@@ -309,7 +307,7 @@ TransposerBase *TransposerBase::newInstance()
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
///
|
||||
/// Sample rate transposer. Changes sample rate by using linear interpolation
|
||||
/// together with anti-alias filtering (first order interpolation with anti-
|
||||
/// alias filtering should be quite adequate for this application).
|
||||
///
|
||||
/// Use either of the derived classes of 'RateTransposerInteger' or
|
||||
/// Use either of the derived classes of 'RateTransposerInteger' or
|
||||
/// 'RateTransposerFloat' for corresponding integer/floating point tranposing
|
||||
/// algorithm implementation.
|
||||
///
|
||||
@@ -59,14 +59,14 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeMono(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) = 0;
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeStereo(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) = 0;
|
||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
virtual int transposeMulti(SAMPLETYPE *dest,
|
||||
const SAMPLETYPE *src,
|
||||
int &srcSamples) = 0;
|
||||
|
||||
static ALGORITHM algorithm;
|
||||
@@ -115,11 +115,11 @@ protected:
|
||||
bool bUseAAFilter;
|
||||
|
||||
|
||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
/// Transposes sample rate by applying anti-alias filter to prevent folding.
|
||||
/// Returns amount of samples returned in the "dest" buffer.
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples(const SAMPLETYPE *src,
|
||||
void processSamples(const SAMPLETYPE *src,
|
||||
uint numSamples);
|
||||
|
||||
public:
|
||||
@@ -138,7 +138,7 @@ public:
|
||||
/// Returns nonzero if anti-alias filter is enabled.
|
||||
bool isAAFilterEnabled() const;
|
||||
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// Sets new target rate. Normal rate = 1.0, smaller values represent slower
|
||||
/// rate, larger faster rates.
|
||||
virtual void setRate(double newRate);
|
||||
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
/// SoundTouch - main class for tempo/pitch/rate adjusting routines.
|
||||
///
|
||||
/// Notes:
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// - Initialize the SoundTouch object instance by setting up the sound stream
|
||||
/// parameters with functions 'setSampleRate' and 'setChannels', then set
|
||||
/// desired tempo/pitch/rate settings with the corresponding functions.
|
||||
///
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// - The SoundTouch class behaves like a first-in-first-out pipeline: The
|
||||
/// samples that are to be processed are fed into one of the pipe by calling
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// function 'putSamples', while the ready processed samples can be read
|
||||
/// from the other end of the pipeline with function 'receiveSamples'.
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
///
|
||||
/// - The SoundTouch processing classes require certain sized 'batches' of
|
||||
/// samples in order to process the sound. For this reason the classes buffer
|
||||
/// incoming samples until there are enough of samples available for
|
||||
/// processing, then they carry out the processing step and consequently
|
||||
/// make the processed samples available for outputting.
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
///
|
||||
/// - For the above reason, the processing routines introduce a certain
|
||||
/// 'latency' between the input and output, so that the samples input to
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// SoundTouch may not be immediately available in the output, and neither
|
||||
/// the amount of outputtable samples may not immediately be in direct
|
||||
/// relationship with the amount of previously input samples.
|
||||
///
|
||||
/// - The tempo/pitch/rate control parameters can be altered during processing.
|
||||
@@ -30,8 +30,8 @@
|
||||
/// required.
|
||||
///
|
||||
/// - This class utilizes classes 'TDStretch' for tempo change (without modifying
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// pitch) and 'RateTransposer' for changing the playback rate (that is, both
|
||||
/// tempo and pitch in the same ratio) of the sound. The third available control
|
||||
/// 'pitch' (change pitch but maintain tempo) is produced by a combination of
|
||||
/// combining the two other controls.
|
||||
///
|
||||
@@ -74,7 +74,7 @@
|
||||
#include "cpu_detect.h"
|
||||
|
||||
using namespace soundtouch;
|
||||
|
||||
|
||||
/// test if two floating point numbers are equal
|
||||
#define TEST_FLOAT_EQUAL(a, b) (fabs(a - b) < 1e-10)
|
||||
|
||||
@@ -83,7 +83,7 @@ using namespace soundtouch;
|
||||
extern "C" void soundtouch_ac_test()
|
||||
{
|
||||
printf("SoundTouch Version: %s\n",SOUNDTOUCH_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SoundTouch::SoundTouch()
|
||||
@@ -97,8 +97,8 @@ SoundTouch::SoundTouch()
|
||||
|
||||
rate = tempo = 0;
|
||||
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualPitch =
|
||||
virtualRate =
|
||||
virtualTempo = 1.0;
|
||||
|
||||
calcEffectiveRateAndTempo();
|
||||
@@ -227,9 +227,9 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||
if (!TEST_FLOAT_EQUAL(tempo, oldTempo)) pTDStretch->setTempo(tempo);
|
||||
|
||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
if (rate <= 1.0f)
|
||||
if (rate <= 1.0f)
|
||||
{
|
||||
if (output != pTDStretch)
|
||||
if (output != pTDStretch)
|
||||
{
|
||||
FIFOSamplePipe *tempoOut;
|
||||
|
||||
@@ -246,7 +246,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (output != pRateTransposer)
|
||||
if (output != pRateTransposer)
|
||||
{
|
||||
FIFOSamplePipe *transOut;
|
||||
|
||||
@@ -259,7 +259,7 @@ void SoundTouch::calcEffectiveRateAndTempo()
|
||||
|
||||
output = pRateTransposer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -276,31 +276,31 @@ void SoundTouch::setSampleRate(uint srate)
|
||||
// the input of the object.
|
||||
void SoundTouch::putSamples(const SAMPLETYPE *samples, uint nSamples)
|
||||
{
|
||||
if (bSrateSet == false)
|
||||
if (bSrateSet == false)
|
||||
{
|
||||
ST_THROW_RT_ERROR("SoundTouch : Sample rate not defined");
|
||||
}
|
||||
else if (channels == 0)
|
||||
}
|
||||
else if (channels == 0)
|
||||
{
|
||||
ST_THROW_RT_ERROR("SoundTouch : Number of channels not defined");
|
||||
}
|
||||
|
||||
// accumulate how many samples are expected out from processing, given the current
|
||||
// accumulate how many samples are expected out from processing, given the current
|
||||
// processing setting
|
||||
samplesExpectedOut += (double)nSamples / ((double)rate * (double)tempo);
|
||||
|
||||
#ifndef SOUNDTOUCH_PREVENT_CLICK_AT_RATE_CROSSOVER
|
||||
if (rate <= 1.0f)
|
||||
if (rate <= 1.0f)
|
||||
{
|
||||
// transpose the rate down, output the transposed sound to tempo changer buffer
|
||||
assert(output == pTDStretch);
|
||||
pRateTransposer->putSamples(samples, nSamples);
|
||||
pTDStretch->moveSamples(*pRateTransposer);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
// evaluate the tempo changer, then transpose the rate up,
|
||||
assert(output == pRateTransposer);
|
||||
pTDStretch->putSamples(samples, nSamples);
|
||||
pRateTransposer->moveSamples(*pTDStretch);
|
||||
@@ -327,8 +327,8 @@ void SoundTouch::flush()
|
||||
|
||||
memset(buff, 0, 128 * channels * sizeof(SAMPLETYPE));
|
||||
// "Push" the last active samples out from the processing pipeline by
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// feeding blank samples into the processing pipeline until new,
|
||||
// processed samples appear in the output (not however, more than
|
||||
// 24ksamples in any case)
|
||||
for (i = 0; (numStillExpected > (int)numSamples()) && (i < 200); i ++)
|
||||
{
|
||||
@@ -355,7 +355,7 @@ bool SoundTouch::setSetting(int settingId, int value)
|
||||
// read current tdstretch routine parameters
|
||||
pTDStretch->getParameters(&sampleRate, &sequenceMs, &seekWindowMs, &overlapMs);
|
||||
|
||||
switch (settingId)
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
// enables / disabless anti-alias filter
|
||||
@@ -401,7 +401,7 @@ int SoundTouch::getSetting(int settingId) const
|
||||
{
|
||||
int temp;
|
||||
|
||||
switch (settingId)
|
||||
switch (settingId)
|
||||
{
|
||||
case SETTING_USE_AA_FILTER :
|
||||
return (uint)pRateTransposer->isAAFilterEnabled();
|
||||
@@ -413,15 +413,15 @@ int SoundTouch::getSetting(int settingId) const
|
||||
return (uint)pTDStretch->isQuickSeekEnabled();
|
||||
|
||||
case SETTING_SEQUENCE_MS:
|
||||
pTDStretch->getParameters(NULL, &temp, NULL, NULL);
|
||||
pTDStretch->getParameters(nullptr, &temp, nullptr, nullptr);
|
||||
return temp;
|
||||
|
||||
case SETTING_SEEKWINDOW_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, &temp, NULL);
|
||||
pTDStretch->getParameters(nullptr, nullptr, &temp, nullptr);
|
||||
return temp;
|
||||
|
||||
case SETTING_OVERLAP_MS:
|
||||
pTDStretch->getParameters(NULL, NULL, NULL, &temp);
|
||||
pTDStretch->getParameters(nullptr, nullptr, nullptr, &temp);
|
||||
return temp;
|
||||
|
||||
case SETTING_NOMINAL_INPUT_SEQUENCE :
|
||||
@@ -503,8 +503,8 @@ uint SoundTouch::numUnprocessedSamples() const
|
||||
}
|
||||
|
||||
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// Output samples from beginning of the sample buffer. Copies requested samples to
|
||||
/// output buffer and removes them from the sample buffer. If there are less than
|
||||
/// 'numsample' samples in the buffer, returns all that available.
|
||||
///
|
||||
/// \return Number of samples returned.
|
||||
@@ -516,8 +516,8 @@ uint SoundTouch::receiveSamples(SAMPLETYPE *output, uint maxSamples)
|
||||
}
|
||||
|
||||
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
/// Adjusts book-keeping so that given number of samples are removed from beginning of the
|
||||
/// sample buffer without copying them anywhere.
|
||||
///
|
||||
/// Used to reduce the number of samples in the buffer when accessing the sample buffer directly
|
||||
/// with 'ptrBegin' function.
|
||||
@@ -530,7 +530,7 @@ uint SoundTouch::receiveSamples(uint maxSamples)
|
||||
|
||||
|
||||
/// Get ratio between input and output audio durations, useful for calculating
|
||||
/// processed output duration: if you'll process a stream of N samples, then
|
||||
/// processed output duration: if you'll process a stream of N samples, then
|
||||
/// you can expect to get out N * getInputOutputSampleRatio() samples.
|
||||
double SoundTouch::getInputOutputSampleRatio()
|
||||
{
|
||||
|
||||
142
3rdparty/soundtouch/source/SoundTouch/TDStretch.cpp
vendored
142
3rdparty/soundtouch/source/SoundTouch/TDStretch.cpp
vendored
@@ -1,15 +1,15 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like
|
||||
/// method with several performance-increasing tweaks.
|
||||
///
|
||||
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
||||
/// Notes : MMX optimized functions reside in a separate, platform-specific
|
||||
/// file, e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'.
|
||||
///
|
||||
/// This source file contains OpenMP optimizations that allow speeding up the
|
||||
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||
/// in parallel. See the following article link for more detailed discussion
|
||||
/// corss-correlation algorithm by executing it in several threads / CPU cores
|
||||
/// in parallel. See the following article link for more detailed discussion
|
||||
/// about SoundTouch OpenMP optimizations:
|
||||
/// http://www.softwarecoven.com/parallel-computing-in-embedded-mobile-devices
|
||||
///
|
||||
@@ -54,25 +54,6 @@ using namespace soundtouch;
|
||||
|
||||
#define max(x, y) (((x) > (y)) ? (x) : (y))
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Constant definitions
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// Table for the hierarchical mixing position seeking algorithm
|
||||
const short _scanOffsets[5][24]={
|
||||
{ 124, 186, 248, 310, 372, 434, 496, 558, 620, 682, 744, 806,
|
||||
868, 930, 992, 1054, 1116, 1178, 1240, 1302, 1364, 1426, 1488, 0},
|
||||
{-100, -75, -50, -25, 25, 50, 75, 100, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -20, -15, -10, -5, 5, 10, 15, 20, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ -4, -3, -2, -1, 1, 2, 3, 4, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
{ 121, 114, 97, 114, 98, 105, 108, 32, 104, 99, 117, 111,
|
||||
116, 100, 110, 117, 111, 115, 0, 0, 0, 0, 0, 0}};
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Implementation of the class 'TDStretch'
|
||||
@@ -85,8 +66,8 @@ TDStretch::TDStretch() : FIFOProcessor(&outputBuffer)
|
||||
bQuickSeek = false;
|
||||
channels = 2;
|
||||
|
||||
pMidBuffer = NULL;
|
||||
pMidBufferUnaligned = NULL;
|
||||
pMidBuffer = nullptr;
|
||||
pMidBufferUnaligned = nullptr;
|
||||
overlapLength = 0;
|
||||
|
||||
bAutoSeqSetting = true;
|
||||
@@ -113,11 +94,11 @@ TDStretch::~TDStretch()
|
||||
//
|
||||
// 'sampleRate' = sample rate of the sound
|
||||
// 'sequenceMS' = one processing sequence length in milliseconds (default = 82 ms)
|
||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
// position (default = 28 ms)
|
||||
// 'overlapMS' = overlapping length (default = 12 ms)
|
||||
|
||||
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
int aSeekWindowMS, int aOverlapMS)
|
||||
{
|
||||
// accept only positive parameter values - if zero or negative, use old values instead
|
||||
@@ -133,19 +114,19 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
{
|
||||
this->sequenceMs = aSequenceMS;
|
||||
bAutoSeqSetting = false;
|
||||
}
|
||||
}
|
||||
else if (aSequenceMS == 0)
|
||||
{
|
||||
// if zero, use automatic setting
|
||||
bAutoSeqSetting = true;
|
||||
}
|
||||
|
||||
if (aSeekWindowMS > 0)
|
||||
if (aSeekWindowMS > 0)
|
||||
{
|
||||
this->seekWindowMs = aSeekWindowMS;
|
||||
bAutoSeekSetting = false;
|
||||
}
|
||||
else if (aSeekWindowMS == 0)
|
||||
}
|
||||
else if (aSeekWindowMS == 0)
|
||||
{
|
||||
// if zero, use automatic setting
|
||||
bAutoSeekSetting = true;
|
||||
@@ -162,7 +143,7 @@ void TDStretch::setParameters(int aSampleRate, int aSequenceMS,
|
||||
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void TDStretch::getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const
|
||||
{
|
||||
@@ -251,11 +232,11 @@ bool TDStretch::isQuickSeekEnabled() const
|
||||
// Seeks for the optimal overlap-mixing position.
|
||||
int TDStretch::seekBestOverlapPosition(const SAMPLETYPE *refPos)
|
||||
{
|
||||
if (bQuickSeek)
|
||||
if (bQuickSeek)
|
||||
{
|
||||
return seekBestOverlapPositionQuick(refPos);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
return seekBestOverlapPositionFull(refPos);
|
||||
}
|
||||
@@ -276,8 +257,8 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||
{
|
||||
// stereo sound
|
||||
overlapStereo(pOutput, pInput + 2 * ovlPos);
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
#endif // USE_MULTICH_ALWAYS
|
||||
{
|
||||
assert(channels > 0);
|
||||
@@ -292,7 +273,7 @@ inline void TDStretch::overlap(SAMPLETYPE *pOutput, const SAMPLETYPE *pInput, ui
|
||||
// The best position is determined as the position where the two overlapped
|
||||
// sample sequences are 'most alike', in terms of the highest cross-correlation
|
||||
// value over the overlapping period
|
||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
{
|
||||
int bestOffs;
|
||||
double bestCorr;
|
||||
@@ -319,7 +300,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
corr = calcCrossCorr(refPos + channels * i, pMidBuffer, norm);
|
||||
#else
|
||||
// In non-parallel version call "calcCrossCorrAccumulate" that is otherwise same
|
||||
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||
// as "calcCrossCorr", but saves time by reusing & updating previously stored
|
||||
// "norm" value
|
||||
corr = calcCrossCorrAccumulate(refPos + channels * i, pMidBuffer, norm);
|
||||
#endif
|
||||
@@ -328,7 +309,7 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
corr = ((corr + 0.1) * (1.0 - 0.25 * tmp * tmp));
|
||||
|
||||
// Checks for the highest correlation value
|
||||
if (corr > bestCorr)
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
// For optimal performance, enter critical section only in case that best value found.
|
||||
// in such case repeat 'if' condition as it's possible that parallel execution may have
|
||||
@@ -353,14 +334,14 @@ int TDStretch::seekBestOverlapPositionFull(const SAMPLETYPE *refPos)
|
||||
}
|
||||
|
||||
|
||||
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||
// Quick seek algorithm for improved runtime-performance: First roughly scans through the
|
||||
// correlation area, and then scan surroundings of two best preliminary correlation candidates
|
||||
// with improved precision
|
||||
//
|
||||
// Based on testing:
|
||||
// - This algorithm gives on average 99% as good match as the full algorithm
|
||||
// - this quick seek algorithm finds the best match on ~90% of cases
|
||||
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||
// - on those 10% of cases when this algorithm doesn't find best match,
|
||||
// it still finds on average ~90% match vs. the best possible match
|
||||
int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
{
|
||||
@@ -379,7 +360,7 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
|
||||
bestCorr =
|
||||
bestCorr2 = -FLT_MAX;
|
||||
bestOffs =
|
||||
bestOffs =
|
||||
bestOffs2 = SCANWIND;
|
||||
|
||||
// Scans for the best correlation value by testing each possible position
|
||||
@@ -387,7 +368,7 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
// increase possibility of ideal match.
|
||||
//
|
||||
// Begin from "SCANSTEP" instead of SCANWIND to make the calculation
|
||||
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||
// catch the 'middlepoint' of seekLength vector as that's the a-priori
|
||||
// expected best match position
|
||||
//
|
||||
// Roughly:
|
||||
@@ -475,7 +456,7 @@ int TDStretch::seekBestOverlapPositionQuick(const SAMPLETYPE *refPos)
|
||||
|
||||
|
||||
|
||||
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||
/// For integer algorithm: adapt normalization factor divider with music so that
|
||||
/// it'll not be pessimistically restrictive that can degrade quality on quieter sections
|
||||
/// yet won't cause integer overflows either
|
||||
void TDStretch::adaptNormalizer()
|
||||
@@ -483,7 +464,7 @@ void TDStretch::adaptNormalizer()
|
||||
// Do not adapt normalizer over too silent sequences to avoid averaging filter depleting to
|
||||
// too low values during pauses in music
|
||||
if ((maxnorm > 1000) || (maxnormf > 40000000))
|
||||
{
|
||||
{
|
||||
//norm averaging filter
|
||||
maxnormf = 0.9f * maxnormf + 0.1f * (float)maxnorm;
|
||||
|
||||
@@ -504,7 +485,7 @@ void TDStretch::adaptNormalizer()
|
||||
}
|
||||
|
||||
|
||||
/// clear cross correlation routine state if necessary
|
||||
/// clear cross correlation routine state if necessary
|
||||
void TDStretch::clearCrossCorrState()
|
||||
{
|
||||
// default implementation is empty.
|
||||
@@ -534,7 +515,7 @@ void TDStretch::calcSeqParameters()
|
||||
#define CHECK_LIMITS(x, mi, ma) (((x) < (mi)) ? (mi) : (((x) > (ma)) ? (ma) : (x)))
|
||||
|
||||
double seq, seek;
|
||||
|
||||
|
||||
if (bAutoSeqSetting)
|
||||
{
|
||||
seq = AUTOSEQ_C + AUTOSEQ_K * tempo;
|
||||
@@ -551,7 +532,7 @@ void TDStretch::calcSeqParameters()
|
||||
|
||||
// Update seek window lengths
|
||||
seekWindowLength = (sampleRate * sequenceMs) / 1000;
|
||||
if (seekWindowLength < 2 * overlapLength)
|
||||
if (seekWindowLength < 2 * overlapLength)
|
||||
{
|
||||
seekWindowLength = 2 * overlapLength;
|
||||
}
|
||||
@@ -560,7 +541,7 @@ void TDStretch::calcSeqParameters()
|
||||
|
||||
|
||||
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
// tempo, larger faster tempo.
|
||||
void TDStretch::setTempo(double newTempo)
|
||||
{
|
||||
@@ -571,11 +552,11 @@ void TDStretch::setTempo(double newTempo)
|
||||
// Calculate new sequence duration
|
||||
calcSeqParameters();
|
||||
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
// Calculate ideal skip length (according to tempo value)
|
||||
nominalSkip = tempo * (seekWindowLength - overlapLength);
|
||||
intskip = (int)(nominalSkip + 0.5);
|
||||
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// Calculate how many samples are needed in the 'inputBuffer' to
|
||||
// process another batch of samples
|
||||
//sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength / 2;
|
||||
sampleReq = max(intskip + overlapLength, seekWindowLength) + seekLength;
|
||||
@@ -606,18 +587,18 @@ void TDStretch::processNominalTempo()
|
||||
{
|
||||
assert(tempo == 1.0f);
|
||||
|
||||
if (bMidBufferDirty)
|
||||
if (bMidBufferDirty)
|
||||
{
|
||||
// If there are samples in pMidBuffer waiting for overlapping,
|
||||
// do a single sliding overlapping with them in order to prevent a
|
||||
// do a single sliding overlapping with them in order to prevent a
|
||||
// clicking distortion in the output sound
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
if (inputBuffer.numSamples() < overlapLength)
|
||||
{
|
||||
// wait until we've got overlapLength input samples
|
||||
return;
|
||||
}
|
||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
// Mix the samples in the beginning of 'inputBuffer' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
overlap(outputBuffer.ptrEnd(overlapLength), inputBuffer.ptrBegin(), 0);
|
||||
outputBuffer.putSamples(overlapLength);
|
||||
inputBuffer.receiveSamples(overlapLength);
|
||||
@@ -642,7 +623,7 @@ void TDStretch::processSamples()
|
||||
|
||||
/* Removed this small optimization - can introduce a click to sound when tempo setting
|
||||
crosses the nominal value
|
||||
if (tempo == 1.0f)
|
||||
if (tempo == 1.0f)
|
||||
{
|
||||
// tempo not changed from the original, so bypass the processing
|
||||
processNominalTempo();
|
||||
@@ -652,15 +633,15 @@ void TDStretch::processSamples()
|
||||
|
||||
// Process samples as long as there are enough samples in 'inputBuffer'
|
||||
// to form a processing frame.
|
||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||
while ((int)inputBuffer.numSamples() >= sampleReq)
|
||||
{
|
||||
if (isBeginning == false)
|
||||
{
|
||||
// apart from the very beginning of the track,
|
||||
// apart from the very beginning of the track,
|
||||
// scan for the best overlapping position & do overlap-add
|
||||
offset = seekBestOverlapPosition(inputBuffer.ptrBegin());
|
||||
|
||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||
// Mix the samples in the 'inputBuffer' at position of 'offset' with the
|
||||
// samples in 'midBuffer' using sliding overlapping
|
||||
// ... first partially overlap with the end of the previous sequence
|
||||
// (that's in 'midBuffer')
|
||||
@@ -705,11 +686,11 @@ void TDStretch::processSamples()
|
||||
temp = (seekWindowLength - 2 * overlapLength);
|
||||
outputBuffer.putSamples(inputBuffer.ptrBegin() + channels * offset, (uint)temp);
|
||||
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// Copies the end of the current sequence from 'inputBuffer' to
|
||||
// 'midBuffer' for being mixed with the beginning of the next
|
||||
// processing sequence and so on
|
||||
assert((offset + temp + overlapLength) <= (int)inputBuffer.numSamples());
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
||||
memcpy(pMidBuffer, inputBuffer.ptrBegin() + channels * (offset + temp),
|
||||
channels * sizeof(SAMPLETYPE) * overlapLength);
|
||||
|
||||
// Remove the processed samples from the input buffer. Update
|
||||
@@ -757,9 +738,9 @@ void TDStretch::acceptNewOverlapLength(int newOverlapLength)
|
||||
}
|
||||
|
||||
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
void * TDStretch::operator new(size_t s)
|
||||
void * TDStretch::operator new(size_t)
|
||||
{
|
||||
// Notice! don't use "new TDStretch" directly, use "newInstance" to create a new instance instead!
|
||||
ST_THROW_RT_ERROR("Error in TDStretch::new: Don't use 'new TDStretch' directly, use 'newInstance' member instead!");
|
||||
@@ -772,6 +753,7 @@ TDStretch * TDStretch::newInstance()
|
||||
uint uExtensions;
|
||||
|
||||
uExtensions = detectCPUextensions();
|
||||
(void)uExtensions;
|
||||
|
||||
// Check if MMX/SSE instruction set extensions supported by CPU
|
||||
|
||||
@@ -809,7 +791,7 @@ TDStretch * TDStretch::newInstance()
|
||||
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'. The 'Stereo'
|
||||
// version of the routine.
|
||||
void TDStretch::overlapStereo(short *poutput, const short *input) const
|
||||
{
|
||||
@@ -862,8 +844,8 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||
assert(aoverlapMs >= 0);
|
||||
|
||||
// calculate overlap length so that it's power of 2 - thus it's easy to do
|
||||
// integer division by right-shifting. Term "-1" at end is to account for
|
||||
// the extra most significatnt bit left unused in result by signed multiplication
|
||||
// integer division by right-shifting. Term "-1" at end is to account for
|
||||
// the extra most significatnt bit left unused in result by signed multiplication
|
||||
overlapDividerBitsPure = _getClosest2Power((sampleRate * aoverlapMs) / 1000.0) - 1;
|
||||
if (overlapDividerBitsPure > 9) overlapDividerBitsPure = 9;
|
||||
if (overlapDividerBitsPure < 3) overlapDividerBitsPure = 3;
|
||||
@@ -873,8 +855,8 @@ void TDStretch::calculateOverlapLength(int aoverlapMs)
|
||||
|
||||
overlapDividerBitsNorm = overlapDividerBitsPure;
|
||||
|
||||
// calculate sloping divider so that crosscorrelation operation won't
|
||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||
// calculate sloping divider so that crosscorrelation operation won't
|
||||
// overflow 32-bit register. Max. sum of the crosscorrelation sum without
|
||||
// divider would be 2^30*(N^3-N)/3, where N = overlap length
|
||||
slopingDivider = (newOvl * newOvl - 1) / 3;
|
||||
}
|
||||
@@ -898,9 +880,9 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||
// Same routine for stereo and mono
|
||||
for (i = 0; i < ilength; i += 2)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||
lnorm += (mixingPos[i] * mixingPos[i] +
|
||||
mixingPos[i + 1] * mixingPos[i + 1]) >> overlapDividerBitsNorm;
|
||||
// do intermediate scalings to avoid integer overflow
|
||||
}
|
||||
@@ -914,7 +896,7 @@ double TDStretch::calcCrossCorr(const short *mixingPos, const short *compare, do
|
||||
maxnorm = lnorm;
|
||||
}
|
||||
}
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
norm = (double)lnorm;
|
||||
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||
@@ -940,9 +922,9 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||
|
||||
corr = 0;
|
||||
// Same routine for stereo and mono.
|
||||
for (i = 0; i < ilength; i += 2)
|
||||
for (i = 0; i < ilength; i += 2)
|
||||
{
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
corr += (mixingPos[i] * compare[i] +
|
||||
mixingPos[i + 1] * compare[i + 1]) >> overlapDividerBitsNorm;
|
||||
}
|
||||
|
||||
@@ -959,7 +941,7 @@ double TDStretch::calcCrossCorrAccumulate(const short *mixingPos, const short *c
|
||||
maxnorm = (unsigned long)norm;
|
||||
}
|
||||
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
return (double)corr / sqrt((norm < 1e-9) ? 1.0 : norm);
|
||||
}
|
||||
@@ -986,7 +968,7 @@ void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
||||
f1 = 0;
|
||||
f2 = 1.0f;
|
||||
|
||||
for (i = 0; i < 2 * (int)overlapLength ; i += 2)
|
||||
for (i = 0; i < 2 * (int)overlapLength ; i += 2)
|
||||
{
|
||||
pOutput[i + 0] = pInput[i + 0] * f1 + pMidBuffer[i + 0] * f2;
|
||||
pOutput[i + 1] = pInput[i + 1] * f1 + pMidBuffer[i + 1] * f2;
|
||||
@@ -997,7 +979,7 @@ void TDStretch::overlapStereo(float *pOutput, const float *pInput) const
|
||||
}
|
||||
|
||||
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'.
|
||||
// Overlaps samples in 'midBuffer' with the samples in 'input'.
|
||||
void TDStretch::overlapMulti(float *pOutput, const float *pInput) const
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
///
|
||||
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
|
||||
/// while maintaining the original pitch by using a time domain WSOLA-like method
|
||||
/// with several performance-increasing tweaks.
|
||||
///
|
||||
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
|
||||
/// Note : MMX/SSE optimized functions reside in separate, platform-specific files
|
||||
/// 'mmx_optimized.cpp' and 'sse_optimized.cpp'
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -46,14 +46,14 @@ namespace soundtouch
|
||||
{
|
||||
|
||||
/// Default values for sound processing parameters:
|
||||
/// Notice that the default parameters are tuned for contemporary popular music
|
||||
/// Notice that the default parameters are tuned for contemporary popular music
|
||||
/// processing. For speech processing applications these parameters suit better:
|
||||
/// #define DEFAULT_SEQUENCE_MS 40
|
||||
/// #define DEFAULT_SEEKWINDOW_MS 15
|
||||
/// #define DEFAULT_OVERLAP_MS 8
|
||||
///
|
||||
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// Default length of a single processing sequence, in milliseconds. This determines to how
|
||||
/// long sequences the original sound is chopped in the time-stretch algorithm.
|
||||
///
|
||||
/// The larger this value is, the lesser sequences are used in processing. In principle
|
||||
@@ -68,15 +68,15 @@ namespace soundtouch
|
||||
/// according to tempo setting (recommended)
|
||||
#define USE_AUTO_SEQUENCE_LEN 0
|
||||
|
||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
||||
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||
/// optimal joining location when mixing the sound sequences back together.
|
||||
/// Seeking window default length in milliseconds for algorithm that finds the best possible
|
||||
/// overlapping location. This determines from how wide window the algorithm may look for an
|
||||
/// optimal joining location when mixing the sound sequences back together.
|
||||
///
|
||||
/// The bigger this window setting is, the higher the possibility to find a better mixing
|
||||
/// position will become, but at the same time large values may cause a "drifting" artifact
|
||||
/// because consequent sequences will be taken at more uneven intervals.
|
||||
///
|
||||
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||
/// If there's a disturbing artifact that sounds as if a constant frequency was drifting
|
||||
/// around, try reducing this setting.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
@@ -87,11 +87,11 @@ namespace soundtouch
|
||||
/// according to tempo setting (recommended)
|
||||
#define USE_AUTO_SEEKWINDOW_LEN 0
|
||||
|
||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
||||
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||
/// consecutive sequences are let to overlap each other.
|
||||
/// Overlap length in milliseconds. When the chopped sound sequences are mixed back together,
|
||||
/// to form a continuous sound stream, this parameter defines over how long period the two
|
||||
/// consecutive sequences are let to overlap each other.
|
||||
///
|
||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
||||
/// This shouldn't be that critical parameter. If you reduce the DEFAULT_SEQUENCE_MS setting
|
||||
/// by a large amount, you might wish to try a smaller value on this.
|
||||
///
|
||||
/// Increasing this value increases computational burden & vice versa.
|
||||
@@ -162,27 +162,27 @@ protected:
|
||||
/// The maximum amount of samples that can be returned at a time is set by
|
||||
/// the 'set_returnBuffer_size' function.
|
||||
void processSamples();
|
||||
|
||||
|
||||
public:
|
||||
TDStretch();
|
||||
virtual ~TDStretch() override;
|
||||
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// Operator 'new' is overloaded so that it automatically creates a suitable instance
|
||||
/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
|
||||
static void *operator new(size_t s);
|
||||
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// Use this function instead of "new" operator to create a new instance of this class.
|
||||
/// This function automatically chooses a correct feature set depending on if the CPU
|
||||
/// supports MMX/SSE/etc extensions.
|
||||
static TDStretch *newInstance();
|
||||
|
||||
|
||||
/// Returns the output buffer object
|
||||
FIFOSamplePipe *getOutput() { return &outputBuffer; };
|
||||
|
||||
/// Returns the input buffer object
|
||||
FIFOSamplePipe *getInput() { return &inputBuffer; };
|
||||
|
||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
|
||||
/// tempo, larger faster tempo.
|
||||
void setTempo(double newTempo);
|
||||
|
||||
@@ -195,7 +195,7 @@ public:
|
||||
/// Sets the number of channels, 1 = mono, 2 = stereo
|
||||
void setChannels(int numChannels);
|
||||
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// Enables/disables the quick position seeking algorithm. Zero to disable,
|
||||
/// nonzero to enable
|
||||
void enableQuickSeek(bool enable);
|
||||
|
||||
@@ -207,7 +207,7 @@ public:
|
||||
//
|
||||
/// 'sampleRate' = sample rate of the sound
|
||||
/// 'sequenceMS' = one processing sequence length in milliseconds
|
||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// 'seekwindowMS' = seeking window length for scanning the best overlapping
|
||||
/// position
|
||||
/// 'overlapMS' = overlapping length
|
||||
void setParameters(int sampleRate, ///< Samplerate of sound being processed (Hz)
|
||||
@@ -217,7 +217,7 @@ public:
|
||||
);
|
||||
|
||||
/// Get routine control parameters, see setParameters() function.
|
||||
/// Any of the parameters to this function can be NULL, in such case corresponding parameter
|
||||
/// Any of the parameters to this function can be nullptr, in such case corresponding parameter
|
||||
/// value isn't returned.
|
||||
void getParameters(int *pSampleRate, int *pSequenceMs, int *pSeekWindowMs, int *pOverlapMs) const;
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
///
|
||||
/// A header file for detecting the Intel MMX instructions set extension.
|
||||
///
|
||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||
/// Please see 'mmx_win.cpp', 'mmx_cpp.cpp' and 'mmx_non_x86.cpp' for the
|
||||
/// routine implementations for x86 Windows, x86 gnu version and non-x86
|
||||
/// platforms, respectively.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
///
|
||||
/// Generic version of the x86 CPU extension detection routine.
|
||||
///
|
||||
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
|
||||
/// This file is for GNU & other non-Windows compilers, see 'cpu_detect_x86_win.cpp'
|
||||
/// for the Microsoft compiler version.
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -86,9 +86,9 @@ uint detectCPUextensions(void)
|
||||
&& defined(SOUNDTOUCH_ALLOW_X86_OPTIMIZATIONS)
|
||||
|
||||
if (_dwDisabledISA == 0xffffffff) return 0;
|
||||
|
||||
|
||||
uint res = 0;
|
||||
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// GCC version of cpuid. Requires GCC 4.3.0 or later for __cpuid intrinsic support.
|
||||
uint eax, ebx, ecx, edx; // unsigned int is the standard type. uint is defined by the compiler and not guaranteed to be portable.
|
||||
@@ -101,7 +101,7 @@ uint detectCPUextensions(void)
|
||||
if (edx & bit_SSE2) res = res | SUPPORT_SSE2;
|
||||
|
||||
#else
|
||||
// Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
|
||||
// Window / VS version of cpuid. Notice that Visual Studio 2005 or later required
|
||||
// for __cpuid intrinsic support.
|
||||
int reg[4] = {-1};
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// MMX optimized routines. All MMX optimized functions have been gathered into
|
||||
/// this single source code file, regardless to their class or original source
|
||||
/// code file, in order to ease porting the library to other compiler and
|
||||
/// processor platforms.
|
||||
///
|
||||
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
|
||||
/// is available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||
@@ -68,14 +68,14 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||
__m64 accu, normaccu;
|
||||
long corr, norm;
|
||||
int i;
|
||||
|
||||
|
||||
pVec1 = (__m64*)pV1;
|
||||
pVec2 = (__m64*)pV2;
|
||||
|
||||
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||
normaccu = accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||
// during each round for improved CPU-level parallellization.
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
@@ -126,7 +126,7 @@ double TDStretchMMX::calcCrossCorr(const short *pV1, const short *pV2, double &d
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
dnorm = (double)norm;
|
||||
|
||||
@@ -144,7 +144,7 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||
__m64 accu;
|
||||
long corr, lnorm;
|
||||
int i;
|
||||
|
||||
|
||||
// cancel first normalizer tap from previous round
|
||||
lnorm = 0;
|
||||
for (i = 1; i <= channels; i ++)
|
||||
@@ -158,7 +158,7 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||
shifter = _m_from_int(overlapDividerBitsNorm);
|
||||
accu = _mm_setzero_si64();
|
||||
|
||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||
// Process 4 parallel sets of 2 * stereo samples or 4 * mono samples
|
||||
// during each round for improved CPU-level parallellization.
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
@@ -203,7 +203,7 @@ double TDStretchMMX::calcCrossCorrAccumulate(const short *pV1, const short *pV2,
|
||||
maxnorm = lnorm;
|
||||
}
|
||||
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// Normalize result by dividing by sqrt(norm) - this step is easiest
|
||||
// done using floating point operation
|
||||
return (double)corr / sqrt((dnorm < 1e-9) ? 1.0 : dnorm);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
// mix1 = mixer values for 1st stereo sample
|
||||
// mix1 = mixer values for 2nd stereo sample
|
||||
// adder = adder for updating mixer values after each round
|
||||
|
||||
|
||||
mix1 = _mm_set_pi16(0, overlapLength, 0, overlapLength);
|
||||
adder = _mm_set_pi16(1, -1, 1, -1);
|
||||
mix2 = _mm_add_pi16(mix1, adder);
|
||||
@@ -245,7 +245,7 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
for (i = 0; i < overlapLength / 4; i ++)
|
||||
{
|
||||
__m64 temp1, temp2;
|
||||
|
||||
|
||||
// load & shuffle data so that input & mixbuffer data samples are paired
|
||||
temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]); // = i0l m0l i0r m0r
|
||||
temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]); // = i1l m1l i1r m1r
|
||||
@@ -294,8 +294,8 @@ void TDStretchMMX::overlapStereo(short *output, const short *input) const
|
||||
|
||||
FIRFilterMMX::FIRFilterMMX() : FIRFilter()
|
||||
{
|
||||
filterCoeffsAlign = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
filterCoeffsAlign = nullptr;
|
||||
filterCoeffsUnalign = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -316,8 +316,8 @@ void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uRe
|
||||
filterCoeffsUnalign = new short[2 * newLength + 8];
|
||||
filterCoeffsAlign = (short *)SOUNDTOUCH_ALIGN_POINTER_16(filterCoeffsUnalign);
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0;i < length; i += 4)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
|
||||
filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// SSE optimized routines for Pentium-III, Athlon-XP and later CPUs. All SSE
|
||||
/// optimized functions have been gathered into this single source
|
||||
/// code file, regardless to their class or original source code file, in order
|
||||
/// to ease porting the library to other compiler and processor platforms.
|
||||
///
|
||||
/// The SSE-optimizations are programmed using SSE compiler intrinsics that
|
||||
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
|
||||
/// should compile with both toolsets.
|
||||
///
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
|
||||
/// 6.0 processor pack" update to support SSE instruction set. The update is
|
||||
/// available for download at Microsoft Developers Network, see here:
|
||||
/// http://msdn.microsoft.com/en-us/vstudio/aa718349.aspx
|
||||
///
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// If the above URL is expired or removed, go to "http://msdn.microsoft.com" and
|
||||
/// perform a search with keywords "processor pack".
|
||||
///
|
||||
/// Author : Copyright (c) Olli Parviainen
|
||||
@@ -51,7 +51,7 @@ using namespace soundtouch;
|
||||
|
||||
#ifdef SOUNDTOUCH_ALLOW_SSE
|
||||
|
||||
// SSE routines available only with float sample type
|
||||
// SSE routines available only with float sample type
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -71,8 +71,8 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||
const __m128 *pVec2;
|
||||
__m128 vSum, vNorm;
|
||||
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
// Note. It means a major slow-down if the routine needs to tolerate
|
||||
// unaligned __m128 memory accesses. It's way faster if we can skip
|
||||
// unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps.
|
||||
// This can mean up to ~ 10-fold difference (incl. part of which is
|
||||
// due to skipping every second round for stereo sound though).
|
||||
@@ -81,7 +81,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||
// for choosing if this little cheating is allowed.
|
||||
|
||||
#ifdef ST_SIMD_AVOID_UNALIGNED
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
// Little cheating allowed, return valid correlation only for
|
||||
// aligned locations, meaning every second round for stereo sound.
|
||||
|
||||
#define _MM_LOAD _mm_load_ps
|
||||
@@ -92,7 +92,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||
// No cheating allowed, use unaligned load & take the resulting
|
||||
// performance hit.
|
||||
#define _MM_LOAD _mm_loadu_ps
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ensure overlapLength is divisible by 8
|
||||
assert((overlapLength % 8) == 0);
|
||||
@@ -105,7 +105,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||
|
||||
// Unroll the loop by factor of 4 * 4 operations. Use same routine for
|
||||
// stereo & mono, for mono it just means twice the amount of unrolling.
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
__m128 vTemp;
|
||||
// vSum += pV1[0..3] * pV2[0..3]
|
||||
@@ -146,7 +146,7 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||
|
||||
// Calculates the cross-correlation value between 'pV1' and 'pV2' vectors
|
||||
corr = norm = 0.0;
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
for (i = 0; i < channels * overlapLength / 16; i ++)
|
||||
{
|
||||
corr += pV1[0] * pV2[0] +
|
||||
pV1[1] * pV2[1] +
|
||||
@@ -178,8 +178,8 @@ double TDStretchSSE::calcCrossCorr(const float *pV1, const float *pV2, double &a
|
||||
|
||||
double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2, double &norm)
|
||||
{
|
||||
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||
// call usual calcCrossCorr function because SSE does not show big benefit of
|
||||
// accumulating "norm" value, and also the "norm" rolling algorithm would get
|
||||
// complicated due to SSE-specific alignment-vs-nonexact correlation rules.
|
||||
return calcCrossCorr(pV1, pV2, norm);
|
||||
}
|
||||
@@ -195,16 +195,16 @@ double TDStretchSSE::calcCrossCorrAccumulate(const float *pV1, const float *pV2,
|
||||
|
||||
FIRFilterSSE::FIRFilterSSE() : FIRFilter()
|
||||
{
|
||||
filterCoeffsAlign = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
filterCoeffsAlign = nullptr;
|
||||
filterCoeffsUnalign = nullptr;
|
||||
}
|
||||
|
||||
|
||||
FIRFilterSSE::~FIRFilterSSE()
|
||||
{
|
||||
delete[] filterCoeffsUnalign;
|
||||
filterCoeffsAlign = NULL;
|
||||
filterCoeffsUnalign = NULL;
|
||||
filterCoeffsAlign = nullptr;
|
||||
filterCoeffsUnalign = nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ void FIRFilterSSE::setCoefficients(const float *coeffs, uint newLength, uint uRe
|
||||
|
||||
fDivider = (float)resultDivider;
|
||||
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
// rearrange the filter coefficients for mmx routines
|
||||
for (i = 0; i < newLength; i ++)
|
||||
{
|
||||
filterCoeffsAlign[2 * i + 0] =
|
||||
@@ -245,10 +245,10 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||
|
||||
if (count < 2) return 0;
|
||||
|
||||
assert(source != NULL);
|
||||
assert(dest != NULL);
|
||||
assert(source != nullptr);
|
||||
assert(dest != nullptr);
|
||||
assert((length % 8) == 0);
|
||||
assert(filterCoeffsAlign != NULL);
|
||||
assert(filterCoeffsAlign != nullptr);
|
||||
assert(((ulongptr)filterCoeffsAlign) % 16 == 0);
|
||||
|
||||
// filter is evaluated for two stereo samples with each iteration, thus use of 'j += 2'
|
||||
@@ -263,13 +263,13 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||
|
||||
pSrc = (const float*)source + j * 2; // source audio data
|
||||
pDest = dest + j * 2; // destination audio data
|
||||
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
pFil = (const __m128*)filterCoeffsAlign; // filter coefficients. NOTE: Assumes coefficients
|
||||
// are aligned to 16-byte boundary
|
||||
sum1 = sum2 = _mm_setzero_ps();
|
||||
|
||||
for (i = 0; i < length / 8; i ++)
|
||||
for (i = 0; i < length / 8; i ++)
|
||||
{
|
||||
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
||||
// Unroll loop for efficiency & calculate filter for 2*2 stereo samples
|
||||
// at each pass
|
||||
|
||||
// sum1 is accu for 2*2 filtered stereo sound data at the primary sound data offset
|
||||
@@ -302,14 +302,14 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||
}
|
||||
|
||||
// Ideas for further improvement:
|
||||
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
||||
// 1. If it could be guaranteed that 'source' were always aligned to 16-byte
|
||||
// boundary, a faster aligned '_mm_load_ps' instruction could be used.
|
||||
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
||||
// 2. If it could be guaranteed that 'dest' were always aligned to 16-byte
|
||||
// boundary, a faster '_mm_store_ps' instruction could be used.
|
||||
|
||||
return (uint)count;
|
||||
|
||||
/* original routine in C-language. please notice the C-version has differently
|
||||
/* original routine in C-language. please notice the C-version has differently
|
||||
organized coefficients though.
|
||||
double suml1, suml2;
|
||||
double sumr1, sumr2;
|
||||
@@ -324,26 +324,26 @@ uint FIRFilterSSE::evaluateFilterStereo(float *dest, const float *source, uint n
|
||||
suml2 = sumr2 = 0.0;
|
||||
ptr = src;
|
||||
pFil = filterCoeffs;
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
for (i = 0; i < lengthLocal; i ++)
|
||||
{
|
||||
// unroll loop for efficiency.
|
||||
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
suml1 += ptr[0] * pFil[0] +
|
||||
ptr[2] * pFil[2] +
|
||||
ptr[4] * pFil[4] +
|
||||
ptr[6] * pFil[6];
|
||||
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
sumr1 += ptr[1] * pFil[1] +
|
||||
ptr[3] * pFil[3] +
|
||||
ptr[5] * pFil[5] +
|
||||
ptr[7] * pFil[7];
|
||||
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
suml2 += ptr[8] * pFil[0] +
|
||||
ptr[10] * pFil[2] +
|
||||
ptr[12] * pFil[4] +
|
||||
ptr[14] * pFil[6];
|
||||
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
sumr2 += ptr[9] * pFil[1] +
|
||||
ptr[11] * pFil[3] +
|
||||
ptr[13] * pFil[5] +
|
||||
ptr[15] * pFil[7];
|
||||
|
||||
@@ -60,6 +60,14 @@ if(ENABLE_TESTS)
|
||||
add_subdirectory(tests/ctest)
|
||||
endif()
|
||||
|
||||
# gsrunner
|
||||
if(ENABLE_GSRUNNER)
|
||||
if (NOT WIN32)
|
||||
message(WARNING "GSRunner is only supported on Windows and may not build on your system")
|
||||
endif()
|
||||
add_subdirectory(pcsx2-gsrunner)
|
||||
endif()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
if(NOT IS_SUPPORTED_COMPILER)
|
||||
message(WARNING "
|
||||
|
||||
165
COPYING.LGPLv3
165
COPYING.LGPLv3
@@ -1,165 +0,0 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@
|
||||
|
||||
# Windows
|
||||
03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,
|
||||
03000000fa190000918d000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,
|
||||
03000000fa2d00000100000000000000,3dRudder Foot Motion Controller,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows,
|
||||
03000000d0160000040d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,
|
||||
03000000d0160000050d000000000000,4Play Adapter,a:b1,b:b3,back:b4,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,leftstick:b14,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b15,righttrigger:b9,rightx:a3,righty:a4,start:b5,x:b0,y:b2,platform:Windows,
|
||||
@@ -115,6 +116,7 @@
|
||||
03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
|
||||
03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows,
|
||||
03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
|
||||
03000000380800001889000000000000,AtGames Legends Gamer Pro,a:b1,b:b2,x:b0,y:b3,start:b9,leftshoulder:b13,rightshoulder:b5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,lefttrigger:b14,righttrigger:b7,platform:Windows,
|
||||
030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
|
||||
030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
|
||||
030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
|
||||
@@ -484,7 +486,7 @@
|
||||
03000000f0250000c183000000000000,PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000d9040000160f000000000000,PlayStation Controller Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
|
||||
030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000d620000011a7000000000000,PowerA Core Plus GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
|
||||
03000000d620000011a7000000000000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000dd62000015a7000000000000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000d620000012a7000000000000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
03000000dd62000016a7000000000000,PowerA Fusion Pro Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
|
||||
@@ -873,7 +875,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
|
||||
03000000050b00000579000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b42,paddle1:b9,paddle2:b11,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
|
||||
03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:MacOSX
|
||||
03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
|
||||
03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
|
||||
03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
|
||||
@@ -1081,7 +1083,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000005e040000d102000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000dd02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000e002000000000000,Xbox One Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
030000005e040000e002000003090000,Xbox One Controller,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b10,start:b7,leftstick:b8,rightstick:b9,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Mac OS X,
|
||||
030000005e040000e302000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000ea02000000000000,Xbox One Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
|
||||
030000005e040000fd02000003090000,Xbox One Controller,a:b0,b:b1,back:b16,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
|
||||
@@ -1170,7 +1172,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
05000000a00500003232000001000000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
|
||||
05000000a00500003232000008010000,8BitDo Zero,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Linux,
|
||||
03000000c82d00001890000011010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Linux,
|
||||
050000005e040000e002000030110000,8BitDo Zero 2,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
|
||||
05000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
|
||||
03000000c01100000355000011010000,Acrux Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
030000006f0e00008801000011010000,Afterglow Deluxe Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
@@ -1390,7 +1391,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000120b000001050000,Microsoft Xbox Series X Controller,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b8,leftshoulder:b4,rightshoulder:b5,leftstick:b9,rightstick:b10,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,platform:Linux,
|
||||
060000005e040000120b000001050000,Microsoft Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
|
||||
03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
@@ -1468,7 +1469,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
03000000d620000011a7000011010000,PowerA Core Plus Gamecube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000d620000011a7000011010000,PowerA Core Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000dd62000015a7000011010000,PowerA Fusion Nintendo Switch Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000d620000012a7000011010000,PowerA Fusion Nintendo Switch Fight Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000d62000000140000001010000,PowerA Fusion Pro 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
@@ -1524,6 +1525,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
03000000300f00001211000011010000,Qanba Arcade Joystick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux,
|
||||
03000000222c00000225000011010000,Qanba Dragon Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000222c00000025000011010000,Qanba Dragon Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
03000000222c00001220000011010000,Qanba Drone 2 Arcade Joystick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000222c00001020000011010000,Qanba Drone 2 Arcade Joystick (PS5),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
|
||||
03000000222c00000020000011010000,Qanba Drone Arcade PS4 Joystick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
|
||||
03000000300f00001210000010010000,Qanba Joystick Plus,a:b0,b:b1,back:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,start:b9,x:b2,y:b3,platform:Linux,
|
||||
03000000222c00000223000011010000,Qanba Obsidian Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
|
||||
@@ -1690,7 +1693,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
|
||||
050000005e040000220b000013050000,Xbox One Elite 2 Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
050000005e040000050b000002090000,Xbox One Elite Series 2,a:b0,b:b1,back:b136,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
|
||||
030000005e040000ea02000011050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
050082795e040000e002000003090000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000ea0200000b050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
060000005e040000ea0200000d050000,Xbox One S Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
030000005e040000120b000001050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifndef FXAA_HLSL
|
||||
#define FXAA_HLSL 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
cbuffer vertexBuffer : register(b0)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
Texture2D Texture;
|
||||
SamplerState Sampler;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
Texture2D Texture;
|
||||
SamplerState Sampler;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
struct VS_INPUT
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
Texture2D Texture;
|
||||
SamplerState Sampler;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#define FMT_32 0
|
||||
#define FMT_24 1
|
||||
@@ -772,17 +772,17 @@ float4 ps_color(PS_INPUT input)
|
||||
uint4 denorm_c_before = uint4(T);
|
||||
if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||
{
|
||||
T.r = float((denorm_c_before.b << 3) & 0xF8);
|
||||
T.g = float(((denorm_c_before.b >> 2) & 0x38) | ((denorm_c_before.a << 6) & 0xC0));
|
||||
T.b = float((denorm_c_before.a << 1) & 0xF8);
|
||||
T.a = float(denorm_c_before.a & 0x80);
|
||||
T.r = float((denorm_c_before.b << 3) & 0xF8u);
|
||||
T.g = float(((denorm_c_before.b >> 2) & 0x38u) | ((denorm_c_before.a << 6) & 0xC0u));
|
||||
T.b = float((denorm_c_before.a << 1) & 0xF8u);
|
||||
T.a = float(denorm_c_before.a & 0x80u);
|
||||
}
|
||||
else
|
||||
{
|
||||
T.r = float((denorm_c_before.r << 3) & 0xF8);
|
||||
T.g = float(((denorm_c_before.r >> 2) & 0x38) | ((denorm_c_before.g << 6) & 0xC0));
|
||||
T.b = float((denorm_c_before.g << 1) & 0xF8);
|
||||
T.a = float(denorm_c_before.g & 0x80);
|
||||
T.r = float((denorm_c_before.r << 3) & 0xF8u);
|
||||
T.g = float(((denorm_c_before.r >> 2) & 0x38u) | ((denorm_c_before.g << 6) & 0xC0u));
|
||||
T.b = float((denorm_c_before.g << 1) & 0xF8u);
|
||||
T.a = float(denorm_c_before.g & 0x80u);
|
||||
}
|
||||
|
||||
T.a = (T.a >= 127.5f ? TA.y : !PS_AEM || any(int3(T.rgb) & 0xF8) ? TA.x : 0) * 255.0f;
|
||||
@@ -862,9 +862,15 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
// PABE
|
||||
if (PS_PABE)
|
||||
{
|
||||
// As_rgba needed for accumulation blend to manipulate Cd.
|
||||
// No blending so early exit
|
||||
if (As < 1.0f)
|
||||
{
|
||||
As_rgba.rgb = (float3)0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
As_rgba.rgb = (float3)1.0f;
|
||||
}
|
||||
|
||||
float4 RT = SW_BLEND_NEEDS_RT ? RtTexture.Load(int3(pos_xy, 0)) : (float4)0.0f;
|
||||
@@ -874,17 +880,17 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
|
||||
uint4 denorm_rt = uint4(RT);
|
||||
if (PS_PROCESS_BA & SHUFFLE_WRITE)
|
||||
{
|
||||
RT.r = float((denorm_rt.b << 3) & 0xF8);
|
||||
RT.g = float(((denorm_rt.b >> 2) & 0x38) | ((denorm_rt.a << 6) & 0xC0));
|
||||
RT.b = float((denorm_rt.a << 1) & 0xF8);
|
||||
RT.a = float(denorm_rt.a & 0x80);
|
||||
RT.r = float((denorm_rt.b << 3) & 0xF8u);
|
||||
RT.g = float(((denorm_rt.b >> 2) & 0x38u) | ((denorm_rt.a << 6) & 0xC0u));
|
||||
RT.b = float((denorm_rt.a << 1) & 0xF8u);
|
||||
RT.a = float(denorm_rt.a & 0x80u);
|
||||
}
|
||||
else
|
||||
{
|
||||
RT.r = float((denorm_rt.r << 3) & 0xF8);
|
||||
RT.g = float(((denorm_rt.r >> 2) & 0x38) | ((denorm_rt.g << 6) & 0xC0));
|
||||
RT.b = float((denorm_rt.g << 1) & 0xF8);
|
||||
RT.a = float(denorm_rt.g & 0x80);
|
||||
RT.r = float((denorm_rt.r << 3) & 0xF8u);
|
||||
RT.g = float(((denorm_rt.r >> 2) & 0x38u) | ((denorm_rt.g << 6) & 0xC0u));
|
||||
RT.b = float((denorm_rt.g << 1) & 0xF8u);
|
||||
RT.a = float(denorm_rt.g & 0x80u);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1067,13 +1073,13 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
uint4 denorm_c_after = uint4(C);
|
||||
if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||
{
|
||||
C.b = float(((denorm_c_after.r >> 3) & 0x1F) | ((denorm_c_after.g << 2) & 0xE0));
|
||||
C.a = float(((denorm_c_after.g >> 6) & 0x3) | ((denorm_c_after.b >> 1) & 0x7C) | (denorm_c_after.a & 0x80));
|
||||
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||
C.a = float(((denorm_c_after.g >> 6) & 0x3u) | ((denorm_c_after.b >> 1) & 0x7Cu) | (denorm_c_after.a & 0x80u));
|
||||
}
|
||||
else
|
||||
{
|
||||
C.r = float(((denorm_c_after.r >> 3) & 0x1F) | ((denorm_c_after.g << 2) & 0xE0));
|
||||
C.g = float(((denorm_c_after.g >> 6) & 0x3) | ((denorm_c_after.b >> 1) & 0x7C) | (denorm_c_after.a & 0x80));
|
||||
C.r = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||
C.g = float(((denorm_c_after.g >> 6) & 0x3u) | ((denorm_c_after.b >> 1) & 0x7Cu) | (denorm_c_after.a & 0x80u));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for text editor detection
|
||||
|
||||
@@ -681,15 +681,15 @@ vec4 ps_color()
|
||||
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME
|
||||
uvec4 denorm_c_before = uvec4(T);
|
||||
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||
T.r = float((denorm_c_before.b << 3) & 0xF8);
|
||||
T.g = float(((denorm_c_before.b >> 2) & 0x38) | ((denorm_c_before.a << 6) & 0xC0));
|
||||
T.b = float((denorm_c_before.a << 1) & 0xF8);
|
||||
T.a = float(denorm_c_before.a & 0x80);
|
||||
T.r = float((denorm_c_before.b << 3) & 0xF8u);
|
||||
T.g = float(((denorm_c_before.b >> 2) & 0x38u) | ((denorm_c_before.a << 6) & 0xC0u));
|
||||
T.b = float((denorm_c_before.a << 1) & 0xF8u);
|
||||
T.a = float(denorm_c_before.a & 0x80u);
|
||||
#else
|
||||
T.r = float((denorm_c_before.r << 3) & 0xF8);
|
||||
T.g = float(((denorm_c_before.r >> 2) & 0x38) | ((denorm_c_before.g << 6) & 0xC0));
|
||||
T.b = float((denorm_c_before.g << 1) & 0xF8);
|
||||
T.a = float(denorm_c_before.g & 0x80);
|
||||
T.r = float((denorm_c_before.r << 3) & 0xF8u);
|
||||
T.g = float(((denorm_c_before.r >> 2) & 0x38u) | ((denorm_c_before.g << 6) & 0xC0u));
|
||||
T.b = float((denorm_c_before.g << 1) & 0xF8u);
|
||||
T.a = float(denorm_c_before.g & 0x80u);
|
||||
#endif
|
||||
|
||||
T.a = ((T.a >= 127.5f) ? TA.y : ((PS_AEM == 0 || any(bvec3(ivec3(T.rgb) & ivec3(0xF8)))) ? TA.x : 0.0f)) * 255.0f;
|
||||
@@ -783,9 +783,15 @@ float As = As_rgba.a;
|
||||
|
||||
// PABE
|
||||
#if PS_PABE
|
||||
// As_rgba needed for accumulation blend to manipulate Cd.
|
||||
// No blending so early exit
|
||||
if (As < 1.0f)
|
||||
{
|
||||
As_rgba.rgb = vec3(0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
As_rgba.rgb = vec3(1.0f);
|
||||
#endif
|
||||
|
||||
#if SW_BLEND_NEEDS_RT
|
||||
@@ -804,15 +810,15 @@ float As = As_rgba.a;
|
||||
#if PS_SHUFFLE && SW_BLEND_NEEDS_RT
|
||||
uvec4 denorm_rt = uvec4(RT);
|
||||
#if (PS_PROCESS_BA & SHUFFLE_WRITE)
|
||||
RT.r = float((denorm_rt.b << 3) & 0xF8);
|
||||
RT.g = float(((denorm_rt.b >> 2) & 0x38) | ((denorm_rt.a << 6) & 0xC0));
|
||||
RT.b = float((denorm_rt.a << 1) & 0xF8);
|
||||
RT.a = float(denorm_rt.a & 0x80);
|
||||
RT.r = float((denorm_rt.b << 3) & 0xF8u);
|
||||
RT.g = float(((denorm_rt.b >> 2) & 0x38u) | ((denorm_rt.a << 6) & 0xC0u));
|
||||
RT.b = float((denorm_rt.a << 1) & 0xF8u);
|
||||
RT.a = float(denorm_rt.a & 0x80u);
|
||||
#else
|
||||
RT.r = float((denorm_rt.r << 3) & 0xF8);
|
||||
RT.g = float(((denorm_rt.r >> 2) & 0x38) | ((denorm_rt.g << 6) & 0xC0));
|
||||
RT.b = float((denorm_rt.g << 1) & 0xF8);
|
||||
RT.a = float(denorm_rt.g & 0x80);
|
||||
RT.r = float((denorm_rt.r << 3) & 0xF8u);
|
||||
RT.g = float(((denorm_rt.r >> 2) & 0x38u) | ((denorm_rt.g << 6) & 0xC0u));
|
||||
RT.b = float((denorm_rt.g << 1) & 0xF8u);
|
||||
RT.a = float(denorm_rt.g & 0x80u);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1046,11 +1052,11 @@ void ps_main()
|
||||
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME
|
||||
uvec4 denorm_c_after = uvec4(C);
|
||||
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||
C.b = float(((denorm_c_after.r >> 3) & 0x1F) | ((denorm_c_after.g << 2) & 0xE0));
|
||||
C.a = float(((denorm_c_after.g >> 6) & 0x3) | ((denorm_c_after.b >> 1) & 0x7C) | (denorm_c_after.a & 0x80));
|
||||
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||
C.a = float(((denorm_c_after.g >> 6) & 0x3u) | ((denorm_c_after.b >> 1) & 0x7Cu) | (denorm_c_after.a & 0x80u));
|
||||
#else
|
||||
C.r = float(((denorm_c_after.r >> 3) & 0x1F) | ((denorm_c_after.g << 2) & 0xE0));
|
||||
C.g = float(((denorm_c_after.g >> 6) & 0x3) | ((denorm_c_after.b >> 1) & 0x7C) | (denorm_c_after.a & 0x80));
|
||||
C.r = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||
C.g = float(((denorm_c_after.g >> 6) & 0x3u) | ((denorm_c_after.b >> 1) & 0x7Cu) | (denorm_c_after.a & 0x80u));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for text editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#ifdef VERTEX_SHADER
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//#version 420 // Keep it for editor detection
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Vertex Shader
|
||||
@@ -349,7 +349,7 @@ layout(set = 1, binding = 1) uniform texture2D Palette;
|
||||
#endif
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED
|
||||
#if defined(DISABLE_TEXTURE_BARRIER)
|
||||
#if defined(DISABLE_TEXTURE_BARRIER) || defined(HAS_FEEDBACK_LOOP_LAYOUT)
|
||||
layout(set = 1, binding = 2) uniform texture2D RtSampler;
|
||||
vec4 sample_from_rt() { return texelFetch(RtSampler, ivec2(gl_FragCoord.xy), 0); }
|
||||
#else
|
||||
@@ -948,15 +948,15 @@ vec4 ps_color()
|
||||
#if PS_SHUFFLE && !PS_READ16_SRC && !PS_SHUFFLE_SAME
|
||||
uvec4 denorm_c_before = uvec4(T);
|
||||
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||
T.r = float((denorm_c_before.b << 3) & 0xF8);
|
||||
T.g = float(((denorm_c_before.b >> 2) & 0x38) | ((denorm_c_before.a << 6) & 0xC0));
|
||||
T.b = float((denorm_c_before.a << 1) & 0xF8);
|
||||
T.a = float(denorm_c_before.a & 0x80);
|
||||
T.r = float((denorm_c_before.b << 3) & 0xF8u);
|
||||
T.g = float(((denorm_c_before.b >> 2) & 0x38u) | ((denorm_c_before.a << 6) & 0xC0u));
|
||||
T.b = float((denorm_c_before.a << 1) & 0xF8u);
|
||||
T.a = float(denorm_c_before.a & 0x80u);
|
||||
#else
|
||||
T.r = float((denorm_c_before.r << 3) & 0xF8);
|
||||
T.g = float(((denorm_c_before.r >> 2) & 0x38) | ((denorm_c_before.g << 6) & 0xC0));
|
||||
T.b = float((denorm_c_before.g << 1) & 0xF8);
|
||||
T.a = float(denorm_c_before.g & 0x80);
|
||||
T.r = float((denorm_c_before.r << 3) & 0xF8u);
|
||||
T.g = float(((denorm_c_before.r >> 2) & 0x38) | ((denorm_c_before.g << 6) & 0xC0u));
|
||||
T.b = float((denorm_c_before.g << 1) & 0xF8u);
|
||||
T.a = float(denorm_c_before.g & 0x80u);
|
||||
#endif
|
||||
|
||||
T.a = ((T.a >= 127.5f) ? TA.y : ((PS_AEM == 0 || any(bvec3(ivec3(T.rgb) & ivec3(0xF8)))) ? TA.x : 0.0f)) * 255.0f;
|
||||
@@ -1050,9 +1050,15 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
|
||||
|
||||
// PABE
|
||||
#if PS_PABE
|
||||
// As_rgba needed for accumulation blend to manipulate Cd
|
||||
// No blending so early exit
|
||||
if (As < 1.0f)
|
||||
{
|
||||
As_rgba.rgb = vec3(0.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
As_rgba.rgb = vec3(1.0f);
|
||||
#endif
|
||||
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED
|
||||
@@ -1071,15 +1077,15 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
|
||||
#if PS_SHUFFLE && PS_FEEDBACK_LOOP_IS_NEEDED
|
||||
uvec4 denorm_rt = uvec4(RT);
|
||||
#if (PS_PROCESS_BA & SHUFFLE_WRITE)
|
||||
RT.r = float((denorm_rt.b << 3) & 0xF8);
|
||||
RT.g = float(((denorm_rt.b >> 2) & 0x38) | ((denorm_rt.a << 6) & 0xC0));
|
||||
RT.b = float((denorm_rt.a << 1) & 0xF8);
|
||||
RT.a = float(denorm_rt.a & 0x80);
|
||||
RT.r = float((denorm_rt.b << 3) & 0xF8u);
|
||||
RT.g = float(((denorm_rt.b >> 2) & 0x38u) | ((denorm_rt.a << 6) & 0xC0u));
|
||||
RT.b = float((denorm_rt.a << 1) & 0xF8u);
|
||||
RT.a = float(denorm_rt.a & 0x80u);
|
||||
#else
|
||||
RT.r = float((denorm_rt.r << 3) & 0xF8);
|
||||
RT.g = float(((denorm_rt.r >> 2) & 0x38) | ((denorm_rt.g << 6) & 0xC0));
|
||||
RT.b = float((denorm_rt.g << 1) & 0xF8);
|
||||
RT.a = float(denorm_rt.g & 0x80);
|
||||
RT.r = float((denorm_rt.r << 3) & 0xF8u);
|
||||
RT.g = float(((denorm_rt.r >> 2) & 0x38u) | ((denorm_rt.g << 6) & 0xC0u));
|
||||
RT.b = float((denorm_rt.g << 1) & 0xF8u);
|
||||
RT.a = float(denorm_rt.g & 0x80u);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -1312,11 +1318,11 @@ void main()
|
||||
#if !PS_READ16_SRC && !PS_SHUFFLE_SAME
|
||||
uvec4 denorm_c_after = uvec4(C);
|
||||
#if (PS_PROCESS_BA & SHUFFLE_READ)
|
||||
C.b = float(((denorm_c_after.r >> 3) & 0x1F) | ((denorm_c_after.g << 2) & 0xE0));
|
||||
C.a = float(((denorm_c_after.g >> 6) & 0x3) | ((denorm_c_after.b >> 1) & 0x7C) | (denorm_c_after.a & 0x80));
|
||||
C.b = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||
C.a = float(((denorm_c_after.g >> 6) & 0x3u) | ((denorm_c_after.b >> 1) & 0x7Cu) | (denorm_c_after.a & 0x80u));
|
||||
#else
|
||||
C.r = float(((denorm_c_after.r >> 3) & 0x1F) | ((denorm_c_after.g << 2) & 0xE0));
|
||||
C.g = float(((denorm_c_after.g >> 6) & 0x3) | ((denorm_c_after.b >> 1) & 0x7C) | (denorm_c_after.a & 0x80));
|
||||
C.r = float(((denorm_c_after.r >> 3) & 0x1Fu) | ((denorm_c_after.g << 2) & 0xE0u));
|
||||
C.g = float(((denorm_c_after.g >> 6) & 0x3u) | ((denorm_c_after.b >> 1) & 0x7Cu) | (denorm_c_after.a & 0x80u));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# Extra preprocessor definitions that will be added to all pcsx2 builds
|
||||
set(PCSX2_DEFS "")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Misc option
|
||||
#-------------------------------------------------------------------------------
|
||||
option(ENABLE_TESTS "Enables building the unit tests" ON)
|
||||
option(ENABLE_GSRUNNER "Enables building the GSRunner" OFF)
|
||||
option(LTO_PCSX2_CORE "Enable LTO/IPO/LTCG on the subset of pcsx2 that benefits most from it but not anything else")
|
||||
option(USE_VTUNE "Plug VTUNE to profile GS JIT.")
|
||||
option(PACKAGE_MODE "Use this option to ease packaging of PCSX2 (developer/distribution option)")
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Graphical option
|
||||
@@ -23,6 +27,7 @@ if(UNIX AND NOT APPLE)
|
||||
option(ENABLE_SETCAP "Enable networking capability for DEV9" OFF)
|
||||
option(X11_API "Enable X11 support" ON)
|
||||
option(WAYLAND_API "Enable Wayland support" ON)
|
||||
option(USE_BACKTRACE "Enable libbacktrace support" ON)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
@@ -114,6 +119,13 @@ elseif("${CMAKE_HOST_SYSTEM_PROCESSOR}" STREQUAL "arm64" OR "${CMAKE_HOST_SYSTEM
|
||||
detect_cache_line_size()
|
||||
list(APPEND PCSX2_DEFS OVERRIDE_HOST_CACHE_LINE_SIZE=${HOST_CACHE_LINE_SIZE})
|
||||
endif()
|
||||
|
||||
# Windows page/cache line size seems to match x68-64
|
||||
if(WIN32)
|
||||
list(APPEND PCSX2_DEFS OVERRIDE_HOST_PAGE_SIZE=0x1000)
|
||||
# Value of std::hardware_destructive_interference_size for ARM64 on MSVC toolset 14.40.33807
|
||||
list(APPEND PCSX2_DEFS OVERRIDE_HOST_CACHE_LINE_SIZE=64)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
@@ -190,6 +202,15 @@ if(MSVC)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(PACKAGE_MODE)
|
||||
file(RELATIVE_PATH relative_datadir ${CMAKE_INSTALL_FULL_BINDIR} ${CMAKE_INSTALL_FULL_DATADIR}/PCSX2)
|
||||
|
||||
# Compile all source codes with those defines
|
||||
list(APPEND PCSX2_DEFS
|
||||
PCSX2_APP_DATADIR="${relative_datadir}")
|
||||
endif()
|
||||
|
||||
|
||||
if(USE_VTUNE)
|
||||
list(APPEND PCSX2_DEFS ENABLE_VTUNE)
|
||||
endif()
|
||||
|
||||
@@ -65,7 +65,10 @@ else()
|
||||
find_package(Wayland REQUIRED Egl)
|
||||
endif()
|
||||
|
||||
find_package(Libbacktrace REQUIRED)
|
||||
if(USE_BACKTRACE)
|
||||
find_package(Libbacktrace REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(DBUS REQUIRED dbus-1)
|
||||
endif()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
// This module contains implementations of _aligned_malloc for platforms that don't have
|
||||
// it built into their CRT/libc.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user