This commit is contained in:
Armin 2026-07-23 23:40:48 +02:00
commit d21bc831e1
178 changed files with 24136 additions and 0 deletions

15
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,15 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: binyaminf
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help improve Just a Sample
title: ""
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Plugin Version**
Hover your mouse over the plugin logo to show the version
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request
url: https://github.com/BOBONA/Just-a-Sample/discussions/46
about: Please suggest new features here.

320
.github/workflows/build_and_release.yml vendored Normal file
View file

@ -0,0 +1,320 @@
name: Build and Release Just A Sample
on:
workflow_dispatch:
jobs:
get-version:
runs-on: ubuntu-latest
outputs:
full_version: ${{ steps.calc.outputs.FULL_VERSION }}
base_version: ${{ steps.calc.outputs.BASE_VERSION }}
patch: ${{ steps.calc.outputs.PATCH }}
steps:
- uses: actions/checkout@v4
- id: calc
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
BASE_VERSION=$(cmake -DPRINT_VERSION=ON -P version.cmake 2>&1)
RELEASE_DATA=$(gh release view "v${BASE_VERSION}-latest" --json isDraft,assets 2>/dev/null || echo "")
if [ -z "$RELEASE_DATA" ]; then
PATCH=0
else
IS_DRAFT=$(echo "$RELEASE_DATA" | jq -r '.isDraft')
ASSETS=$(echo "$RELEASE_DATA" | jq -r '.assets[].name' 2>/dev/null || echo "")
HIGHEST_PATCH=$(echo "$ASSETS" | grep -oP "(?<=v${BASE_VERSION}\.)\d+" | sort -n | tail -1)
if [ -z "$HIGHEST_PATCH" ]; then
PATCH=0
elif [ "$IS_DRAFT" == "true" ]; then
PATCH=$HIGHEST_PATCH
else
PATCH=$((HIGHEST_PATCH + 1))
fi
fi
FULL_VERSION="${BASE_VERSION}.${PATCH}"
echo "BASE_VERSION=$BASE_VERSION" >> $GITHUB_OUTPUT
echo "FULL_VERSION=$FULL_VERSION" >> $GITHUB_OUTPUT
echo "PATCH=$PATCH" >> $GITHUB_OUTPUT
build-windows:
needs: get-version
runs-on: windows-latest
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Cache .deps, build, and ccache
uses: actions/cache@v5.0.3
with:
path: |
out/build
.deps
~/.ccache
key: ${{ runner.os }}-build-${{ hashFiles('CMakeLists.txt', '**/*.cmake') }}
restore-keys: ${{ runner.os }}-build-
- name: Setup MSVC environment
uses: ilammy/msvc-dev-cmd@v1
- name: Install ccache
run: choco install ccache -y
- name: Configure CMake
run: cmake --preset windows -DJAS_PATCH_INDEX=${{ needs.get-version.outputs.patch }}
- name: Build Release
run: cmake --build --preset release-windows
- name: Restore signing certificate
shell: pwsh
run: |
$cert = "${{ secrets.WINDOWS_PFX_BASE64 }}"
[IO.File]::WriteAllBytes("cert.pfx",[Convert]::FromBase64String($cert))
- name: Sign VST3 binary
run: |
signtool sign `
/f cert.pfx `
/p "${{ secrets.WINDOWS_PFX_PASSWORD }}" `
/fd SHA256 `
/tr http://timestamp.digicert.com `
/td SHA256 `
"out\build\windows\JustASample_artefacts\Release\VST3\Just a Sample.vst3\Contents\x86_64-win\Just a Sample.vst3"
- name: Install 7-Zip
run: choco install 7zip -y
- name: Zip Windows VST3
run: |
cd "out\build\windows\JustASample_artefacts\Release\VST3"
7z a -tzip "${{ github.workspace }}\JAS.Windows.VST3.v${{ needs.get-version.outputs.full_version }}.zip" "Just a Sample.vst3"
- name: Build Windows Installer
run: iscc "Releases\Windows\installer_setup.iss" /DMyAppVersion="${{ needs.get-version.outputs.full_version }}" /DSourceDir="${{ github.workspace }}"
- name: Sign installer
run: |
signtool sign `
/f cert.pfx `
/p "${{ secrets.WINDOWS_PFX_PASSWORD }}" `
/fd SHA256 `
/tr http://timestamp.digicert.com `
/td SHA256 `
"Releases\Windows\Output\Install Just a Sample.exe"
- name: Cleanup certificate
run: del cert.pfx
- name: Zip Windows Installer
run: |
cd "Releases\Windows\Output"
7z a -tzip "${{ github.workspace }}\Install.Just.a.Sample.Windows.v${{ needs.get-version.outputs.full_version }}.zip" "Install Just a Sample.exe"
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: windows-artifacts
path: |
Install.Just.a.Sample.Windows.v${{ needs.get-version.outputs.full_version }}.zip
JAS.Windows.VST3.v${{ needs.get-version.outputs.full_version }}.zip
build-linux:
needs: get-version
runs-on: ubuntu-22.04
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Cache .deps, build, and ccache
uses: actions/cache@v5.0.3
with:
path: |
out/build
.deps
~/.ccache
key: ${{ runner.os }}-build-${{ hashFiles('CMakeLists.txt', '**/*.cmake') }}
restore-keys: ${{ runner.os }}-build-
- name: Install JUCE Linux Dependencies
run: chmod +x Releases/Linux/install-dependencies.sh && ./Releases/Linux/install-dependencies.sh
- name: Install ccache
run: sudo apt-get install ccache -y
- name: Configure CMake
run: cmake --preset linux -DJAS_PATCH_INDEX=${{ needs.get-version.outputs.patch }}
- name: Build Release
run: cmake --build --preset release-linux
- name: Strip VST3 binary
run: strip "out/build/linux/JustASample_artefacts/Release/VST3/Just a Sample.vst3/Contents/x86_64-linux/Just a Sample.so"
- name: Zip Linux VST3
run: |
cd "out/build/linux/JustASample_artefacts/Release/VST3"
zip -r "${{ github.workspace }}/JAS.Linux.VST3.v${{ needs.get-version.outputs.full_version }}.zip" "Just a Sample.vst3"
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: linux-artifacts
path: JAS.Linux.VST3.v${{ needs.get-version.outputs.full_version }}.zip
build-macos:
needs: get-version
runs-on: macos-latest
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Cache .deps, build, and ccache
uses: actions/cache@v5.0.3
with:
path: |
out/build
.deps
~/.ccache
key: ${{ runner.os }}-build-${{ hashFiles('CMakeLists.txt', '**/*.cmake') }}
restore-keys: ${{ runner.os }}-build-
- name: Install ccache
run: brew install ccache
- name: Configure CMake
run: cmake --preset macos -DJAS_PATCH_INDEX=${{ needs.get-version.outputs.patch }}
- name: Build Release
run: cmake --build --preset release-macos
- name: Install macOS packaging tools
run: |
brew install fileicon
python3 -m venv venv
source venv/bin/activate
pip3 install dmgbuild
- name: Set File Icons
run: |
cp "Releases/macOS/icon.icns" "out/build/macos/JustASample_artefacts/Release/VST3/Just a Sample.vst3/Contents/Resources/Icon.icns"
cp "Releases/macOS/icon.icns" "out/build/macos/JustASample_artefacts/Release/AU/Just a Sample.component/Contents/Resources/Icon.icns"
- name: Ad-Hoc Sign Binaries
run: |
codesign --force --deep -s - "out/build/macos/JustASample_artefacts/Release/VST3/Just a Sample.vst3"
codesign --force --deep -s - "out/build/macos/JustASample_artefacts/Release/AU/Just a Sample.component"
- name: Zip macOS VST3
run: |
cd "out/build/macos/JustASample_artefacts/Release/VST3"
zip -r "${{ github.workspace }}/JAS.macOS.VST3.v${{ needs.get-version.outputs.full_version }}.zip" "Just a Sample.vst3"
- name: Zip macOS AU
run: |
cd "out/build/macos/JustASample_artefacts/Release/AU"
zip -r "${{ github.workspace }}/JAS.macOS.AU.v${{ needs.get-version.outputs.full_version }}.zip" "Just a Sample.component"
- name: Stage files for PKG
run: |
mkdir -p pkg_root/Library/Audio/Plug-Ins/VST3
mkdir -p pkg_root/Library/Audio/Plug-Ins/Components
cp -R "out/build/macos/JustASample_artefacts/Release/VST3/Just a Sample.vst3" "pkg_root/Library/Audio/Plug-Ins/VST3/"
cp -R "out/build/macos/JustASample_artefacts/Release/AU/Just a Sample.component" "pkg_root/Library/Audio/Plug-Ins/Components/"
- name: Build PKG Installer
run: |
pkgbuild --root pkg_root \
--identifier com.BinyaminFriedman.JustASample \
--version "${{ needs.get-version.outputs.full_version }}" \
--install-location / \
"Just a Sample.pkg"
- name: Set PKG Icon
run: |
fileicon set "Just a Sample.pkg" "Releases/macOS/installer_icon.png"
- name: Create Clean DMG
run: |
mkdir dmg_root
cp "Just a Sample.pkg" dmg_root/
source venv/bin/activate
python3 -m dmgbuild -s Releases/macOS/dmg_settings.py "Install Just a Sample" Install.Just.a.Sample.macOS.v${{ needs.get-version.outputs.full_version }}.dmg
fileicon set "Install.Just.a.Sample.macOS.v${{ needs.get-version.outputs.full_version }}.dmg" "Releases/macOS/icon.png"
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: macos-artifacts
path: |
Install.Just.a.Sample.macOS.v${{ needs.get-version.outputs.full_version }}.dmg
JAS.macOS.VST3.v${{ needs.get-version.outputs.full_version }}.zip
JAS.macOS.AU.v${{ needs.get-version.outputs.full_version }}.zip
publish-release:
needs: [get-version, build-windows, build-linux, build-macos]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Delete old assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ASSETS=$(gh release view v${{ needs.get-version.outputs.base_version }}-latest --json assets -q '.assets[].name' || true)
for asset in $ASSETS; do
gh release delete-asset v${{ needs.get-version.outputs.base_version }}-latest "$asset" -y
done
- name: Create or Update Release
uses: ncipollo/release-action@v1.20.0
with:
tag: "v${{ needs.get-version.outputs.base_version }}-latest"
name: "Just A Sample v${{ needs.get-version.outputs.base_version }} Latest"
artifacts: "artifacts/*"
allowUpdates: true
replacesArtifacts: true
makeLatest: true
draft: false
omitBodyDuringUpdate: true
- name: Download Butler
run: |
curl -L -o butler.zip https://broth.itch.zone/butler/linux-amd64/LATEST/archive/default
unzip butler.zip -d butler
echo "${{ github.workspace }}/butler" >> $GITHUB_PATH
- name: Publish to itch.io
env:
BUTLER_API_KEY: ${{ secrets.BUTLER_API_KEY }}
run: |
butler push artifacts/Install.Just.a.Sample.Windows.v${{ needs.get-version.outputs.full_version }}.zip binyaminf/just-a-sample:windows-easy-installer --userversion ${{ needs.get-version.outputs.full_version }}
butler push artifacts/Install.Just.a.Sample.macOS.v${{ needs.get-version.outputs.full_version }}.dmg binyaminf/just-a-sample:macos-easy-installer --userversion ${{ needs.get-version.outputs.full_version }}
butler push artifacts/JAS.Windows.VST3.v${{ needs.get-version.outputs.full_version }}.zip binyaminf/just-a-sample:windows-vst3 --userversion ${{ needs.get-version.outputs.full_version }}
butler push artifacts/JAS.macOS.VST3.v${{ needs.get-version.outputs.full_version }}.zip binyaminf/just-a-sample:macos-vst3 --userversion ${{ needs.get-version.outputs.full_version }}
butler push artifacts/JAS.macOS.AU.v${{ needs.get-version.outputs.full_version }}.zip binyaminf/just-a-sample:macos-au --userversion ${{ needs.get-version.outputs.full_version }}
butler push artifacts/JAS.Linux.VST3.v${{ needs.get-version.outputs.full_version }}.zip binyaminf/just-a-sample:linux-vst3 --userversion ${{ needs.get-version.outputs.full_version }}

38
.gitignore vendored Normal file
View file

@ -0,0 +1,38 @@
# Build directories
/build/
/out/
# CMake generated files
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
# CMake build dependencies (FetchContent)
.deps/
# IDEs and editors
**/.idea/
.vscode/
*.swp
*.swo
*~
\#*\#
.#*
# OS files
.DS_Store
Thumbs.db
# Build artifacts
*.o
*.obj
*.a
*.lib
*.so
*.dylib
*.dll
*.exe
*.out
# Project specific
/Releases/Windows/Secrets/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "External/readerwriterqueue"]
path = External/readerwriterqueue
url = https://github.com/cameron314/readerwriterqueue/

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
Assets/Features/Effects.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
Assets/Features/Footer.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Binary file not shown.

BIN
Assets/Fonts/Inter-Bold.ttf Normal file

Binary file not shown.

Binary file not shown.

4
Assets/Icons/IconAdd.svg Normal file
View file

@ -0,0 +1,4 @@
<svg width="39" height="41" viewBox="0 0 39 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 8V32" stroke="#171614" stroke-width="4" stroke-linecap="round"/>
<path d="M31 20L7 20" stroke="#171614" stroke-width="4" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 262 B

View file

@ -0,0 +1,3 @@
<svg width="47" height="46" viewBox="0 0 47 46" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.5 43.5C34.8879 43.5 44.1196 34.2684 44.1196 22.8804C44.1196 22.6871 44.1168 22.4946 44.1115 22.3024C41.5155 24.111 38.3591 25.1715 34.9553 25.1715C26.0981 25.1715 18.9179 17.9913 18.9179 9.13411C18.9179 6.76811 19.4302 4.52177 20.35 2.5C10.4573 4.01645 2.88043 12.5636 2.88043 22.8804C2.88043 34.2684 12.1121 43.5 23.5 43.5Z" stroke="#171614" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 525 B

View file

@ -0,0 +1,3 @@
<svg width="49" height="47" viewBox="0 0 49 47" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.481 4.62717L17.0852 5.89416C16.815 5.993 16.6348 6.25358 16.6348 6.54113C16.6348 6.82867 16.815 7.08925 17.0852 7.1881L20.481 8.45508L21.751 11.8427C21.8501 12.1123 22.1113 12.292 22.3995 12.292C22.6878 12.292 22.949 12.1123 23.0481 11.8427L24.3181 8.45508L27.7139 7.1881C27.9841 7.08925 28.1642 6.82867 28.1642 6.54113C28.1642 6.25358 27.9841 5.993 27.7139 5.89416L24.3181 4.62717L23.0481 1.23957C22.949 0.969997 22.6878 0.790283 22.3995 0.790283C22.1113 0.790283 21.8501 0.969997 21.751 1.23957L20.481 4.62717ZM3.49309 36.3197C1.80871 38 1.80871 40.7317 3.49309 42.421L6.60963 45.53C8.29401 47.2104 11.0322 47.2104 12.7256 45.53L47.0707 11.2586C48.755 9.57829 48.755 6.84664 47.0707 5.15733L43.9541 2.05727C42.2697 0.376941 39.5315 0.376941 37.8381 2.05727L3.49309 36.3197ZM42.9903 8.21246L33.5326 17.6474L31.4339 15.5538L40.8916 6.1188L42.9903 8.21246ZM0.0162497 11.3215C-0.389081 11.4743 -0.659302 11.8607 -0.659302 12.292C-0.659302 12.7233 -0.389081 13.1097 0.0162497 13.2624L5.1054 15.1674L7.01496 20.2443C7.16809 20.6487 7.5554 20.9182 7.98776 20.9182C8.42011 20.9182 8.80743 20.6487 8.96055 20.2443L10.8701 15.1674L15.9593 13.2624C16.3646 13.1097 16.6348 12.7233 16.6348 12.292C16.6348 11.8607 16.3646 11.4743 15.9593 11.3215L10.8701 9.41655L8.96055 4.33963C8.80743 3.93528 8.42011 3.6657 7.98776 3.6657C7.5554 3.6657 7.16809 3.93528 7.01496 4.33963L5.1054 9.41655L0.0162497 11.3215ZM31.7221 34.3249C31.3168 34.4776 31.0466 34.864 31.0466 35.2953C31.0466 35.7267 31.3168 36.113 31.7221 36.2658L36.8113 38.1708L38.7208 43.2477C38.874 43.652 39.2613 43.9216 39.6936 43.9216C40.126 43.9216 40.5133 43.652 40.6664 43.2477L42.576 38.1708L47.6651 36.2658C48.0705 36.113 48.3407 35.7267 48.3407 35.2953C48.3407 34.864 48.0705 34.4776 47.6651 34.3249L42.576 32.4199L40.6664 27.343C40.5133 26.9386 40.126 26.6691 39.6936 26.6691C39.2613 26.6691 38.874 26.9386 38.7208 27.343L36.8113 32.4199L31.7221 34.3249Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

25
Assets/Icons/IconDrag.svg Normal file
View file

@ -0,0 +1,25 @@
<svg width="47" height="32" viewBox="0 0 47 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_146_398)">
<g clip-path="url(#clip1_146_398)">
<circle cx="6.18182" cy="7.99999" r="6.18182" fill="#171614"/>
<circle cx="23.2727" cy="7.99999" r="6.18182" fill="#171614"/>
<circle cx="40.3636" cy="7.99999" r="6.18182" fill="#171614"/>
</g>
<g clip-path="url(#clip2_146_398)">
<circle cx="6.18182" cy="24" r="6.18182" fill="#171614"/>
<circle cx="23.2727" cy="24" r="6.18182" fill="#171614"/>
<circle cx="40.3636" cy="24" r="6.18182" fill="#171614"/>
</g>
</g>
<defs>
<clipPath id="clip0_146_398">
<rect width="46.5455" height="32" fill="white"/>
</clipPath>
<clipPath id="clip1_146_398">
<rect width="46.5455" height="16" fill="white"/>
</clipPath>
<clipPath id="clip2_146_398">
<rect width="46.5455" height="16" fill="white" transform="translate(0 16)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 905 B

3
Assets/Icons/IconFit.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="32" height="33" viewBox="0 0 32 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.8333 30.5H25.9167C26.9996 30.5 28.0382 30.0698 28.804 29.304C29.5698 28.5382 30 27.4996 30 26.4167V22.3333M30 10.6667V6.58333C30 5.50037 29.5698 4.46175 28.804 3.69598C28.0382 2.93021 26.9996 2.5 25.9167 2.5H21.8333M10.1667 30.5H6.08333C5.00037 30.5 3.96175 30.0698 3.19598 29.304C2.43021 28.5382 2 27.4996 2 26.4167V22.3333M2 10.6667V6.58333C2 5.50037 2.43021 4.46175 3.19598 3.69598C3.96175 2.93021 5.00037 2.5 6.08333 2.5H10.1667" stroke="#171614" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 633 B

View file

@ -0,0 +1,5 @@
<svg width="43" height="44" viewBox="0 0 43 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.5001 2.22583C17.5891 2.22583 13.766 3.38557 10.5141 5.55839C7.26224 7.73121 4.72773 10.8195 3.23106 14.4328C1.7344 18.0461 1.3428 22.022 2.1058 25.8578C2.86879 29.6937 4.7521 33.2171 7.51758 35.9826C10.2831 38.7481 13.8065 40.6314 17.6423 41.3944C21.4781 42.1574 25.4541 41.7658 29.0674 40.2691C32.6806 38.7724 35.7689 36.2379 37.9418 32.9861C40.1146 29.7342 41.2743 25.9111 41.2743 22.0001C41.2743 16.7556 39.191 11.726 35.4826 8.01757C31.7742 4.30918 26.7445 2.22583 21.5001 2.22583Z" stroke="#1E1E1E" stroke-width="3.5" stroke-miterlimit="10"/>
<path d="M16 16.2949C16 16.2949 16.0814 14.5982 17.8974 13.137C18.9746 12.2693 20.266 12.0182 21.4295 12.0007C22.4892 11.9872 23.4355 12.1626 24.0017 12.4322C24.9712 12.8937 26.859 14.0203 26.859 16.4161C26.859 18.9369 25.2107 20.0819 23.3327 21.3414C21.4547 22.6008 20.9447 23.9679 20.9447 25.3805" stroke="#1E1E1E" stroke-width="3.63176" stroke-miterlimit="10" stroke-linecap="round"/>
<path d="M20.939 33.3613C22.01 33.3613 22.8781 32.4931 22.8781 31.4222C22.8781 30.3513 22.01 29.4831 20.939 29.4831C19.8681 29.4831 18.9999 30.3513 18.9999 31.4222C18.9999 32.4931 19.8681 33.3613 20.939 33.3613Z" fill="#1E1E1E"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,4 @@
<svg width="225" height="29" viewBox="0 0 225 29" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M49.315 27.5V1.31818H52.4854V12.9773H66.4457V1.31818H69.6161V27.5H66.4457V15.7898H52.4854V27.5H49.315ZM75.5543 27.5V7.86364H78.5714V27.5H75.5543ZM77.0884 4.59091C76.5004 4.59091 75.9933 4.39062 75.5671 3.99006C75.1495 3.58949 74.9407 3.10795 74.9407 2.54545C74.9407 1.98295 75.1495 1.50142 75.5671 1.10085C75.9933 0.700284 76.5004 0.5 77.0884 0.5C77.6765 0.5 78.1793 0.700284 78.5969 1.10085C79.0231 1.50142 79.2362 1.98295 79.2362 2.54545C79.2362 3.10795 79.0231 3.58949 78.5969 3.99006C78.1793 4.39062 77.6765 4.59091 77.0884 4.59091ZM91.5121 27.9091C89.8757 27.9091 88.4311 27.4957 87.1783 26.669C85.9254 25.8338 84.9453 24.6577 84.2379 23.1406C83.5305 21.6151 83.1768 19.8125 83.1768 17.733C83.1768 15.6705 83.5305 13.8807 84.2379 12.3636C84.9453 10.8466 85.9297 9.67472 87.1911 8.84801C88.4524 8.02131 89.9098 7.60795 91.5632 7.60795C92.8416 7.60795 93.8516 7.82102 94.593 8.24716C95.343 8.66477 95.9141 9.14205 96.3061 9.67898C96.7067 10.2074 97.0178 10.642 97.2393 10.983H97.495V1.31818H100.512V27.5H97.5973V24.483H97.2393C97.0178 24.8409 96.7024 25.2926 96.2933 25.8381C95.8842 26.375 95.3004 26.8565 94.5419 27.2827C93.7834 27.7003 92.7734 27.9091 91.5121 27.9091ZM91.9212 25.1989C93.1314 25.1989 94.1541 24.8835 94.9893 24.2528C95.8246 23.6136 96.4595 22.7315 96.8942 21.6065C97.3288 20.473 97.5462 19.1648 97.5462 17.6818C97.5462 16.2159 97.3331 14.9332 96.907 13.8338C96.4808 12.7259 95.8501 11.8651 95.0149 11.2514C94.1797 10.6293 93.1484 10.3182 91.9212 10.3182C90.6428 10.3182 89.5774 10.6463 88.7251 11.3026C87.8814 11.9503 87.2464 12.8324 86.8203 13.9489C86.4027 15.0568 86.1939 16.3011 86.1939 17.6818C86.1939 19.0795 86.407 20.3494 86.8331 21.4915C87.2678 22.625 87.907 23.5284 88.7507 24.2017C89.603 24.8665 90.6598 25.1989 91.9212 25.1989ZM114.69 27.9091C112.798 27.9091 111.165 27.4915 109.793 26.6562C108.43 25.8125 107.377 24.6364 106.636 23.1278C105.903 21.6108 105.536 19.8466 105.536 17.8352C105.536 15.8239 105.903 14.0511 106.636 12.517C107.377 10.9744 108.408 9.77273 109.729 8.91193C111.059 8.04261 112.61 7.60795 114.383 7.60795C115.406 7.60795 116.415 7.77841 117.413 8.11932C118.41 8.46023 119.317 9.0142 120.136 9.78125C120.954 10.5398 121.606 11.5455 122.092 12.7983C122.577 14.0511 122.82 15.5937 122.82 17.4261V18.7045H107.684V16.0966H119.752C119.752 14.9886 119.531 14 119.087 13.1307C118.653 12.2614 118.031 11.5753 117.221 11.0724C116.42 10.5696 115.474 10.3182 114.383 10.3182C113.181 10.3182 112.141 10.6165 111.263 11.2131C110.394 11.8011 109.725 12.5682 109.256 13.5142C108.788 14.4602 108.553 15.4744 108.553 16.5568V18.2955C108.553 19.7784 108.809 21.0355 109.32 22.0668C109.84 23.0895 110.56 23.8693 111.481 24.4062C112.401 24.9347 113.471 25.1989 114.69 25.1989C115.482 25.1989 116.198 25.0881 116.837 24.8665C117.485 24.6364 118.043 24.2955 118.512 23.8438C118.981 23.3835 119.343 22.8125 119.599 22.1307L122.513 22.9489C122.207 23.9375 121.691 24.8068 120.967 25.5568C120.242 26.2983 119.347 26.8778 118.282 27.2955C117.217 27.7045 116.019 27.9091 114.69 27.9091ZM137.944 27.5V1.31818H153.643V4.13068H141.114V12.9773H152.467V15.7898H141.114V27.5H137.944ZM160.607 1.31818L167.357 12.2102H167.561L174.311 1.31818H178.044L169.811 14.4091L178.044 27.5H174.311L167.561 16.8125H167.357L160.607 27.5H156.874L165.311 14.4091L156.874 1.31818H160.607Z" fill="#171614"/>
<path d="M210.47 6.32752C211.316 5.42929 212.69 5.42929 213.536 6.32752L224.365 17.825C225.212 18.7232 225.212 20.1819 224.365 21.0802C223.519 21.9784 222.146 21.9784 221.299 21.0802L212 11.2067L202.701 21.073C201.854 21.9712 200.481 21.9712 199.635 21.073C198.788 20.1747 198.788 18.716 199.635 17.8178L210.464 6.32034L210.47 6.32752Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,10 @@
<svg width="43" height="44" viewBox="0 0 43 44" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_706_201)">
<path d="M21.5 16.1364C24.725 16.1364 27.3636 18.775 27.3636 22C27.3636 25.225 24.725 27.8636 21.5 27.8636C18.275 27.8636 15.6364 25.225 15.6364 22C15.6364 18.775 18.275 16.1364 21.5 16.1364ZM21.5 12.2273C16.1055 12.2273 11.7273 16.6055 11.7273 22C11.7273 27.3945 16.1055 31.7727 21.5 31.7727C26.8945 31.7727 31.2727 27.3945 31.2727 22C31.2727 16.6055 26.8945 12.2273 21.5 12.2273ZM1.95455 23.9545H5.86364C6.93864 23.9545 7.81818 23.075 7.81818 22C7.81818 20.925 6.93864 20.0455 5.86364 20.0455H1.95455C0.879545 20.0455 0 20.925 0 22C0 23.075 0.879545 23.9545 1.95455 23.9545ZM37.1364 23.9545H41.0455C42.1205 23.9545 43 23.075 43 22C43 20.925 42.1205 20.0455 41.0455 20.0455H37.1364C36.0614 20.0455 35.1818 20.925 35.1818 22C35.1818 23.075 36.0614 23.9545 37.1364 23.9545ZM19.5455 2.45455V6.36364C19.5455 7.43864 20.425 8.31818 21.5 8.31818C22.575 8.31818 23.4545 7.43864 23.4545 6.36364V2.45455C23.4545 1.37955 22.575 0.5 21.5 0.5C20.425 0.5 19.5455 1.37955 19.5455 2.45455ZM19.5455 37.6364V41.5455C19.5455 42.6205 20.425 43.5 21.5 43.5C22.575 43.5 23.4545 42.6205 23.4545 41.5455V37.6364C23.4545 36.5614 22.575 35.6818 21.5 35.6818C20.425 35.6818 19.5455 36.5614 19.5455 37.6364ZM9.75318 7.49727C9.57236 7.31608 9.35758 7.17233 9.12113 7.07425C8.88468 6.97616 8.63121 6.92568 8.37523 6.92568C8.11924 6.92568 7.86577 6.97616 7.62933 7.07425C7.39288 7.17233 7.17809 7.31608 6.99727 7.49727C6.81608 7.67809 6.67233 7.89288 6.57425 8.12933C6.47616 8.36577 6.42568 8.61924 6.42568 8.87523C6.42568 9.13121 6.47616 9.38468 6.57425 9.62113C6.67233 9.85758 6.81608 10.0724 6.99727 10.2532L9.06909 12.325C9.83136 13.0873 11.0823 13.0873 11.825 12.325C12.5677 11.5627 12.5873 10.3118 11.825 9.56909L9.75318 7.49727ZM33.9309 31.675C33.7501 31.4938 33.5353 31.3501 33.2989 31.252C33.0624 31.1539 32.8089 31.1034 32.553 31.1034C32.297 31.1034 32.0435 31.1539 31.8071 31.252C31.5706 31.3501 31.3558 31.4938 31.175 31.675C30.9938 31.8558 30.8501 32.0706 30.752 32.3071C30.6539 32.5435 30.6034 32.797 30.6034 33.053C30.6034 33.3089 30.6539 33.5624 30.752 33.7989C30.8501 34.0353 30.9938 34.2501 31.175 34.4309L33.2468 36.5027C34.0091 37.265 35.26 37.265 36.0027 36.5027C36.1839 36.3219 36.3277 36.1071 36.4258 35.8707C36.5238 35.6342 36.5743 35.3808 36.5743 35.1248C36.5743 34.8688 36.5238 34.6153 36.4258 34.3789C36.3277 34.1424 36.1839 33.9276 36.0027 33.7468L33.9309 31.675ZM36.0027 10.2532C36.1839 10.0724 36.3277 9.85758 36.4258 9.62113C36.5238 9.38468 36.5743 9.13121 36.5743 8.87523C36.5743 8.61924 36.5238 8.36577 36.4258 8.12933C36.3277 7.89288 36.1839 7.67809 36.0027 7.49727C35.8219 7.31608 35.6071 7.17233 35.3707 7.07425C35.1342 6.97616 34.8808 6.92568 34.6248 6.92568C34.3688 6.92568 34.1153 6.97616 33.8789 7.07425C33.6424 7.17233 33.4276 7.31608 33.2468 7.49727L31.175 9.56909C30.4127 10.3314 30.4127 11.5823 31.175 12.325C31.9373 13.0677 33.1882 13.0873 33.9309 12.325L36.0027 10.2532ZM11.825 34.4309C12.0062 34.2501 12.1499 34.0353 12.248 33.7989C12.3461 33.5624 12.3966 33.3089 12.3966 33.053C12.3966 32.797 12.3461 32.5435 12.248 32.3071C12.1499 32.0706 12.0062 31.8558 11.825 31.675C11.6442 31.4938 11.4294 31.3501 11.1929 31.252C10.9565 31.1539 10.703 31.1034 10.447 31.1034C10.1911 31.1034 9.93759 31.1539 9.70114 31.252C9.4647 31.3501 9.24991 31.4938 9.06909 31.675L6.99727 33.7468C6.235 34.5091 6.235 35.76 6.99727 36.5027C7.75955 37.2455 9.01045 37.265 9.75318 36.5027L11.825 34.4309Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_706_201">
<rect width="43" height="43" fill="white" transform="translate(0 0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,3 @@
<svg width="40" height="39" viewBox="0 0 40 39" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M32.3395 19.5675C34.942 16.9199 34.942 12.6323 32.3395 9.98474C30.0364 7.64177 26.4066 7.33718 23.758 9.2631L23.6843 9.31465C23.021 9.7973 22.869 10.7345 23.3434 11.4046C23.8179 12.0747 24.7391 12.234 25.3978 11.7513L25.4715 11.6998C26.9502 10.6267 28.9723 10.7954 30.2529 12.1028C31.7038 13.5789 31.7038 15.9687 30.2529 17.4447L25.0846 22.7117C23.6336 24.1878 21.2844 24.1878 19.8335 22.7117C18.5483 21.4044 18.3825 19.3472 19.4373 17.8477L19.488 17.7728C19.9624 17.098 19.8058 16.1608 19.1471 15.6828C18.4884 15.2049 17.5626 15.3595 17.0927 16.0296L17.0421 16.1046C15.1443 18.7943 15.4437 22.4868 17.7468 24.8298C20.3494 27.4773 24.5641 27.4773 27.1667 24.8298L32.3395 19.5675ZM8.40528 18.471C5.80273 21.1185 5.80273 25.4062 8.40528 28.0537C10.7084 30.3967 14.3382 30.7013 16.9868 28.7753L17.0605 28.7238C17.7238 28.2411 17.8758 27.304 17.4013 26.6339C16.9269 25.9638 16.0056 25.8045 15.3469 26.2871L15.2732 26.3387C13.7946 27.4117 11.7725 27.243 10.4919 25.9357C9.04095 24.4549 9.04095 22.0651 10.4919 20.589L15.6602 15.3267C17.1111 13.8506 19.4603 13.8506 20.9113 15.3267C22.1965 16.6341 22.3623 18.6912 21.3075 20.1954L21.2568 20.2704C20.7823 20.9451 20.939 21.8823 21.5977 22.3603C22.2564 22.8383 23.1822 22.6836 23.6521 22.0135L23.7027 21.9386C25.6005 19.2441 25.3011 15.5516 22.998 13.2087C20.3954 10.5611 16.1807 10.5611 13.5781 13.2087L8.40528 18.471Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

12
Assets/Icons/IconLofi.svg Normal file
View file

@ -0,0 +1,12 @@
<svg width="67" height="64" viewBox="0 0 67 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.5362 20.7635V22.7075H12.6082V6.38746H14.6962V20.7635H21.5362Z" fill="#403D37"/>
<path d="M35.4623 12.7475V16.3235C35.4623 18.3555 34.9503 19.9715 33.9263 21.1715C32.9183 22.3555 31.4623 22.9475 29.5583 22.9475C27.6543 22.9475 26.1903 22.3555 25.1663 21.1715C24.1583 19.9715 23.6543 18.3555 23.6543 16.3235V12.7475C23.6543 10.7315 24.1583 9.13146 25.1663 7.94746C26.1903 6.74746 27.6543 6.14746 29.5583 6.14746C31.4623 6.14746 32.9183 6.74746 33.9263 7.94746C34.9503 9.13146 35.4623 10.7315 35.4623 12.7475ZM33.3023 12.7475C33.3023 11.2595 32.9823 10.1155 32.3423 9.31546C31.7183 8.49946 30.7903 8.09146 29.5583 8.09146C28.3423 8.09146 27.4143 8.49946 26.7743 9.31546C26.1343 10.1155 25.8143 11.2595 25.8143 12.7475V16.3235C25.8143 17.8275 26.1343 18.9875 26.7743 19.8035C27.4143 20.6195 28.3423 21.0275 29.5583 21.0275C30.7903 21.0275 31.7183 20.6195 32.3423 19.8035C32.9823 18.9875 33.3023 17.8275 33.3023 16.3235V12.7475Z" fill="#403D37"/>
<path d="M47.9054 15.8435H41.9774V22.7075H39.8894V6.38746H48.6254V8.35546H41.9774V13.8755H47.9054V15.8435Z" fill="#403D37"/>
<path d="M54.118 22.7075H52.03V6.38746H54.118V22.7075Z" fill="#403D37"/>
<path d="M26.5173 51.891C25.8185 51.891 25.2887 51.7107 24.928 51.35C24.5673 51.0118 24.387 50.5609 24.387 49.9973C24.387 49.3887 24.556 48.8251 24.8942 48.3066C25.2549 47.7655 25.717 47.3372 26.2806 47.0216C26.8442 46.706 27.4304 46.5482 28.039 46.5482C28.3096 46.5482 28.5801 46.582 28.8506 46.6496C29.1437 46.6947 29.4255 46.7736 29.696 46.8863V31.6694L42.3429 28.4231V45.6352C42.3429 46.6045 42.1288 47.4612 41.7004 48.2051C41.2947 48.9265 40.7536 49.5014 40.0773 49.9297C39.4235 50.3355 38.7021 50.5384 37.9131 50.5384C37.2143 50.5384 36.6845 50.3693 36.3238 50.0312C35.9631 49.6705 35.7828 49.2083 35.7828 48.6447C35.7828 48.036 35.9518 47.4725 36.29 46.954C36.6507 46.4129 37.1128 45.9846 37.6764 45.669C38.24 45.3534 38.8261 45.1956 39.4348 45.1956C39.7053 45.1956 39.9759 45.2294 40.2464 45.297C40.5395 45.3421 40.8212 45.421 41.0918 45.5337V30.3844L41.6666 30.8578L30.4399 33.9688L30.9472 33.1573V46.9878C30.9472 47.9571 30.733 48.8138 30.3047 49.5577C29.8989 50.2791 29.3578 50.8427 28.6815 51.2485C28.0278 51.6768 27.3064 51.891 26.5173 51.891Z" fill="#403D37"/>
<path d="M21.5362 20.7635V22.7075H12.6082V6.38746H14.6962V20.7635H21.5362Z" stroke="#403D37" stroke-width="1.20769"/>
<path d="M35.4623 12.7475V16.3235C35.4623 18.3555 34.9503 19.9715 33.9263 21.1715C32.9183 22.3555 31.4623 22.9475 29.5583 22.9475C27.6543 22.9475 26.1903 22.3555 25.1663 21.1715C24.1583 19.9715 23.6543 18.3555 23.6543 16.3235V12.7475C23.6543 10.7315 24.1583 9.13146 25.1663 7.94746C26.1903 6.74746 27.6543 6.14746 29.5583 6.14746C31.4623 6.14746 32.9183 6.74746 33.9263 7.94746C34.9503 9.13146 35.4623 10.7315 35.4623 12.7475ZM33.3023 12.7475C33.3023 11.2595 32.9823 10.1155 32.3423 9.31546C31.7183 8.49946 30.7903 8.09146 29.5583 8.09146C28.3423 8.09146 27.4143 8.49946 26.7743 9.31546C26.1343 10.1155 25.8143 11.2595 25.8143 12.7475V16.3235C25.8143 17.8275 26.1343 18.9875 26.7743 19.8035C27.4143 20.6195 28.3423 21.0275 29.5583 21.0275C30.7903 21.0275 31.7183 20.6195 32.3423 19.8035C32.9823 18.9875 33.3023 17.8275 33.3023 16.3235V12.7475Z" stroke="#403D37" stroke-width="1.20769"/>
<path d="M47.9054 15.8435H41.9774V22.7075H39.8894V6.38746H48.6254V8.35546H41.9774V13.8755H47.9054V15.8435Z" stroke="#403D37" stroke-width="1.20769"/>
<path d="M54.118 22.7075H52.03V6.38746H54.118V22.7075Z" stroke="#403D37" stroke-width="1.20769"/>
<path d="M26.5173 51.891C25.8185 51.891 25.2887 51.7107 24.928 51.35C24.5673 51.0118 24.387 50.5609 24.387 49.9973C24.387 49.3887 24.556 48.8251 24.8942 48.3066C25.2549 47.7655 25.717 47.3372 26.2806 47.0216C26.8442 46.706 27.4304 46.5482 28.039 46.5482C28.3096 46.5482 28.5801 46.582 28.8506 46.6496C29.1437 46.6947 29.4255 46.7736 29.696 46.8863V31.6694L42.3429 28.4231V45.6352C42.3429 46.6045 42.1288 47.4612 41.7004 48.2051C41.2947 48.9265 40.7536 49.5014 40.0773 49.9297C39.4235 50.3355 38.7021 50.5384 37.9131 50.5384C37.2143 50.5384 36.6845 50.3693 36.3238 50.0312C35.9631 49.6705 35.7828 49.2083 35.7828 48.6447C35.7828 48.036 35.9518 47.4725 36.29 46.954C36.6507 46.4129 37.1128 45.9846 37.6764 45.669C38.24 45.3534 38.8261 45.1956 39.4348 45.1956C39.7053 45.1956 39.9759 45.2294 40.2464 45.297C40.5395 45.3421 40.8212 45.421 41.0918 45.5337V30.3844L41.6666 30.8578L30.4399 33.9688L30.9472 33.1573V46.9878C30.9472 47.9571 30.733 48.8138 30.3047 49.5577C29.8989 50.2791 29.3578 50.8427 28.6815 51.2485C28.0278 51.6768 27.3064 51.891 26.5173 51.891Z" stroke="#403D37" stroke-width="1.20769"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,6 @@
<svg width="86" height="64" viewBox="0 0 86 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M37.6399 47.7H9.86297C9.19598 47.7 8.65527 47.1593 8.65527 46.4923V16.3C8.65527 15.633 9.19597 15.0923 9.86296 15.0923C22.3598 15.0923 35.7076 15.0923 35.7076 15.0923" stroke="#403D37" stroke-width="4.83077"/>
<path d="M48.5091 48.3039L30.3937 60.3316L30.3937 36.2761L48.5091 48.3039Z" fill="#403D37"/>
<path d="M48.5091 15.0923L76.286 15.0923C76.953 15.0923 77.4937 15.633 77.4937 16.3L77.4937 46.4923C77.4937 47.1593 76.953 47.7 76.286 47.7C63.7892 47.7 50.4414 47.7 50.4414 47.7" stroke="#403D37" stroke-width="4.83077"/>
<path d="M37.6399 14.4884L55.7552 2.46067L55.7552 26.5162L37.6399 14.4884Z" fill="#403D37"/>
</svg>

After

Width:  |  Height:  |  Size: 730 B

View file

@ -0,0 +1,4 @@
<svg width="35" height="58" viewBox="0 0 35 58" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.5937 29.9269H10.1399M10.1399 29.9269C10.1399 29.9269 10.1399 13.7336 10.1399 3.35767M10.1399 29.9269C10.1399 29.9269 10.1399 45.3841 10.1399 55.2884" stroke="#403D37" stroke-width="4.83077" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M31.086 29.9269L16.5937 39.3399L16.5937 20.5139L31.086 29.9269Z" fill="#403D37"/>
</svg>

After

Width:  |  Height:  |  Size: 446 B

10
Assets/Icons/IconMono.svg Normal file
View file

@ -0,0 +1,10 @@
<svg width="64" height="18" viewBox="0 0 64 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.97534 0.839244L7.94334 12.9352L12.9353 0.839244H15.3113V17.1592H13.2713V10.1752L13.5353 3.88724L8.85534 15.2392H6.88734L2.30334 3.83924L2.56734 10.1752V17.1592H0.527344V0.839244H2.97534Z" fill="white"/>
<path d="M31.5378 7.19924V10.7752C31.5378 12.8072 31.0258 14.4232 30.0018 15.6232C28.9938 16.8072 27.5378 17.3992 25.6338 17.3992C23.7298 17.3992 22.2658 16.8072 21.2418 15.6232C20.2338 14.4232 19.7298 12.8072 19.7298 10.7752V7.19924C19.7298 5.18324 20.2338 3.58324 21.2418 2.39924C22.2658 1.19924 23.7298 0.599243 25.6338 0.599243C27.5378 0.599243 28.9938 1.19924 30.0018 2.39924C31.0258 3.58324 31.5378 5.18324 31.5378 7.19924ZM29.3778 7.19924C29.3778 5.71124 29.0578 4.56724 28.4178 3.76724C27.7938 2.95124 26.8658 2.54324 25.6338 2.54324C24.4178 2.54324 23.4898 2.95124 22.8498 3.76724C22.2098 4.56724 21.8898 5.71124 21.8898 7.19924V10.7752C21.8898 12.2792 22.2098 13.4392 22.8498 14.2552C23.4898 15.0712 24.4178 15.4792 25.6338 15.4792C26.8658 15.4792 27.7938 15.0712 28.4178 14.2552C29.0578 13.4392 29.3778 12.2792 29.3778 10.7752V7.19924Z" fill="white"/>
<path d="M35.9648 17.1592V0.839244H38.1488C39.6048 2.91924 40.9408 5.01524 42.1568 7.12724C43.3888 9.22324 44.5408 11.4152 45.6128 13.7032L45.3728 7.46324V0.839244H47.4128V17.1592H45.2288C44.1088 14.8232 42.9488 12.5512 41.7488 10.3432C40.5648 8.11924 39.2688 5.95124 37.8608 3.83924L38.0048 10.4872V17.1592H35.9648Z" fill="white"/>
<path d="M63.6472 7.19924V10.7752C63.6472 12.8072 63.1352 14.4232 62.1112 15.6232C61.1032 16.8072 59.6472 17.3992 57.7432 17.3992C55.8392 17.3992 54.3752 16.8072 53.3512 15.6232C52.3432 14.4232 51.8392 12.8072 51.8392 10.7752V7.19924C51.8392 5.18324 52.3432 3.58324 53.3512 2.39924C54.3752 1.19924 55.8392 0.599243 57.7432 0.599243C59.6472 0.599243 61.1032 1.19924 62.1112 2.39924C63.1352 3.58324 63.6472 5.18324 63.6472 7.19924ZM61.4872 7.19924C61.4872 5.71124 61.1672 4.56724 60.5272 3.76724C59.9032 2.95124 58.9752 2.54324 57.7432 2.54324C56.5272 2.54324 55.5992 2.95124 54.9592 3.76724C54.3192 4.56724 53.9992 5.71124 53.9992 7.19924V10.7752C53.9992 12.2792 54.3192 13.4392 54.9592 14.2552C55.5992 15.0712 56.5272 15.4792 57.7432 15.4792C58.9752 15.4792 59.9032 15.0712 60.5272 14.2552C61.1672 13.4392 61.4872 12.2792 61.4872 10.7752V7.19924Z" fill="white"/>
<path d="M2.97534 0.839244L7.94334 12.9352L12.9353 0.839244H15.3113V17.1592H13.2713V10.1752L13.5353 3.88724L8.85534 15.2392H6.88734L2.30334 3.83924L2.56734 10.1752V17.1592H0.527344V0.839244H2.97534Z" stroke="white" stroke-width="0.603846"/>
<path d="M31.5378 7.19924V10.7752C31.5378 12.8072 31.0258 14.4232 30.0018 15.6232C28.9938 16.8072 27.5378 17.3992 25.6338 17.3992C23.7298 17.3992 22.2658 16.8072 21.2418 15.6232C20.2338 14.4232 19.7298 12.8072 19.7298 10.7752V7.19924C19.7298 5.18324 20.2338 3.58324 21.2418 2.39924C22.2658 1.19924 23.7298 0.599243 25.6338 0.599243C27.5378 0.599243 28.9938 1.19924 30.0018 2.39924C31.0258 3.58324 31.5378 5.18324 31.5378 7.19924ZM29.3778 7.19924C29.3778 5.71124 29.0578 4.56724 28.4178 3.76724C27.7938 2.95124 26.8658 2.54324 25.6338 2.54324C24.4178 2.54324 23.4898 2.95124 22.8498 3.76724C22.2098 4.56724 21.8898 5.71124 21.8898 7.19924V10.7752C21.8898 12.2792 22.2098 13.4392 22.8498 14.2552C23.4898 15.0712 24.4178 15.4792 25.6338 15.4792C26.8658 15.4792 27.7938 15.0712 28.4178 14.2552C29.0578 13.4392 29.3778 12.2792 29.3778 10.7752V7.19924Z" stroke="white" stroke-width="0.603846"/>
<path d="M35.9648 17.1592V0.839244H38.1488C39.6048 2.91924 40.9408 5.01524 42.1568 7.12724C43.3888 9.22324 44.5408 11.4152 45.6128 13.7032L45.3728 7.46324V0.839244H47.4128V17.1592H45.2288C44.1088 14.8232 42.9488 12.5512 41.7488 10.3432C40.5648 8.11924 39.2688 5.95124 37.8608 3.83924L38.0048 10.4872V17.1592H35.9648Z" stroke="white" stroke-width="0.603846"/>
<path d="M63.6472 7.19924V10.7752C63.6472 12.8072 63.1352 14.4232 62.1112 15.6232C61.1032 16.8072 59.6472 17.3992 57.7432 17.3992C55.8392 17.3992 54.3752 16.8072 53.3512 15.6232C52.3432 14.4232 51.8392 12.8072 51.8392 10.7752V7.19924C51.8392 5.18324 52.3432 3.58324 53.3512 2.39924C54.3752 1.19924 55.8392 0.599243 57.7432 0.599243C59.6472 0.599243 61.1032 1.19924 62.1112 2.39924C63.1352 3.58324 63.6472 5.18324 63.6472 7.19924ZM61.4872 7.19924C61.4872 5.71124 61.1672 4.56724 60.5272 3.76724C59.9032 2.95124 58.9752 2.54324 57.7432 2.54324C56.5272 2.54324 55.5992 2.95124 54.9592 3.76724C54.3192 4.56724 53.9992 5.71124 53.9992 7.19924V10.7752C53.9992 12.2792 54.3192 13.4392 54.9592 14.2552C55.5992 15.0712 56.5272 15.4792 57.7432 15.4792C58.9752 15.4792 59.9032 15.0712 60.5272 14.2552C61.1672 13.4392 61.4872 12.2792 61.4872 10.7752V7.19924Z" stroke="white" stroke-width="0.603846"/>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

3
Assets/Icons/IconPin.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="34" height="35" viewBox="0 0 34 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.08579 29.5858C1.30474 30.3668 1.30474 31.6332 2.08579 32.4142C2.86683 33.1953 4.13316 33.1953 4.91421 32.4142L2.08579 29.5858ZM20 32.5L18.5858 33.9142C19.3668 34.6953 20.6332 34.6953 21.4142 33.9142L20 32.5ZM22.5 23L21.05 21.6225L20.1429 22.5773L20.6617 23.7878L22.5 23ZM31.5 12.5L33.2889 11.6056C33.1927 11.4133 33.0662 11.2378 32.9142 11.0858L31.5 12.5ZM22.5 3.49999L20.9383 4.74938L21.0075 4.83588L21.0858 4.9142L22.5 3.49999ZM11.5 12V14C11.9898 14 12.4626 13.8202 12.8287 13.4948L11.5 12ZM2 14.5L0.505183 13.1713C-0.198348 13.9627 -0.163007 15.1654 0.585785 15.9142L2 14.5ZM4.91421 32.4142L12.4142 24.9142L9.58579 22.0858L2.08579 29.5858L4.91421 32.4142ZM9.58579 24.9142L18.5858 33.9142L21.4142 31.0858L12.4142 22.0858L9.58579 24.9142ZM21.4142 33.9142C24.2268 31.1016 26.2865 26.758 24.3383 22.2122L20.6617 23.7878C21.7135 26.242 20.7732 28.8984 18.5858 31.0858L21.4142 33.9142ZM23.95 24.3775C24.8271 23.4543 25.8716 22.475 26.9684 21.4629C28.0461 20.4684 29.1847 19.4329 30.1713 18.4588C31.1427 17.4998 32.0798 16.4925 32.7097 15.5321C33.0252 15.0511 33.3298 14.4842 33.4895 13.8636C33.6546 13.2218 33.6898 12.4075 33.2889 11.6056L29.7111 13.3944C29.5602 13.0925 29.6176 12.8593 29.6156 12.8668C29.6083 12.8956 29.5566 13.0462 29.365 13.3383C28.9808 13.924 28.301 14.6844 27.3611 15.6123C26.4366 16.525 25.3695 17.4954 24.2557 18.5233C23.1608 19.5336 22.0279 20.5931 21.05 21.6225L23.95 24.3775ZM32.9142 11.0858L23.9142 2.08577L21.0858 4.9142L30.0858 13.9142L32.9142 11.0858ZM24.0617 2.2506C23.5308 1.58686 22.7918 1.11106 21.8835 0.998555C21.0452 0.894715 20.291 1.12957 19.7126 1.40419C18.5981 1.9333 17.4961 2.92545 16.5233 3.89827C15.5157 4.9059 14.4187 6.13302 13.3645 7.27826C12.2826 8.4535 11.2155 9.57693 10.1713 10.5052L12.8287 13.4948C14.0345 12.423 15.2174 11.1715 16.3074 9.98733C17.4251 8.7732 18.4218 7.65657 19.3517 6.7267C20.3164 5.76203 20.9957 5.22292 21.4281 5.01766C21.623 4.92509 21.5837 4.99198 21.3918 4.96822C21.1301 4.93579 20.9692 4.78811 20.9383 4.74938L24.0617 2.2506ZM3.49482 15.8287C5.09236 14.0315 6.68017 13.6064 7.94639 13.5612C8.61587 13.5373 9.23645 13.6202 9.78768 13.7235C10.0855 13.7794 10.2959 13.8272 10.5716 13.883C10.7595 13.9211 11.1393 14 11.5 14V9.99998C11.6107 9.99998 11.6311 10.0164 11.3659 9.96265C11.1885 9.92672 10.852 9.85339 10.5248 9.79205C9.82605 9.66103 8.88413 9.52517 7.80361 9.56376C5.56983 9.64355 2.90764 10.4685 0.505183 13.1713L3.49482 15.8287ZM12.4142 22.0858L3.41422 13.0858L0.585785 15.9142L9.58578 24.9142L12.4142 22.0858Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,3 @@
<svg width="31" height="35" viewBox="0 0 31 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.7679 14.687L5.00725 1.03098C3.0767 -0.078038 0.120117 0.998168 0.120117 3.74118V31.0466C0.120117 33.5075 2.86744 34.9905 5.00725 33.7568L28.7679 20.1074C30.8875 18.8934 30.8942 15.901 28.7679 14.687Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 334 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,3 @@
<svg width="17" height="24" viewBox="0 0 17 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.50006 14.1631C6.72149 14.1631 5.29649 12.6821 5.29649 10.8473L5.28578 4.21575C5.28578 2.38102 6.72149 0.899963 8.50006 0.899963C10.2786 0.899963 11.7143 2.38102 11.7143 4.21575V10.8473C11.7143 12.6821 10.2786 14.1631 8.50006 14.1631ZM6.10352e-05 10.8473H2.82149C2.82149 14.1631 5.54292 16.4842 8.50006 16.4842C11.4572 16.4842 14.1786 14.1631 14.1786 10.8473H17.0001C17.0001 14.6163 13.0858 18.7331 9.57149 19.2747V23.9H7.42863V19.2747C3.91435 18.7331 6.10352e-05 14.6163 6.10352e-05 10.8473Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 625 B

View file

@ -0,0 +1,4 @@
<svg width="225" height="29" viewBox="0 0 225 29" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M47.848 7.86364C47.6946 6.56818 47.0724 5.5625 45.9815 4.84659C44.8906 4.13068 43.5526 3.77273 41.9673 3.77273C40.8082 3.77273 39.794 3.96023 38.9247 4.33523C38.0639 4.71023 37.3906 5.22585 36.9048 5.8821C36.4276 6.53835 36.1889 7.28409 36.1889 8.11932C36.1889 8.81818 36.3551 9.41903 36.6875 9.92188C37.0284 10.4162 37.4631 10.8295 37.9915 11.1619C38.5199 11.4858 39.0739 11.7543 39.6534 11.9673C40.233 12.1719 40.7656 12.3381 41.2514 12.4659L43.9105 13.1818C44.5923 13.3608 45.3509 13.608 46.1861 13.9233C47.0298 14.2386 47.8352 14.669 48.6023 15.2145C49.3778 15.7514 50.017 16.4418 50.5199 17.2855C51.0227 18.1293 51.2741 19.1648 51.2741 20.392C51.2741 21.8068 50.9034 23.0852 50.1619 24.2273C49.429 25.3693 48.3551 26.277 46.9403 26.9503C45.5341 27.6236 43.8253 27.9602 41.8139 27.9602C39.9389 27.9602 38.3153 27.6577 36.9432 27.0526C35.5795 26.4474 34.5057 25.6037 33.7216 24.5213C32.946 23.4389 32.5071 22.1818 32.4048 20.75H35.6776C35.7628 21.7386 36.0952 22.5568 36.6747 23.2045C37.2628 23.8437 38.0043 24.321 38.8991 24.6364C39.8026 24.9432 40.7741 25.0966 41.8139 25.0966C43.0241 25.0966 44.1108 24.9006 45.0739 24.5085C46.0369 24.108 46.7997 23.554 47.3622 22.8466C47.9247 22.1307 48.206 21.2955 48.206 20.3409C48.206 19.4716 47.9631 18.7642 47.4773 18.2188C46.9915 17.6733 46.3523 17.2301 45.5597 16.8892C44.767 16.5483 43.9105 16.25 42.9901 15.9943L39.7685 15.0739C37.723 14.4858 36.1037 13.6463 34.9105 12.5554C33.7173 11.4645 33.1207 10.0369 33.1207 8.27273C33.1207 6.80682 33.517 5.52841 34.3097 4.4375C35.1108 3.33807 36.1847 2.48579 37.5312 1.88068C38.8864 1.26704 40.3991 0.960226 42.0696 0.960226C43.7571 0.960226 45.2571 1.26278 46.5696 1.8679C47.8821 2.46449 48.9219 3.28267 49.6889 4.32244C50.4645 5.36221 50.8736 6.54261 50.9162 7.86364H47.848ZM59.0948 15.6875V27.5H56.0778V1.31818H59.0948V10.9318H59.3505C59.8107 9.91761 60.5011 9.11222 61.4215 8.51562C62.3505 7.91051 63.5863 7.60795 65.1289 7.60795C66.467 7.60795 67.6388 7.87642 68.6445 8.41335C69.6502 8.94176 70.43 9.75568 70.984 10.8551C71.5465 11.946 71.8278 13.3352 71.8278 15.0227V27.5H68.8107V15.2273C68.8107 13.6676 68.4059 12.4616 67.5962 11.6094C66.7951 10.7486 65.6829 10.3182 64.2596 10.3182C63.271 10.3182 62.3846 10.527 61.6005 10.9446C60.8249 11.3622 60.2113 11.9716 59.7596 12.7727C59.3164 13.5739 59.0948 14.5455 59.0948 15.6875ZM85.3246 27.9091C83.5518 27.9091 81.9964 27.4872 80.6584 26.6435C79.3288 25.7997 78.2891 24.6193 77.5391 23.1023C76.7976 21.5852 76.4268 19.8125 76.4268 17.7841C76.4268 15.7386 76.7976 13.9531 77.5391 12.4276C78.2891 10.902 79.3288 9.71733 80.6584 8.87358C81.9964 8.02983 83.5518 7.60795 85.3246 7.60795C87.0973 7.60795 88.6484 8.02983 89.978 8.87358C91.3161 9.71733 92.3558 10.902 93.0973 12.4276C93.8473 13.9531 94.2223 15.7386 94.2223 17.7841C94.2223 19.8125 93.8473 21.5852 93.0973 23.1023C92.3558 24.6193 91.3161 25.7997 89.978 26.6435C88.6484 27.4872 87.0973 27.9091 85.3246 27.9091ZM85.3246 25.1989C86.6712 25.1989 87.7791 24.8537 88.6484 24.1634C89.5178 23.473 90.1612 22.5653 90.5788 21.4403C90.9964 20.3153 91.2053 19.0966 91.2053 17.7841C91.2053 16.4716 90.9964 15.2486 90.5788 14.1151C90.1612 12.9815 89.5178 12.0653 88.6484 11.3665C87.7791 10.6676 86.6712 10.3182 85.3246 10.3182C83.978 10.3182 82.87 10.6676 82.0007 11.3665C81.1314 12.0653 80.4879 12.9815 80.0703 14.1151C79.6527 15.2486 79.4439 16.4716 79.4439 17.7841C79.4439 19.0966 79.6527 20.3153 80.0703 21.4403C80.4879 22.5653 81.1314 23.473 82.0007 24.1634C82.87 24.8537 83.978 25.1989 85.3246 25.1989ZM102.609 27.5L96.6257 7.86364H99.7962L104.04 22.8977H104.245L108.438 7.86364H111.66L115.802 22.8466H116.006L120.251 7.86364H123.421L117.438 27.5H114.472L110.177 12.4148H109.87L105.575 27.5H102.609ZM137.944 27.5V1.31818H153.643V4.13068H141.114V12.9773H152.467V15.7898H141.114V27.5H137.944ZM160.607 1.31818L167.357 12.2102H167.561L174.311 1.31818H178.044L169.811 14.4091L178.044 27.5H174.311L167.561 16.8125H167.357L160.607 27.5H156.874L165.311 14.4091L156.874 1.31818H160.607Z" fill="#171614"/>
<path d="M210.47 22.6725C211.316 23.5707 212.69 23.5707 213.536 22.6725L224.365 11.175C225.212 10.2768 225.212 8.81807 224.365 7.91983C223.519 7.0216 222.146 7.0216 221.299 7.91983L212 17.7933L202.701 7.92702C201.854 7.02878 200.481 7.02878 199.635 7.92702C198.788 8.82525 198.788 10.284 199.635 11.1822L210.464 22.6797L210.47 22.6725Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,4 @@
<svg width="42" height="19" viewBox="0 0 42 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25 8.5L13 14.1292L13 2.87083L25 8.5Z" fill="#171614"/>
<path d="M34 8.5L22 14.1292L22 2.87083L34 8.5Z" fill="#171614"/>
</svg>

After

Width:  |  Height:  |  Size: 233 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

8
Assets/Icons/Logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 177 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View file

@ -0,0 +1,22 @@
execute_process(COMMAND git describe --always --dirty --exclude=*
WORKING_DIRECTORY "${SOURCE_DIR}" OUTPUT_VARIABLE GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(NOT GIT_HASH)
set(GIT_HASH "unknown")
endif()
execute_process(COMMAND git symbolic-ref -q HEAD
WORKING_DIRECTORY "${SOURCE_DIR}" OUTPUT_VARIABLE GIT_REF OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
if(NOT GIT_REF)
set(DETACHED "1")
else()
set(DETACHED "0")
endif()
string(TIMESTAMP BUILD_TIME "%Y-%m-%d_%H-%M-%S")
file(WRITE "${OUTPUT_FILE}"
"// Auto-generated at build time. Do not edit.\n"
"#pragma once\n"
"#define JAS_GIT_INFO \"${GIT_HASH}-detached=${DETACHED}\"\n"
"#define JAS_BUILD_TIME \"${BUILD_TIME}\"\n"
)

371
CMakeLists.txt Normal file
View file

@ -0,0 +1,371 @@
cmake_minimum_required(VERSION 3.22)
# High-level configuration
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
option(USE_CCACHE "Enable compiler caching via ccache" OFF)
if (USE_CCACHE)
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
endif()
endif()
include(version.cmake)
if (DEFINED JAS_PATCH_INDEX)
set(APP_VERSION "${APP_VERSION}.${JAS_PATCH_INDEX}")
endif()
project(JUST_A_SAMPLE VERSION ${APP_VERSION})
set(FETCHCONTENT_BASE_DIR ${CMAKE_SOURCE_DIR}/.deps CACHE PATH "")
set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
include(FetchContent)
# Generate BuildInfo.h at build time so the timestamp reflects the actual build
set(BUILD_INFO_HEADER "${CMAKE_BINARY_DIR}/BuildInfo.h")
# Custom command with OUTPUT for proper dependency tracking
add_custom_command(OUTPUT "${BUILD_INFO_HEADER}"
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR} -DOUTPUT_FILE=${BUILD_INFO_HEADER}
-P ${CMAKE_SOURCE_DIR}/CMake/generate_build_info.cmake
DEPENDS ${CMAKE_SOURCE_DIR}/CMake/generate_build_info.cmake
)
# Phony target that always runs, forcing the custom command to re-execute
add_custom_target(RegenerateBuildInfo
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR} -DOUTPUT_FILE=${BUILD_INFO_HEADER}
-P ${CMAKE_SOURCE_DIR}/CMake/generate_build_info.cmake
)
# GenerateBuildInfo depends on the header, which triggers the custom command.
# RegenerateBuildInfo forces a fresh timestamp on every build.
add_custom_target(GenerateBuildInfo ALL DEPENDS "${BUILD_INFO_HEADER}")
add_dependencies(GenerateBuildInfo RegenerateBuildInfo)
# Dependency versions
set(JUCE_VERSION 8.0.12)
set(BUNGEE_INSTALL_VERSION v2.4.10)
set(MELATONIN_BLUR_VERSION origin/main)
set(MELATONIN_INSPECTOR_VERSION origin/main)
set(LEAF_VERSION 8d86c4e96ac48740da34f24c9f995bc3c4b3b2a0)
# JAS build options
option(JAS_DARKMODE_DEFAULT "Set the default theme to dark mode" ON)
option(JAS_VST3_REAPER_INTEGRATION "Enable Reaper-specific VST3 extensions (Windows only)" OFF)
option(JAS_FAST_MATH "Enable fast-math in Release" ON)
option(JAS_ENABLE_AVX2 "Enable AVX2/FMA SIMD in Release" ON)
if (JAS_ENABLE_AVX2 AND APPLE AND "arm64" IN_LIST CMAKE_OSX_ARCHITECTURES AND "x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES)
message(WARNING "AVX2 is not compatible with universal builds on macOS. Disabling JAS_ENABLE_AVX2.")
set(JAS_ENABLE_AVX2 OFF)
endif()
if (JAS_VST3_REAPER_INTEGRATION AND NOT WIN32)
set(JUCE_VST3_REAPER_INTEGRATION OFF)
endif()
# Bungee build options
set(BUNGEE_BUILD_SHARED_LIBRARY OFF CACHE INTERNAL "")
set(STRETCHER_BUNGEE_MAX_OCTAVES 4)
# Paths to patch files (careful editing these, the whitespace is important for unified diffs to work correctly)
set(BUNGEE_PATCH_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Patches/bungee_lower_cmake_and_set_num_octaves.patch CACHE INTERNAL "")
set(LEAF_PATCH_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Patches/leaf_more_selective_imports.patch CACHE INTERNAL "")
# Fetch dependencies
FetchContent_Declare(
JUCE
GIT_REPOSITORY https://github.com/juce-framework/JUCE.git
GIT_TAG ${JUCE_VERSION}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/JUCE
)
FetchContent_Declare(
bungee
GIT_REPOSITORY https://github.com/bungee-audio-stretch/bungee.git
GIT_TAG ${BUNGEE_INSTALL_VERSION}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/bungee
# We patch in our own maxPitchOctaves constant and lower the overly restrictive CMake version requirement
PATCH_COMMAND ${CMAKE_COMMAND} -DREPO_DIR=<SOURCE_DIR> -DPATCH_FILE=${BUNGEE_PATCH_FILE}
-DNAME=bungee -P ${CMAKE_SOURCE_DIR}/Patches/apply_patch_if_needed.cmake
)
FetchContent_Declare(
melatonin_blur
GIT_REPOSITORY https://github.com/sudara/melatonin_blur.git
GIT_TAG ${MELATONIN_BLUR_VERSION}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/melatonin_blur
)
FetchContent_Declare(
melatonin_inspector
GIT_REPOSITORY https://github.com/sudara/melatonin_inspector.git
GIT_TAG ${MELATONIN_INSPECTOR_VERSION}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/melatonin_inspector
)
FetchContent_Declare(
LEAF
GIT_REPOSITORY https://github.com/spiricom/LEAF.git
GIT_TAG ${LEAF_VERSION}
SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/LEAF
# We patch leaf-config.h to allow including only the headers we need
# This patch is quite brittle and will likely fail on newer commits, but the pitch detection algorithm is stable
PATCH_COMMAND ${CMAKE_COMMAND} -DREPO_DIR=<SOURCE_DIR> -DPATCH_FILE=${LEAF_PATCH_FILE}
-DNAME=LEAF -P ${CMAKE_SOURCE_DIR}/Patches/apply_patch_if_needed.cmake
)
FetchContent_MakeAvailable(JUCE)
FetchContent_MakeAvailable(bungee)
FetchContent_MakeAvailable(melatonin_blur)
FetchContent_MakeAvailable(melatonin_inspector)
FetchContent_MakeAvailable(LEAF)
# Bungee
set_target_properties(bungee_executable PROPERTIES EXCLUDE_FROM_ALL TRUE)
target_compile_definitions(bungee_library PRIVATE BUNGEE_MAX_OCTAVES=${STRETCHER_BUNGEE_MAX_OCTAVES})
# LEAF
set_source_files_properties(
${leaf_SOURCE_DIR}/leaf/Src/leaf.c
${leaf_SOURCE_DIR}/leaf/Src/leaf-analysis.c
${leaf_SOURCE_DIR}/leaf/Src/leaf-mempool.c
PROPERTIES LANGUAGE CXX # LEAF uses some bad semantics that trip up the linker on GCC
)
add_library(leaf STATIC
${leaf_SOURCE_DIR}/leaf/Externals/d_fft_mayer.c
${leaf_SOURCE_DIR}/leaf/Src/leaf.c
${leaf_SOURCE_DIR}/leaf/Src/leaf-analysis.c
${leaf_SOURCE_DIR}/leaf/Src/leaf-mempool.c
)
target_include_directories(leaf SYSTEM PUBLIC
${leaf_SOURCE_DIR}/leaf
${leaf_SOURCE_DIR}/leaf/Inc
)
target_compile_options(leaf PRIVATE
$<$<C_COMPILER_ID:MSVC>:/W0>
$<$<C_COMPILER_ID:GNU,Clang>:-w>
)
# External
add_library(external_deps STATIC
External/MTS/libMTSClient.cpp
)
target_include_directories(external_deps SYSTEM PUBLIC
External/MTS
External/reaper-plugins
External/Gin
External/readerwriterqueue
)
target_compile_options(external_deps PRIVATE
$<$<C_COMPILER_ID:MSVC>:/W0>
$<$<C_COMPILER_ID:GNU,Clang>:-w>
)
# Just a Sample plugin
juce_add_plugin(JustASample
PRODUCT_NAME "Just a Sample"
DESCRIPTION "Just a Sample is a modern, open-source audio sampler"
ICON_BIG "Assets/Icons/IconLogo.svg"
ICON_SMALL "Assets/Icons/IconLogo.svg"
COMPANY_NAME "Binyamin Friedman"
COMPANY_COPYRIGHT "Copyright (c) 2026 Binyamin Friedman"
COMPANY_WEBSITE "https://github.com/BOBONA/Just-a-Sample/"
BUNDLE_ID com.BinyaminFriedman.JustASample
PLUGIN_MANUFACTURER_CODE Manu
PLUGIN_CODE Bpf2
FORMATS AU VST3
VST3_CATEGORIES Sampler
IS_SYNTH TRUE
NEEDS_MIDI_INPUT TRUE
EDITOR_WANTS_KEYBOARD_FOCUS FALSE
)
juce_generate_juce_header(JustASample)
target_sources(JustASample PRIVATE
Source/CustomLookAndFeel.cpp
Source/CustomLookAndFeel.h
Source/PluginEditor.cpp
Source/PluginEditor.h
Source/PluginParameters.h
Source/PluginProcessor.cpp
Source/PluginProcessor.h
Source/Components/Buttons.h
Source/Components/FxChain.cpp
Source/Components/FxChain.h
Source/Components/FxDragTarget.h
Source/Components/FxModule.cpp
Source/Components/FxModule.h
Source/Components/InputDeviceSelector.cpp
Source/Components/InputDeviceSelector.h
Source/Components/Prompt.h
Source/Components/RangeSelector.h
Source/Components/SampleEditor.cpp
Source/Components/SampleEditor.h
Source/Components/SampleNavigator.cpp
Source/Components/SampleNavigator.h
Source/Components/Displays/ChorusVisualizer.cpp
Source/Components/Displays/ChorusVisualizer.h
Source/Components/Displays/DistortionVisualizer.cpp
Source/Components/Displays/DistortionVisualizer.h
Source/Components/Displays/FilterResponse.cpp
Source/Components/Displays/FilterResponse.h
Source/Components/Displays/ReverbResponse.cpp
Source/Components/Displays/ReverbResponse.h
Source/Components/Displays/SamplePainter.cpp
Source/Components/Displays/SamplePainter.h
Source/Sampler/CustomSamplerVoice.cpp
Source/Sampler/CustomSamplerVoice.h
Source/Sampler/CustomSynthesizer.h
Source/Sampler/SamplerParameters.cpp
Source/Sampler/SamplerParameters.h
Source/Sampler/Stretcher.h
Source/Sampler/Effects/BandEQ.h
Source/Sampler/Effects/Chorus.h
Source/Sampler/Effects/Distortion.h
Source/Sampler/Effects/Effect.h
Source/Sampler/Effects/Reverb.h
External/Gin/gin_distortion.h
External/Gin/gin_simpleverb.cpp
External/Gin/gin_simpleverb.h
Source/Utilities/BufferUtils.h
Source/Utilities/ComponentUtils.h
Source/Utilities/DeviceRecorder.h
Source/Utilities/ListenableValue.h
Source/Utilities/PitchDetector.h
Source/Utilities/SampleLoader.h
Source/Utilities/Reaper/ReaperVST3Extensions.cpp
Source/Utilities/Reaper/ReaperVST3Extensions.h
)
juce_add_binary_data(AudioPluginData
SOURCES
Assets/Fonts/InriaSans-Bold.ttf
Assets/Fonts/InriaSans-Regular.ttf
Assets/Fonts/Inter-Bold.ttf
Assets/Fonts/Inter-Regular.ttf
Assets/Icons/IconAdd.svg
Assets/Icons/IconDarkMode.svg
Assets/Icons/IconDetect.svg
Assets/Icons/IconDrag.svg
Assets/Icons/IconFit.svg
Assets/Icons/IconHelp.svg
Assets/Icons/IconHideFX.svg
Assets/Icons/IconLightMode.svg
Assets/Icons/IconLinkEnabled.svg
Assets/Icons/IconLofi.svg
Assets/Icons/IconLogo.svg
Assets/Icons/IconLoop.svg
Assets/Icons/IconLoopStart.svg
Assets/Icons/IconMono.svg
Assets/Icons/IconPin.svg
Assets/Icons/IconPlay.svg
Assets/Icons/IconPreFX.svg
Assets/Icons/IconRecordSettings.svg
Assets/Icons/IconShowFX.svg
Assets/Icons/IconSpeed.svg
Assets/Icons/IconWaveformMode.svg
Assets/Icons/Logo.svg
)
target_link_libraries(JustASample
PRIVATE
AudioPluginData
juce::juce_audio_utils
juce::juce_cryptography
juce::juce_dsp
juce::juce_opengl
external_deps
bungee_library
melatonin_blur
$<$<OR:$<CONFIG:Debug>,$<STREQUAL:${CMAKE_GENERATOR},Xcode>>:melatonin_inspector> # Only link the inspector in debug
leaf
PUBLIC
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags
)
target_include_directories(JustASample
PRIVATE
"${bungee_SOURCE_DIR}/bungee"
"${CMAKE_BINARY_DIR}"
)
target_compile_definitions(JustASample PUBLIC
JUCE_WEB_BROWSER=0
JUCE_USE_CURL=0
JUCE_VST3_CAN_REPLACE_VST2=0
JUCE_APP_VERSION="${APP_VERSION}"
DONT_SET_USING_JUCE_NAMESPACE=1
BUNGEE_MAX_OCTAVES=${STRETCHER_BUNGEE_MAX_OCTAVES}
JAS_DARKMODE_DEFAULT=$<BOOL:${JAS_DARKMODE_DEFAULT}>
JAS_VST3_REAPER_INTEGRATION=$<BOOL:${JAS_VST3_REAPER_INTEGRATION}>
)
add_dependencies(JustASample GenerateBuildInfo)
# JustASample itself already gets LTO via juce::juce_recommended_lto_flags
set_target_properties(bungee_library leaf PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
)
if (JAS_FAST_MATH)
if (MSVC)
foreach(_jas_target IN ITEMS JustASample bungee_library leaf)
target_compile_options(${_jas_target} PRIVATE $<$<CONFIG:Release>:/fp:fast>)
endforeach()
else()
foreach(_jas_target IN ITEMS JustASample bungee_library leaf)
target_compile_options(${_jas_target} PRIVATE $<$<CONFIG:Release>:-ffast-math>)
endforeach()
endif()
endif()
if (JAS_ENABLE_AVX2)
if (MSVC)
foreach(_jas_target IN ITEMS JustASample bungee_library leaf)
target_compile_options(${_jas_target} PRIVATE $<$<CONFIG:Release>:/arch:AVX2>)
endforeach()
else()
foreach(_jas_target IN ITEMS JustASample bungee_library leaf)
target_compile_options(${_jas_target} PRIVATE $<$<CONFIG:Release>:-mavx2 -mfma>)
endforeach()
endif()
endif()
# MSVC patching for Bungee
if (MSVC)
# Add a dummy unistd.h
target_include_directories(bungee_library PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/fake_headers)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/fake_headers/unistd.h "")
# Use _USE_MATH_DEFINES to get M_PI and other math constants
target_compile_definitions(pffft PRIVATE _USE_MATH_DEFINES)
target_compile_options(pffft PRIVATE "/fp:fast" "/GS-")
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Add a header to define remove __attribute__, since MSVC doesn't support it
set(BUNGEE_COMPAT_H "${CMAKE_CURRENT_BINARY_DIR}/bungee_msvc_compat.h")
file(WRITE "${BUNGEE_COMPAT_H}" "
#ifndef BUNGEE_MSVC_COMPAT_H
#define BUNGEE_MSVC_COMPAT_H
#define __attribute__(x)
#endif
")
target_compile_options(bungee_library PRIVATE "SHELL:/FI \"${BUNGEE_COMPAT_H}\"")
endif()
endif()

62
CMakePresets.json Normal file
View file

@ -0,0 +1,62 @@
{
"version": 3,
"configurePresets": [
{
"name": "windows",
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/out/build/windows",
"cacheVariables": {
"CMAKE_C_COMPILER": "clang-cl",
"CMAKE_CXX_COMPILER": "clang-cl",
"USE_CCACHE": "ON",
"JAS_VST3_REAPER_INTEGRATION": "true"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "macos",
"generator": "Xcode",
"binaryDir": "${sourceDir}/out/build/macos",
"cacheVariables": {
"CMAKE_OSX_ARCHITECTURES": "x86_64;arm64",
"CMAKE_OSX_DEPLOYMENT_TARGET": "10.13",
"JAS_ENABLE_AVX2": "OFF",
"USE_CCACHE": "ON"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
}
},
{
"name": "linux",
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/out/build/linux",
"cacheVariables": {
"USE_CCACHE": "ON"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
}
],
"buildPresets": [
{ "name": "debug-windows", "configurePreset": "windows", "configuration": "Debug", "targets": ["JustASample_VST3"] },
{ "name": "release-windows", "configurePreset": "windows", "configuration": "Release", "targets": ["JustASample_VST3"] },
{ "name": "debug-linux", "configurePreset": "linux", "configuration": "Debug", "targets": ["JustASample_VST3"] },
{ "name": "release-linux", "configurePreset": "linux", "configuration": "Release", "targets": ["JustASample_VST3"] },
{ "name": "debug-macos", "configurePreset": "macos", "configuration": "Debug", "targets": ["JustASample_VST3", "JustASample_AU"] },
{ "name": "release-macos", "configurePreset": "macos", "configuration": "Release", "targets": ["JustASample_VST3", "JustASample_AU"] }
]
}

237
External/Gin/gin_distortion.h vendored Normal file
View file

@ -0,0 +1,237 @@
/*
==============================================================================
This file is part of the GIN library.
Copyright (c) 2020 - Roland Rabien.
MIT License
Copyright (c) 2018 Chris Johnson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
namespace gin
{
//==============================================================================
/** Distortion based on AirWindows plugins
*/
class AirWindowsDistortion
{
public:
AirWindowsDistortion()
{
reset();
}
void setSampleRate(double sr)
{
sampleRate = sr;
}
void reset()
{
A = 0.2f;
B = 0.0f;
C = 1.0f;
D = 1.0f;
iirSampleAL = 0.0f;
iirSampleBL = 0.0f;
iirSampleAR = 0.0f;
iirSampleBR = 0.0f;
fpFlip = true;
fpNShapeL = 0.0f;
fpNShapeR = 0.0f;
}
void setParams(float density, float highpass, float output, float mix)
{
A = density;
B = highpass;
C = output;
D = mix;
}
void process(float* l, float* r, int sampleFrames)
{
double overallscale = 1.0;
overallscale /= 44100.0;
overallscale *= sampleRate;
double density = (A * 5.0) - 1.0;
double iirAmount = pow(B, 3) / overallscale;
double output = C;
double wet = D;
double dry = 1.0 - wet;
double bridgerectifier;
double out = fabs(density);
density = density * fabs(density);
double count;
long double inputSampleL;
long double inputSampleR;
long double drySampleL;
long double drySampleR;
while (--sampleFrames >= 0)
{
inputSampleL = *l;
inputSampleR = *r;
if (inputSampleL < 1.2e-38 && -inputSampleL < 1.2e-38) {
static int noisesource = 0;
//this declares a variable before anything else is compiled. It won't keep assigning
//it to 0 for every sample, it's as if the declaration doesn't exist in this context,
//but it lets me add this denormalization fix in a single place rather than updating
//it in three different locations. The variable isn't thread-safe but this is only
//a random seed and we can share it with whatever.
noisesource = noisesource % 1700021; noisesource++;
int residue = noisesource * noisesource;
residue = residue % 170003; residue *= residue;
residue = residue % 17011; residue *= residue;
residue = residue % 1709; residue *= residue;
residue = residue % 173; residue *= residue;
residue = residue % 17;
double applyresidue = residue;
applyresidue *= 0.00000001;
applyresidue *= 0.00000001;
inputSampleL = applyresidue;
}
if (inputSampleR < 1.2e-38 && -inputSampleR < 1.2e-38) {
static int noisesource = 0;
noisesource = noisesource % 1700021; noisesource++;
int residue = noisesource * noisesource;
residue = residue % 170003; residue *= residue;
residue = residue % 17011; residue *= residue;
residue = residue % 1709; residue *= residue;
residue = residue % 173; residue *= residue;
residue = residue % 17;
double applyresidue = residue;
applyresidue *= 0.00000001;
applyresidue *= 0.00000001;
inputSampleR = applyresidue;
//this denormalization routine produces a white noise at -300 dB which the noise
//shaping will interact with to produce a bipolar output, but the noise is actually
//all positive. That should stop any variables from going denormal, and the routine
//only kicks in if digital black is input. As a final touch, if you save to 24-bit
//the silence will return to being digital black again.
}
drySampleL = inputSampleL;
drySampleR = inputSampleR;
if (fpFlip)
{
iirSampleAL = double((iirSampleAL * (1.0 - iirAmount)) + (inputSampleL * iirAmount));
inputSampleL -= iirSampleAL;
iirSampleAR = double((iirSampleAR * (1.0 - iirAmount)) + (inputSampleR * iirAmount));
inputSampleR -= iirSampleAR;
}
else
{
iirSampleBL = double((iirSampleBL * (1.0 - iirAmount)) + (inputSampleL * iirAmount));
inputSampleL -= iirSampleBL;
iirSampleBR = double((iirSampleBR * (1.0 - iirAmount)) + (inputSampleR * iirAmount));
inputSampleR -= iirSampleBR;
}
//highpass section
fpFlip = !fpFlip;
count = density;
while (count > 1.0)
{
bridgerectifier = double(fabs(inputSampleL) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
bridgerectifier = sin(bridgerectifier);
if (inputSampleL > 0.0) inputSampleL = bridgerectifier;
else inputSampleL = -bridgerectifier;
bridgerectifier = double(fabs(inputSampleR) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
bridgerectifier = sin(bridgerectifier);
if (inputSampleR > 0.0) inputSampleR = bridgerectifier;
else inputSampleR = -bridgerectifier;
count = count - 1.0;
}
//we have now accounted for any really high density settings.
while (out > 1.0) out = out - 1.0;
bridgerectifier = double(fabs(inputSampleL) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (density > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1 - cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleL > 0) inputSampleL = (inputSampleL * (1 - out)) + (bridgerectifier * out);
else inputSampleL = (inputSampleL * (1 - out)) - (bridgerectifier * out);
//blend according to density control
bridgerectifier = double(fabs(inputSampleR) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (density > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1 - cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleR > 0) inputSampleR = (inputSampleR * (1.0 - out)) + (bridgerectifier * out);
else inputSampleR = (inputSampleR * (1.0 - out)) - (bridgerectifier * out);
//blend according to density control
if (output < 1.0) {
inputSampleL *= output;
inputSampleR *= output;
}
if (wet < 1.0) {
inputSampleL = (drySampleL * dry) + (inputSampleL * wet);
inputSampleR = (drySampleR * dry) + (inputSampleR * wet);
}
//nice little output stage template: if we have another scale of floating point
//number, we really don't want to meaninglessly multiply that by 1.0.
//stereo 32 bit dither, made small and tidy.
int expon; frexpf((float)inputSampleL, &expon);
long double dither = (rand() / (RAND_MAX * 7.737125245533627e+25)) * pow(2, expon + 62);
inputSampleL += (dither - fpNShapeL); fpNShapeL = dither;
frexpf((float)inputSampleR, &expon);
dither = (rand() / (RAND_MAX * 7.737125245533627e+25)) * pow(2, expon + 62);
inputSampleR += (dither - fpNShapeR); fpNShapeR = dither;
//end 32 bit dither
*l = float(inputSampleL);
*r = float(inputSampleR);
l++;
r++;
}
}
private:
double sampleRate = 44100.0;
long double fpNShapeL, fpNShapeR;
double iirSampleAL, iirSampleBL, iirSampleAR, iirSampleBR;
bool fpFlip;
float A, B, C, D;
};
} // namespace gin

328
External/Gin/gin_simpleverb.cpp vendored Normal file
View file

@ -0,0 +1,328 @@
/*
==============================================================================
This file is part of the GIN library.
Copyright (c) 2019 - Roland Rabien.
==============================================================================
*/
#include "gin_simpleverb.h"
gin::SimpleVerb::SimpleVerb()
{
roomSizeFader = 0.5;
roomSize = 55;
preDelayFader = 0;
preDelayLength = 0;
preDelayPos = 0;
dampFader = 0.5;
damp = 0.25;
freqLPFader = 1;
freqHPFader = 0;
freqLP = 24000;
freqHP = 0;
b1LP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqLP / sampleRate); // 100Hz
a0LP = 1.0f + b1LP;
b1HP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqHP / sampleRate); // 100Hz
a0HP = 1.0f + b1HP;
dry = 1;
wet = 0.5;
setSampleRate(44100);
}
void gin::SimpleVerb::setSampleRate(float sr)
{
constexpr float roomMaxSize = 100.0f;
sampleRate = sr;
auto comb1MaxLength = static_cast<unsigned int>(C1 * roomMaxSize * sampleRate / 1000);
comb1.resize(comb1MaxLength);
auto comb2MaxLength = static_cast<unsigned int>(C2 * roomMaxSize * sampleRate / 1000);
comb2.resize(comb2MaxLength);
auto comb3MaxLength = static_cast<unsigned int>(C3 * roomMaxSize * sampleRate / 1000);
comb3.resize(comb3MaxLength);
auto comb4MaxLength = static_cast<unsigned int>(C4 * roomMaxSize * sampleRate / 1000);
comb4.resize(comb4MaxLength);
auto comb5MaxLength = static_cast<unsigned int>(C5 * roomMaxSize * sampleRate / 1000);
comb5.resize(comb5MaxLength);
auto comb6MaxLength = static_cast<unsigned int>(C6 * roomMaxSize * sampleRate / 1000);
comb6.resize(comb6MaxLength);
auto comb7MaxLength = static_cast<unsigned int>(C7 * roomMaxSize * sampleRate / 1000);
comb7.resize(comb7MaxLength);
auto comb8MaxLength = static_cast<unsigned int>(C8 * roomMaxSize * sampleRate / 1000);
comb8.resize(comb8MaxLength);
auto comb9MaxLength = static_cast<unsigned int>(C9 * roomMaxSize * sampleRate / 1000);
comb9.resize(comb9MaxLength);
auto comb10MaxLength = static_cast<unsigned int>(C10 * roomMaxSize * sampleRate / 1000);
comb10.resize(comb10MaxLength);
auto comb11MaxLength = static_cast<unsigned int>(C11 * roomMaxSize * sampleRate / 1000);
comb11.resize(comb11MaxLength);
auto comb12MaxLength = static_cast<unsigned int>(C12 * roomMaxSize * sampleRate / 1000);
comb12.resize(comb12MaxLength);
allpassL1Length = static_cast<unsigned int>(AL1 * sampleRate / 1000);
allpassL1.resize(allpassL1Length);
allpassL2Length = static_cast<unsigned int>((AL2 + SW) * sampleRate / 1000);
allpassL2.resize(allpassL2Length);
allpassL3Length = static_cast<unsigned int>(AL3 * sampleRate / 1000);
allpassL3.resize(allpassL3Length);
allpassR1Length = static_cast<unsigned int>((AR1 + SW) * sampleRate / 1000);
allpassR1.resize(allpassR1Length);
allpassR2Length = static_cast<unsigned int>(AR2 * sampleRate / 1000);
allpassR2.resize(allpassR2Length);
allpassR3Length = static_cast<unsigned int>((AR3 + SW) * sampleRate / 1000);
allpassR3.resize(allpassR3Length);
auto preDelayMaxLength = static_cast<unsigned int>(500 * sampleRate / 1000);
preDelay.resize(preDelayMaxLength);
flushPreDelay();
flushBuffers();
allpassL1Pos = allpassL2Pos = allpassL3Pos = allpassR1Pos = allpassR2Pos = allpassR3Pos = 0;
tmp1LP = tmp2LP = tmp1HP = tmp2HP = 0;
comb1Pos = comb2Pos = comb3Pos = comb4Pos = comb5Pos = comb6Pos = comb7Pos = comb8Pos = 0;
comb9Pos = comb10Pos = comb11Pos = comb12Pos = 0;
preDelayPos = 0;
}
void gin::SimpleVerb::flushPreDelay()
{
std::fill(preDelay.begin(), preDelay.end(), 0.0f);
}
void gin::SimpleVerb::flushBuffers()
{
std::fill(comb1.begin(), comb1.end(), 0.0f);
std::fill(comb2.begin(), comb2.end(), 0.0f);
std::fill(comb3.begin(), comb3.end(), 0.0f);
std::fill(comb4.begin(), comb4.end(), 0.0f);
std::fill(comb5.begin(), comb5.end(), 0.0f);
std::fill(comb6.begin(), comb6.end(), 0.0f);
std::fill(comb7.begin(), comb7.end(), 0.0f);
std::fill(comb8.begin(), comb8.end(), 0.0f);
std::fill(comb9.begin(), comb9.end(), 0.0f);
std::fill(comb10.begin(), comb10.end(), 0.0f);
std::fill(comb11.begin(), comb11.end(), 0.0f);
std::fill(comb12.begin(), comb12.end(), 0.0f);
std::fill(allpassL1.begin(), allpassL1.end(), 0.0f);
std::fill(allpassL2.begin(), allpassL2.end(), 0.0f);
std::fill(allpassL3.begin(), allpassL3.end(), 0.0f);
std::fill(allpassR1.begin(), allpassR1.end(), 0.0f);
std::fill(allpassR2.begin(), allpassR2.end(), 0.0f);
std::fill(allpassR3.begin(), allpassR3.end(), 0.0f);
}
void gin::SimpleVerb::setParameters(float roomIn, float dampIn, float preDelayIn, float lpFaderIn, float hpFaderIn, float wetIn, float dryIn)
{
if (!juce::approximatelyEqual(roomIn, roomSizeFader))
{
roomSizeFader = roomIn;
roomSize = 5 + roomSizeFader * roomSizeFader * 95;
comb1Length = static_cast<unsigned int>(C1 * roomSize * sampleRate / 1000);
comb1Pos = 0;
comb2Length = static_cast<unsigned int>(C2 * roomSize * sampleRate / 1000);
comb2Pos = 0;
comb3Length = static_cast<unsigned int>(C3 * roomSize * sampleRate / 1000);
comb3Pos = 0;
comb4Length = static_cast<unsigned int>(C4 * roomSize * sampleRate / 1000);
comb4Pos = 0;
comb5Length = static_cast<unsigned int>(C5 * roomSize * sampleRate / 1000);
comb5Pos = 0;
comb6Length = static_cast<unsigned int>(C6 * roomSize * sampleRate / 1000);
comb6Pos = 0;
comb7Length = static_cast<unsigned int>(C7 * roomSize * sampleRate / 1000);
comb7Pos = 0;
comb8Length = static_cast<unsigned int>(C8 * roomSize * sampleRate / 1000);
comb8Pos = 0;
comb9Length = static_cast<unsigned int>(C9 * roomSize * sampleRate / 1000);
comb9Pos = 0;
comb10Length = static_cast<unsigned int>(C10 * roomSize * sampleRate / 1000);
comb10Pos = 0;
comb11Length = static_cast<unsigned int>(C11 * roomSize * sampleRate / 1000);
comb11Pos = 0;
comb12Length = static_cast<unsigned int>(C12 * roomSize * sampleRate / 1000);
comb12Pos = 0;
flushBuffers();
}
if (!juce::approximatelyEqual(dampIn, dampFader))
{
dampFader = dampIn;
damp = std::min(1.0f - dampFader * dampFader, 0.95f);
}
if (!juce::approximatelyEqual(preDelayIn, preDelayFader))
{
preDelayFader = preDelayIn;
preDelayLength = static_cast<unsigned int>(preDelayFader * preDelayFader * 250 * sampleRate / 1000);
preDelayPos = 0;
flushPreDelay();
}
if (!juce::approximatelyEqual(lpFaderIn, freqLPFader))
{
freqLPFader = lpFaderIn;
freqLP = freqLPFader * freqLPFader * freqLPFader * 24000;
b1LP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqLP / sampleRate); // 100Hz
a0LP = 1.0f + b1LP;
}
if (!juce::approximatelyEqual(hpFaderIn, freqHPFader))
{
freqHPFader = hpFaderIn;
freqHP = freqHPFader * freqHPFader * freqHPFader * 24000;
b1HP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqHP / sampleRate); // 100Hz
a0HP = 1.0f + b1HP;
}
if (!juce::approximatelyEqual(dryIn, dryFader))
{
dryFader = dryIn;
dry = dryFader * 2;
}
if (!juce::approximatelyEqual(wetIn, wetFader))
{
wetFader = wetIn;
wet = wetFader * 2;
}
}
void gin::SimpleVerb::process(const float* in1, const float* in2, float* out1, float* out2, int numSamples)
{
int sampleFrames = numSamples;
while (--sampleFrames >= 0)
{
if (preDelayLength <= 1)
{
reverb = ((*in1) + (*in2)) / (1 + damp) + cDC_;
}
else
{
preDelay[preDelayPos] = ((*in1) + (*in2)) / (1 + damp) + cDC_;
if (++preDelayPos >= preDelayLength)
preDelayPos = 0;
reverb = preDelay[preDelayPos];
}
comb1[comb1Pos] = reverb * 0.49f + comb1[comb1Pos] * damp;
comb2[comb2Pos] = reverb * 0.76f + comb2[comb2Pos] * damp;
comb3[comb3Pos] = reverb * 1.00f + comb3[comb3Pos] * damp;
comb4[comb4Pos] = reverb * 0.91f + comb4[comb4Pos] * damp;
comb5[comb5Pos] = reverb * 0.79f + comb5[comb5Pos] * damp;
comb6[comb6Pos] = reverb * 0.71f + comb6[comb6Pos] * damp;
comb7[comb7Pos] = reverb * 0.59f + comb7[comb7Pos] * damp;
comb8[comb8Pos] = reverb * 0.51f + comb8[comb8Pos] * damp;
comb9[comb9Pos] = reverb * 0.42f + comb9[comb9Pos] * damp;
comb10[comb10Pos] = reverb * 0.38f + comb10[comb10Pos] * damp;
comb11[comb11Pos] = reverb * 0.35f + comb11[comb11Pos] * damp;
comb12[comb12Pos] = reverb * 0.30f + comb12[comb12Pos] * damp;
if (++comb1Pos >= comb1Length) comb1Pos = 0;
if (++comb2Pos >= comb2Length) comb2Pos = 0;
if (++comb3Pos >= comb3Length) comb3Pos = 0;
if (++comb4Pos >= comb4Length) comb4Pos = 0;
if (++comb5Pos >= comb5Length) comb5Pos = 0;
if (++comb6Pos >= comb6Length) comb6Pos = 0;
if (++comb7Pos >= comb7Length) comb7Pos = 0;
if (++comb8Pos >= comb8Length) comb8Pos = 0;
if (++comb9Pos >= comb9Length) comb9Pos = 0;
if (++comb10Pos >= comb10Length) comb10Pos = 0;
if (++comb11Pos >= comb11Length) comb11Pos = 0;
if (++comb12Pos >= comb12Length) comb12Pos = 0;
reverb = (comb1[comb1Pos]
+ comb2[comb2Pos]
+ comb3[comb3Pos]
+ comb4[comb4Pos]
+ comb5[comb5Pos]
+ comb6[comb6Pos]
+ comb7[comb7Pos]
+ comb8[comb8Pos]
+ comb9[comb9Pos]
+ comb10[comb10Pos]
+ comb11[comb11Pos]
+ comb12[comb12Pos]);
jassert(!std::isnan(reverb) && !std::isinf(reverb));
allpassL1[allpassL1Pos] = reverb + allpassL1[allpassL1Pos] * AP1FBQ;
left = (reverb - allpassL1[allpassL1Pos] * AP1FBQ);
jassert(!std::isnan(left) && !std::isinf(left));
if (++allpassL1Pos >= allpassL1Length)
allpassL1Pos = 0;
allpassL2[allpassL2Pos] = left + allpassL2[allpassL2Pos] * AP2FBQ;
left = (left - allpassL2[allpassL2Pos] * AP2FBQ);
jassert(!std::isnan(left) && !std::isinf(left));
if (++allpassL2Pos >= allpassL2Length)
allpassL2Pos = 0;
allpassL3[allpassL3Pos] = left + allpassL3[allpassL3Pos] * AP3FBQ;
left = (left - allpassL3[allpassL3Pos] * AP3FBQ);
jassert(!std::isnan(left) && !std::isinf(left));
if (++allpassL3Pos >= allpassL3Length)
allpassL3Pos = 0;
allpassR1[allpassR1Pos] = reverb + allpassR1[allpassR1Pos] * AP1FBQ;
right = (reverb - allpassR1[allpassR1Pos] * AP1FBQ);
jassert(!std::isnan(right) && !std::isinf(right));
if (++allpassR1Pos >= allpassR1Length)
allpassR1Pos = 0;
allpassR2[allpassR2Pos] = right + allpassR2[allpassR2Pos] * AP2FBQ;
right = (right - allpassR2[allpassR2Pos] * AP2FBQ);
jassert(!std::isnan(right) && !std::isinf(right));
if (++allpassR2Pos >= allpassR2Length)
allpassR2Pos = 0;
allpassR3[allpassR3Pos] = right + allpassR3[allpassR3Pos] * AP3FBQ;
right = (right - allpassR3[allpassR3Pos] * AP3FBQ);
jassert(!std::isnan(right) && !std::isinf(right));
if (++allpassR3Pos >= allpassR3Length)
allpassR3Pos = 0;
if (!juce::approximatelyEqual(freqHPFader, 0.0f))
{
left -= (tmp1HP = a0HP * left - b1HP * tmp1HP + cDC_) - cDC_;
right -= (tmp2HP = a0HP * right - b1HP * tmp2HP + cDC_) - cDC_;
jassert(!std::isnan(left) && !std::isinf(left));
jassert(!std::isnan(right) && !std::isinf(right));
}
if (!juce::approximatelyEqual(freqLPFader, 1.0f))
{
left = (tmp1LP = a0LP * left - b1LP * tmp1LP + cDC_) - cDC_;
right = (tmp2LP = a0LP * right - b1LP * tmp2LP + cDC_) - cDC_;
jassert(!std::isnan(left) && !std::isinf(left));
jassert(!std::isnan(right) && !std::isinf(right));
}
(*out1++) = (*in1++) * dry + left * wet;
(*out2++) = (*in2++) * dry + right * wet;
}
}

134
External/Gin/gin_simpleverb.h vendored Normal file
View file

@ -0,0 +1,134 @@
/*
==============================================================================
This file is part of the GIN library.
Copyright (c) 2019 - Roland Rabien.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
namespace gin
{
/** Simple Reverb
Copyright (c) 2006-2008 and 2012, Michael "LOSER" Gruhn
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
class SimpleVerb
{
public:
SimpleVerb();
void setSampleRate(float sr);
void process(const float* in1, const float* in2, float* out1, float* out2, int numSamples);
void setParameters(float roomIn, float dampIn, float preDelayIn, float lpFaderIn, float hpFaderIn, float wetIn, float dryIn);
private:
void flushPreDelay();
void flushBuffers();
unsigned int preDelayPos, preDelayLength;
std::vector<float> preDelay;
float preDelayFader;
unsigned int comb1Pos, comb1Length;
std::vector<float> comb1;
unsigned int comb2Pos, comb2Length;
std::vector<float> comb2;
unsigned int comb3Pos, comb3Length;
std::vector<float> comb3;
unsigned int comb4Pos, comb4Length;
std::vector<float> comb4;
unsigned int comb5Pos, comb5Length;
std::vector<float> comb5;
unsigned int comb6Pos, comb6Length;
std::vector<float> comb6;
unsigned int comb7Pos, comb7Length;
std::vector<float> comb7;
unsigned int comb8Pos, comb8Length;
std::vector<float> comb8;
unsigned int comb9Pos, comb9Length;
std::vector<float> comb9;
unsigned int comb10Pos, comb10Length;
std::vector<float> comb10;
unsigned int comb11Pos, comb11Length;
std::vector<float> comb11;
unsigned int comb12Pos, comb12Length;
std::vector<float> comb12;
unsigned int allpassL1Pos, allpassL1Length;
std::vector<float> allpassL1;
unsigned int allpassL2Pos, allpassL2Length;
std::vector<float> allpassL2;
unsigned int allpassL3Pos, allpassL3Length;
std::vector<float> allpassL3;
unsigned int allpassR1Pos, allpassR1Length;
std::vector<float> allpassR1;
unsigned int allpassR2Pos, allpassR2Length;
std::vector<float> allpassR2;
unsigned int allpassR3Pos, allpassR3Length;
std::vector<float> allpassR3;
float reverb, damp, dry, wet, left, right;
float roomSize = -1.0f;
float roomSizeFader = -1.0f;
float dampFader = -1.0f;
float dryFader = -1.0f;
float wetFader = -1.0f;
float sampleRate = 44100.0f;
float freqLP, freqLPFader;
float freqHP, freqHPFader;
float a0LP, b1LP, tmp1LP, tmp2LP;
float a0HP, b1HP, tmp1HP, tmp2HP;
static constexpr float cDC_ = 1e-30f;
static constexpr float C1 = 1.00f;
static constexpr float C2 = 1.09f;
static constexpr float C3 = 1.16f;
static constexpr float C4 = 1.23f;
static constexpr float C5 = 1.32f;
static constexpr float C6 = 1.41f;
static constexpr float C7 = 1.45f;
static constexpr float C8 = 1.56f;
static constexpr float C9 = 1.66f;
static constexpr float C10 = 1.71f;
static constexpr float C11 = 1.80f;
static constexpr float C12 = 1.90f;
static constexpr float AL1 = 1.0f;
static constexpr float AL2 = 2.5f;
static constexpr float AL3 = 5.0f;
static constexpr float AR1 = 1.0f;
static constexpr float AR2 = 2.5f;
static constexpr float AR3 = 5.0f;
static constexpr float SW = 1.0f;
static constexpr float AP1FBQ = 0.6f;
static constexpr float AP2FBQ = 0.6f;
static constexpr float AP3FBQ = 0.6f;
};
} // namespace gin

958
External/MTS/libMTSClient.cpp vendored Normal file
View file

@ -0,0 +1,958 @@
/*
Copyright (C) 2021 by ODDSound Ltd. info@oddsound.com
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
#include "libMTSClient.h"
#include <math.h>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__TOS_WIN__) || defined(_MSC_VER)
#define MTS_ESP_WIN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef HRESULT (WINAPI* SHGetKnownFolderPathFunc) (const GUID*, DWORD, HANDLE, PWSTR*);
typedef void (WINAPI* CoTaskMemFreeFunc) (LPVOID);
#else
#include <dlfcn.h>
#endif
const static int libMTSVersion = 0x00010003;
const static double ln2 = 0.693147180559945309417;
const static double ratioToSemitones = 17.31234049066756088832; // 12.0 / log(2.0)
typedef void (*mts_void__void)(void);
typedef bool (*mts_bool__void)(void);
typedef int (*mts_int__void)(void);
typedef bool (*mts_bool__char_char)(char, char);
typedef const double *(*mts_pConstDouble__void)(void);
typedef const double *(*mts_pConstDouble__char)(char);
typedef bool (*mts_bool__char)(char);
typedef const char *(*mts_pConstChar__void)(void);
typedef double (*mts_double__void)(void);
typedef char (*mts_char__void)(void);
struct mtsclientglobal
{
mtsclientglobal()
: RegisterClient(0)
, DeregisterClient(0)
, HasMaster(0)
, GetVersionNumber(0)
, ShouldFilterNote(0)
, ShouldFilterNoteMultiChannel(0)
, GetTuning(0)
, GetMultiChannelTuning(0)
, UseMultiChannelTuning(0)
, GetScaleName(0)
, GetPeriodRatio(0)
, GetMapSize(0)
, GetMapStartKey(0)
, GetRefKey(0)
, esp_retuning(0)
, handle(0)
{
for (int i = 0; i < 128; i++)
iet[i] = 1. / (440.0 * pow(2.0, (i - 69.0) / 12.0));
load_lib();
if (GetTuning)
esp_retuning = GetTuning();
for (int i = 0; i < 16; i++)
multi_channel_esp_retuning[i] = GetMultiChannelTuning ? GetMultiChannelTuning(static_cast<char>(i)) : 0;
}
inline bool isOnline() const {return esp_retuning && HasMaster && HasMaster();}
// interface to lib
mts_void__void RegisterClient;
mts_void__void DeregisterClient;
mts_bool__void HasMaster;
mts_int__void GetVersionNumber;
mts_bool__char_char ShouldFilterNote;
mts_bool__char_char ShouldFilterNoteMultiChannel;
mts_pConstDouble__void GetTuning;
mts_pConstDouble__char GetMultiChannelTuning;
mts_bool__char UseMultiChannelTuning;
mts_pConstChar__void GetScaleName;
mts_double__void GetPeriodRatio;
mts_char__void GetMapSize;
mts_char__void GetMapStartKey;
mts_char__void GetRefKey;
// tuning tables
double iet[128];
const double *esp_retuning;
const double *multi_channel_esp_retuning[16];
#ifdef MTS_ESP_WIN
void load_lib()
{
SHGetKnownFolderPathFunc SHGetKnownFolderPath = 0;
CoTaskMemFreeFunc CoTaskMemFree = 0;
HMODULE shell32Module = GetModuleHandleW(L"Shell32.dll");
HMODULE ole32Module = GetModuleHandleW(L"Ole32.dll");
if (shell32Module)
SHGetKnownFolderPath = (SHGetKnownFolderPathFunc)GetProcAddress(shell32Module, "SHGetKnownFolderPath");
if (ole32Module)
CoTaskMemFree = (CoTaskMemFreeFunc)GetProcAddress(ole32Module, "CoTaskMemFree");
if (SHGetKnownFolderPath && CoTaskMemFree)
{
const GUID FOLDERID_ProgramFilesCommonGUID = {0xF7F1ED05, 0x9F6D, 0x47A2, 0xAA, 0xAE, 0x29, 0xD3, 0x17, 0xC6, 0xF0, 0x66};
PWSTR cf = NULL;
if (SHGetKnownFolderPath(&FOLDERID_ProgramFilesCommonGUID, 0, 0, &cf) >= 0)
{
WCHAR buffer[MAX_PATH];
buffer[0] = L'\0';
if (cf)
wcsncpy(buffer, cf, MAX_PATH);
CoTaskMemFree(cf);
buffer[MAX_PATH - 1] = L'\0';
const WCHAR *libpath = L"\\MTS-ESP\\LIBMTS.dll";
DWORD cfLen = wcslen(buffer);
wcsncat(buffer, libpath, MAX_PATH - cfLen - 1);
handle = LoadLibraryW(buffer);
if (!handle)
return;
}
else
{
CoTaskMemFree(cf);
return;
}
}
else
{
return;
}
RegisterClient = (mts_void__void) GetProcAddress(handle, "MTS_RegisterClient");
DeregisterClient = (mts_void__void) GetProcAddress(handle, "MTS_DeregisterClient");
HasMaster = (mts_bool__void) GetProcAddress(handle, "MTS_HasMaster");
GetVersionNumber = (mts_int__void) GetProcAddress(handle, "MTS_GetVersionNumber");
ShouldFilterNote = (mts_bool__char_char) GetProcAddress(handle, "MTS_ShouldFilterNote");
ShouldFilterNoteMultiChannel = (mts_bool__char_char) GetProcAddress(handle, "MTS_ShouldFilterNoteMultiChannel");
GetTuning = (mts_pConstDouble__void) GetProcAddress(handle, "MTS_GetTuningTable");
GetMultiChannelTuning = (mts_pConstDouble__char) GetProcAddress(handle, "MTS_GetMultiChannelTuningTable");
UseMultiChannelTuning = (mts_bool__char) GetProcAddress(handle, "MTS_UseMultiChannelTuning");
GetScaleName = (mts_pConstChar__void) GetProcAddress(handle, "MTS_GetScaleName");
GetPeriodRatio = (mts_double__void) GetProcAddress(handle, "MTS_GetPeriodRatio");
GetMapSize = (mts_char__void) GetProcAddress(handle, "MTS_GetMapSize");
GetMapStartKey = (mts_char__void) GetProcAddress(handle, "MTS_GetMapStartKey");
GetRefKey = (mts_char__void) GetProcAddress(handle, "MTS_GetRefKey");
}
~mtsclientglobal()
{
if (handle)
FreeLibrary(handle);
}
HINSTANCE handle;
#else
void load_lib()
{
if (!(handle = dlopen("/Library/Application Support/MTS-ESP/libMTS.dylib", RTLD_NOW)) &&
!(handle = dlopen("/usr/local/lib/libMTS.so", RTLD_NOW)))
{
return;
}
RegisterClient = (mts_void__void) dlsym(handle, "MTS_RegisterClient");
DeregisterClient = (mts_void__void) dlsym(handle, "MTS_DeregisterClient");
HasMaster = (mts_bool__void) dlsym(handle, "MTS_HasMaster");
GetVersionNumber = (mts_int__void) dlsym(handle, "MTS_GetVersionNumber");
ShouldFilterNote = (mts_bool__char_char) dlsym(handle, "MTS_ShouldFilterNote");
ShouldFilterNoteMultiChannel = (mts_bool__char_char) dlsym(handle, "MTS_ShouldFilterNoteMultiChannel");
GetTuning = (mts_pConstDouble__void) dlsym(handle, "MTS_GetTuningTable");
GetMultiChannelTuning = (mts_pConstDouble__char) dlsym(handle, "MTS_GetMultiChannelTuningTable");
UseMultiChannelTuning = (mts_bool__char) dlsym(handle, "MTS_UseMultiChannelTuning");
GetScaleName = (mts_pConstChar__void) dlsym(handle, "MTS_GetScaleName");
GetPeriodRatio = (mts_double__void) dlsym(handle, "MTS_GetPeriodRatio");
GetMapSize = (mts_char__void) dlsym(handle, "MTS_GetMapSize");
GetMapStartKey = (mts_char__void) dlsym(handle, "MTS_GetMapStartKey");
GetRefKey = (mts_char__void) dlsym(handle, "MTS_GetRefKey");
}
~mtsclientglobal()
{
if (handle)
dlclose(handle);
}
void *handle;
#endif
};
static mtsclientglobal global;
struct MTSClient
{
struct Tuning
{
enum {eRatioValid = 1, eSemitonesValid = 1 << 1};
int flags;
double freq; // always valid
double ratio;
double semitones;
};
MTSClient()
: tuningName("12-TET")
, periodRatioLocal(2.0)
, periodSemitones(12.0)
, mapSizeLocal(static_cast<char>(-1))
, mapStartKeyLocal(static_cast<char>(-1))
, supportsNoteFiltering(false)
, supportsMultiChannelNoteFiltering(false)
, supportsMultiChannelTuning(false)
, freqRequestReceived(false)
, receivedMTSSysEx(false)
{
for (int i = 0; i < 128; i++)
{
localFreqs[i] = 440.0 * pow(2.0, (i - 69.0) / 12.0);
localTunings[i].flags = 0;
localTunings[i].freq = localFreqs[i];
globalTunings[i].flags = 0;
globalTunings[i].freq = localFreqs[i];
}
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 128; j++)
{
globalMultichannelTunings[i][j].flags = 0;
globalMultichannelTunings[i][j].freq = localFreqs[i];
}
}
if (global.RegisterClient)
global.RegisterClient();
}
~MTSClient()
{
if (global.DeregisterClient)
global.DeregisterClient();
}
inline bool hasMaster() {return global.isOnline();}
inline bool shouldUpdateLibrary() {return global.GetVersionNumber ? (global.GetVersionNumber() < libMTSVersion) : false;}
inline double freq(char midinote, char midichannel)
{
int note = midinote & 127;
int channel = midichannel & 15;
freqRequestReceived = true;
supportsMultiChannelTuning = !(midichannel & ~15);
if (!global.isOnline())
return localTunings[note].freq;
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[channel])
{
globalMultichannelTunings[channel][note].freq = global.multi_channel_esp_retuning[channel][note];
globalMultichannelTunings[channel][note].flags = 0;
return globalMultichannelTunings[channel][note].freq;
}
globalTunings[note].freq = global.esp_retuning[note];
globalTunings[note].flags = 0;
return globalTunings[note].freq;
}
inline double ratio(char midinote, char midichannel)
{
int note = midinote & 127;
int channel = midichannel & 15;
freqRequestReceived = true;
supportsMultiChannelTuning = !(midichannel & ~15);
if (!global.isOnline())
{
if (!receivedMTSSysEx)
return 1.0;
if (localTunings[note].flags & Tuning::eRatioValid)
return localTunings[note].ratio;
localTunings[note].ratio = localTunings[note].freq * global.iet[note];
localTunings[note].flags |= Tuning::eRatioValid;
return localTunings[note].ratio;
}
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[channel])
{
double freq = global.multi_channel_esp_retuning[channel][note];
if (globalMultichannelTunings[channel][note].freq == freq &&
(globalMultichannelTunings[channel][note].flags & Tuning::eRatioValid))
{
return globalMultichannelTunings[channel][note].ratio;
}
globalMultichannelTunings[channel][note].freq = global.multi_channel_esp_retuning[channel][note];
globalMultichannelTunings[channel][note].ratio = globalMultichannelTunings[channel][note].freq * global.iet[note];
globalMultichannelTunings[channel][note].flags = Tuning::eRatioValid;
return globalMultichannelTunings[channel][note].ratio;
}
double freq = global.esp_retuning[note];
if (globalTunings[note].freq == freq &&
(globalTunings[note].flags & Tuning::eRatioValid))
{
return globalTunings[note].ratio;
}
globalTunings[note].freq = global.esp_retuning[note];
globalTunings[note].ratio = globalTunings[note].freq * global.iet[note];
globalTunings[note].flags = Tuning::eRatioValid;
return globalTunings[note].ratio;
}
inline double semitones(char midinote, char midichannel)
{
int note = midinote & 127;
int channel = midichannel & 15;
freqRequestReceived = true;
supportsMultiChannelTuning = !(midichannel & ~15);
if (!global.isOnline())
{
if (!receivedMTSSysEx)
return 0.0;
if (localTunings[note].flags & Tuning::eSemitonesValid)
return localTunings[note].semitones;
if (localTunings[note].flags & Tuning::eRatioValid)
{
localTunings[note].semitones = ratioToSemitones * log(localTunings[note].ratio);
localTunings[note].flags |= Tuning::eSemitonesValid;
return localTunings[note].semitones;
}
localTunings[note].ratio = localTunings[note].freq * global.iet[note];
localTunings[note].semitones = ratioToSemitones * log(localTunings[note].ratio);
localTunings[note].flags |= Tuning::eRatioValid | Tuning::eSemitonesValid;
return localTunings[note].semitones;
}
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[channel])
{
double freq = global.multi_channel_esp_retuning[channel][note];
if (globalMultichannelTunings[channel][note].freq == freq)
{
if (globalMultichannelTunings[channel][note].flags & Tuning::eSemitonesValid)
return globalMultichannelTunings[channel][note].semitones;
if (globalMultichannelTunings[channel][note].flags & Tuning::eRatioValid)
{
globalMultichannelTunings[channel][note].semitones = ratioToSemitones * log(globalMultichannelTunings[channel][note].ratio);
globalMultichannelTunings[channel][note].flags |= Tuning::eSemitonesValid;
return globalMultichannelTunings[channel][note].semitones;
}
}
globalMultichannelTunings[channel][note].freq = freq;
globalMultichannelTunings[channel][note].ratio = freq * global.iet[note];
globalMultichannelTunings[channel][note].semitones = ratioToSemitones * log(globalMultichannelTunings[channel][note].ratio);
globalMultichannelTunings[channel][note].flags = Tuning::eRatioValid | Tuning::eSemitonesValid;
return globalMultichannelTunings[channel][note].semitones;
}
double freq = global.esp_retuning[note];
if (globalTunings[note].freq == freq)
{
if (globalTunings[note].flags & Tuning::eSemitonesValid)
return globalTunings[note].semitones;
if (globalTunings[note].flags & Tuning::eRatioValid)
{
globalTunings[note].semitones = ratioToSemitones * log(globalTunings[note].ratio);
globalTunings[note].flags |= Tuning::eSemitonesValid;
return globalTunings[note].semitones;
}
}
globalTunings[note].freq = freq;
globalTunings[note].ratio = freq * global.iet[note];
globalTunings[note].semitones = ratioToSemitones * log(globalTunings[note].ratio);
globalTunings[note].flags = Tuning::eRatioValid | Tuning::eSemitonesValid;
return globalTunings[note].semitones;
}
inline bool shouldFilterNote(char midinote, char midichannel)
{
supportsNoteFiltering = true;
supportsMultiChannelNoteFiltering = !(midichannel & ~15);
if (!freqRequestReceived)
supportsMultiChannelTuning = supportsMultiChannelNoteFiltering; // assume it supports multi channel tuning until a request is received for a frequency and can verify
if (!global.isOnline())
return false;
if (supportsMultiChannelNoteFiltering &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel))
{
return global.ShouldFilterNoteMultiChannel ? global.ShouldFilterNoteMultiChannel(midinote & 127, midichannel) : false;
}
return global.ShouldFilterNote ? global.ShouldFilterNote(midinote & 127, midichannel) : false;
}
inline char freqToNote(double freq, char midichannel)
{
bool online = global.isOnline();
bool multiChannel = false;
const double *freqs = online ? global.esp_retuning : localFreqs;
if (online &&
!(midichannel & ~15) &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[midichannel & 15])
{
freqs = global.multi_channel_esp_retuning[midichannel & 15];
multiChannel = true;
}
int iLower = 0;
int iUpper = 0;
double dLower = 0.0;
double dUpper = 0.0;
for (int i = 0; i < 128; i++)
{
if (online)
{
if (multiChannel &&
global.ShouldFilterNoteMultiChannel &&
global.ShouldFilterNoteMultiChannel(static_cast<char>(i), midichannel))
{
continue;
}
if (!multiChannel &&
global.ShouldFilterNote &&
global.ShouldFilterNote(static_cast<char>(i), midichannel))
{
continue;
}
}
double d = freqs[i] - freq;
if (d == 0.0)
return static_cast<char>(i);
if (d < 0.0)
{
if (dLower == 0.0 || d > dLower)
{
dLower=d;
iLower=i;
}
}
else if (dUpper == 0.0 || d < dUpper)
{
dUpper = d;
iUpper = i;
}
}
if (dLower == 0.0)
return static_cast<char>(iUpper);
if (dUpper == 0.0 || iLower == iUpper)
return static_cast<char>(iLower);
double fmid = freqs[iLower] * pow(2.0, 0.5 * (log(freqs[iUpper] / freqs[iLower]) / ln2));
return freq < fmid ? static_cast<char>(iLower) : static_cast<char>(iUpper);
}
inline char freqToNote(double freq, char *midichannel)
{
if (!midichannel)
return freqToNote(freq, static_cast<char>(-1));
if (global.isOnline() && global.UseMultiChannelTuning)
{
int channelsInUse[16];
int nMultiChannels = 0;
for (int i = 0; i < 16; i++)
if (global.UseMultiChannelTuning(i) && global.multi_channel_esp_retuning[i])
channelsInUse[nMultiChannels++] = i;
if (nMultiChannels > 0)
{
const int nFreqs = 128 * nMultiChannels;
int iLower = 0;
int iUpper = 0;
int channel = 0;
int note = 0;
double dLower = 0.0;
double dUpper = 0.0;
for (int i = 0; i < nFreqs; i++)
{
channel = channelsInUse[i >> 7];
note = i & 127;
if (global.ShouldFilterNoteMultiChannel &&
global.ShouldFilterNoteMultiChannel(static_cast<char>(note), static_cast<char>(channel)))
{
continue;
}
double d = global.multi_channel_esp_retuning[channel][note] - freq;
if (d == 0.0)
{
*midichannel = static_cast<char>(channel);
return static_cast<char>(note);
}
if (d < 0.0)
{
if (dLower == 0.0 || d > dLower)
{
dLower = d;
iLower = i;
}
}
else if (dUpper == 0.0 || d < dUpper)
{
dUpper = d;
iUpper = i;
}
}
if (dLower==0.0)
{
*midichannel = static_cast<char>(channelsInUse[iUpper >> 7]);
return static_cast<char>(iUpper & 127);
}
if (dUpper == 0.0 || iLower == iUpper)
{
*midichannel = static_cast<char>(channelsInUse[iLower >> 7]);
return static_cast<char>(iLower & 127);
}
double fLower = global.multi_channel_esp_retuning[channelsInUse[iLower >> 7]][iLower & 127];
double fUpper = global.multi_channel_esp_retuning[channelsInUse[iUpper >> 7]][iUpper & 127];
double fmid = fLower * pow(2.0, 0.5 * (log(fUpper / fLower) / ln2));
if (freq < fmid)
{
*midichannel = static_cast<char>(channelsInUse[iLower >> 7]);
return static_cast<char>(iLower & 127);
}
*midichannel = static_cast<char>(channelsInUse[iUpper >> 7]);
return static_cast<char>(iUpper & 127);
}
}
*midichannel = static_cast<char>(0);
return freqToNote(freq, static_cast<char>(0));
}
inline void parseMIDIData(const unsigned char *buffer, int len)
{
int sysex_ctr = 0;
int sysex_value = 0;
int note = 0;
int numTunings = 0;
/*int bank = -1, prog = 0, checksum = 0, deviceID = 0; short int channelBitmap = 0; bool realtime = false;*/ // unused for now
eSysexState state = eIgnoring;
eMTSFormat format = eBulk;
for (int i = 0; i < len; i++)
{
unsigned char b = buffer[i];
if (b == 0xF7)
{
state = eIgnoring;
continue;
}
if (b > 0x7F && b != 0xF0)
continue;
switch (state)
{
case eIgnoring:
if (b == 0xF0)
state = eMatchingSysex;
break;
case eMatchingSysex:
sysex_ctr = 0;
if (b == 0x7E)
state = eSysexValid;
else if (b == 0x7F)
{
/*realtime = true;*/
state = eSysexValid;
}
else
{
state = eIgnoring;
}
break;
case eSysexValid:
switch (sysex_ctr++) // handle device ID
{
case 0:
/*deviceID = b;*/
break;
case 1:
if (b == 0x08)
state = eMatchingMTS;
break;
default: // it's not an MTS message
state = eIgnoring;
break;
}
break;
case eMatchingMTS:
sysex_ctr = 0;
switch (b)
{
case 0:
format = eRequest;
state = eMatchingProg;
break;
case 1:
format = eBulk;
state = eMatchingProg;
break;
case 2:
format = eSingle;
state = eMatchingProg;
break;
case 3:
format = eRequest;
state = eMatchingBank;
break;
case 4:
format = eBulk;
state = eMatchingBank;
break;
case 5:
format = eScaleOctOneByte;
state = eMatchingBank;
break;
case 6:
format = eScaleOctTwoByte;
state = eMatchingBank;
break;
case 7:
format = eSingle;
state = eMatchingBank;
break;
case 8:
format = eScaleOctOneByteExt;
state = eMatchingChannel;
break;
case 9:
format = eScaleOctTwoByteExt;
state = eMatchingChannel;
break;
default: // it's not a valid MTS format
state = eIgnoring;
break;
}
break;
case eMatchingBank:
/*bank = b;*/
state = eMatchingProg;
break;
case eMatchingProg:
/*prog = b;*/
if (format == eSingle)
{
state = eNumTunings;
}
else
{
state = eTuningName;
tuningName[0] = '\0';
}
break;
case eTuningName:
tuningName[sysex_ctr] = static_cast<char>(b);
if (++sysex_ctr >= 16)
{
tuningName[16] = '\0';
sysex_ctr = 0;
state = eTuningData;
}
break;
case eNumTunings:
numTunings = b;
sysex_ctr = 0;
state = eTuningData;
break;
case eMatchingChannel:
switch (sysex_ctr++)
{
case 0:
/*for (int j = 14; j < 16; j++) channelBitmap |= (1 << j);*/
break;
case 1:
/*for (int j = 7; j < 14; j++) channelBitmap |= (1 << j);*/
break;
case 2:
/*for (int j = 0; j < 7; j++) channelBitmap |= (1 << j);*/
sysex_ctr = 0;
state = eTuningData;
break;
}
break;
case eTuningData:
switch (format)
{
case eBulk:
sysex_value = (sysex_value << 7) | b;
sysex_ctr++;
if ((sysex_ctr & 3) == 3)
{
if (!(note == 0x7F && sysex_value == 16383))
updateTuning(note, (sysex_value >> 14) & 127, (sysex_value & 16383) / 16383.0);
sysex_value = 0;
sysex_ctr++;
if (++note >= 128)
state = eCheckSum;
}
break;
case eSingle:
sysex_value = (sysex_value << 7) | b;
sysex_ctr++;
if (!(sysex_ctr & 3))
{
if (!(note == 0x7F && sysex_value == 16383))
updateTuning((sysex_value >> 21) & 127, (sysex_value >> 14) & 127, (sysex_value & 16383) / 16383.0);
sysex_value = 0;
if (++note >= numTunings)
state = eIgnoring;
}
break;
case eScaleOctOneByte:
case eScaleOctOneByteExt:
for (int j = sysex_ctr; j < 128; j += 12)
updateTuning(j, j, (static_cast<double>(b) - 64.0) * 0.01);
if (++sysex_ctr >= 12)
state = format == eScaleOctOneByte ? eCheckSum : eIgnoring;
break;
case eScaleOctTwoByte:
case eScaleOctTwoByteExt:
sysex_value = (sysex_value << 7) | b;
sysex_ctr++;
if (!(sysex_ctr & 1))
{
double detune = (static_cast<double>(sysex_value & 16383) - 8192.0) / (sysex_value > 8192 ? 8191.0 : 8192.0);
for (int j = note; j < 128; j += 12)
updateTuning(j, j, detune);
if (++note >= 12)
state = format == eScaleOctTwoByte ? eCheckSum : eIgnoring;
}
break;
default:
state = eIgnoring;
break;
}
break;
case eCheckSum:
/*checksum = b;*/
state = eIgnoring;
break;
}
}
if (format == eScaleOctOneByte || format == eScaleOctTwoByte || format == eScaleOctOneByteExt || format == eScaleOctTwoByteExt)
{
mapSizeLocal = static_cast<char>(12);
mapStartKeyLocal = static_cast<char>(60);
}
else
{
mapSizeLocal = static_cast<char>(-1);
mapStartKeyLocal = static_cast<char>(-1);
}
}
inline void updateTuning(int note, int retuneNote, double detune)
{
if (note < 0 || note > 127 || retuneNote < 0 || retuneNote > 127)
return;
receivedMTSSysEx = true;
localFreqs[note] = 440.0 * pow(2.0, ((retuneNote + detune) - 69.0) / 12.0);
if (localFreqs[note] != localTunings[note].freq)
{
localTunings[note].freq = localFreqs[note];
localTunings[note].flags = 0;
}
}
inline bool hasReceivedMTSSysEx() {return receivedMTSSysEx;}
const char *getScaleName() {return (global.isOnline() && global.GetScaleName) ? global.GetScaleName() : tuningName;}
double getPeriodRatio() {return (global.isOnline() && global.GetPeriodRatio) ? global.GetPeriodRatio() : 2.0;}
double getPeriodSemitones()
{
double periodRatio = getPeriodRatio();
if (periodRatio != periodRatioLocal)
{
periodSemitones = ratioToSemitones * log(periodRatio);
periodRatioLocal = periodRatio;
}
return periodSemitones;
}
char getMapSize() {return (global.isOnline() && global.GetMapSize) ? global.GetMapSize() : mapSizeLocal;}
char getMapStartKey() {return (global.isOnline() && global.GetMapStartKey) ? global.GetMapStartKey() : mapStartKeyLocal;}
char getRefKey() {return (global.isOnline() && global.GetRefKey) ? global.GetRefKey() : static_cast<char>(-1);}
enum eSysexState {eIgnoring = 0, eMatchingSysex, eSysexValid, eMatchingMTS, eMatchingBank, eMatchingProg, eMatchingChannel, eTuningName, eNumTunings, eTuningData, eCheckSum};
enum eMTSFormat {eRequest = 0, eBulk, eSingle, eScaleOctOneByte, eScaleOctTwoByte, eScaleOctOneByteExt, eScaleOctTwoByteExt};
double localFreqs[128];
Tuning localTunings[128];
Tuning globalTunings[128];
Tuning globalMultichannelTunings[16][128];
char tuningName[17];
double periodRatioLocal;
double periodSemitones;
char mapSizeLocal;
char mapStartKeyLocal;
bool supportsNoteFiltering;
bool supportsMultiChannelNoteFiltering;
bool supportsMultiChannelTuning;
bool freqRequestReceived;
bool receivedMTSSysEx;
};
static char freqToNoteET(double freq)
{
static double freqs[128];
static bool init = false;
if (!init)
{
for (int i = 0; i < 128; i++)
freqs[i] = 440.0 * pow(2.0, (i - 69.0) / 12.0);
init = true;
}
if (freq <= freqs[0])
return 0;
if (freq >= freqs[127])
return 127;
int mid = 0;
int n = -1;
int n2 = -1;
for (int first = 0, last=127;
freq != freqs[(mid = first + (last - first) / 2)];
(freq < freqs[mid]) ? last = mid - 1 : first = mid + 1)
{
if (first > last)
{
if (!mid)
{
n = mid;
break;
}
if (mid > 127)
mid = 127;
n = mid - ((freq - freqs[mid - 1]) < (freqs[mid] - freq));
break;
}
}
if (n == -1)
{
if (freq == freqs[mid])
n = mid;
else
return 60;
}
if (!n)
n2 = 1;
else if (n == 127)
n2 = 126;
else
n2 = n + (fabs(freqs[n - 1] - freq) < fabs(freqs[n + 1] - freq) ? -1 : 1);
if (n2 < n)
{
int t = n;
n = n2;
n2 = t;
}
double fmid = freqs[n] * pow(2.0, 0.5 * (log(freqs[n2] / freqs[n]) / ln2));
return freq < fmid ? static_cast<char>(n) : static_cast<char>(n2);
}
// exported functions:
MTSClient* MTS_RegisterClient() {return new MTSClient;}
void MTS_DeregisterClient(MTSClient *c) {delete c;}
bool MTS_HasMaster(MTSClient *c) {return c ? c->hasMaster() : false;}
bool MTS_Client_ShouldUpdateLibrary(MTSClient *c) {return c ? c->shouldUpdateLibrary() : false;}
bool MTS_ShouldFilterNote(MTSClient *c, char midinote, char midichannel) {return c ? c->shouldFilterNote(midinote & 127, midichannel) : false;}
double MTS_NoteToFrequency(MTSClient *c, char midinote, char midichannel) {return c ? c->freq(midinote, midichannel) : (1.0 / global.iet[midinote & 127]);}
double MTS_RetuningAsRatio(MTSClient *c, char midinote, char midichannel) {return c ? c->ratio(midinote, midichannel) : 1.0;}
double MTS_RetuningInSemitones(MTSClient *c, char midinote, char midichannel) {return c ? c->semitones(midinote, midichannel) : 0.0;}
char MTS_FrequencyToNote(MTSClient *c, double freq, char midichannel) {return c ? c->freqToNote(freq, midichannel) : freqToNoteET(freq);}
char MTS_FrequencyToNoteAndChannel(MTSClient *c, double freq, char *midichannel) {if (c) return c->freqToNote(freq, midichannel); if (midichannel) *midichannel = 0; return freqToNoteET(freq);}
const char *MTS_GetScaleName(MTSClient *c) {return c ? c->getScaleName() : "";}
double MTS_GetPeriodRatio(MTSClient *c) {return c ? c->getPeriodRatio() : 2.0;}
double MTS_GetPeriodSemitones(MTSClient *c) {return c ? c->getPeriodSemitones() : 12.0;}
char MTS_GetMapSize(MTSClient *c) {return c ? c->getMapSize() : static_cast<char>(-1);}
char MTS_GetMapStartKey(MTSClient *c) {return c ? c->getMapStartKey() : static_cast<char>(-1);}
char MTS_GetRefKey(MTSClient *c) {return c ? c->getRefKey() : static_cast<char>(-1);}
void MTS_ParseMIDIDataU(MTSClient *c, const unsigned char *buffer, int len) {if (c) c->parseMIDIData(buffer, len);}
void MTS_ParseMIDIData(MTSClient *c, const char *buffer, int len) {if (c) c->parseMIDIData(reinterpret_cast<const unsigned char*>(buffer), len);}
bool MTS_HasReceivedMTSSysEx(MTSClient *c) {return c ? c->hasReceivedMTSSysEx() : false;}

189
External/MTS/libMTSClient.h vendored Normal file
View file

@ -0,0 +1,189 @@
/*
Copyright (C) 2021 by ODDSound Ltd. info@oddsound.com
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
#ifndef libMTSClient_h
#define libMTSClient_h
#ifdef __cplusplus
extern "C" {
#endif
/*
Steps for using the MTS-ESP client API to add microtuning support to a plug-in.
Steps 1 and 2 are required, however it is recommended to include further steps when
integrating:
1. REQUIRED: Register and de-register a plug-in instance as a client with MTS-ESP.
On startup in the plug-in constructor call:
MTSClient *client = MTS_RegisterClient();
Store the returned MTSClient pointer to supply when calling other MTS-ESP client API
functions. On shutdown in the plug-in destructor call:
MTS_DeregisterClient(client);
2. REQUIRED: Query retuning when a note-on message is received and adjust tuning accordingly.
When given a note call:
double freq = MTS_NoteToFrequency(client, midinote, midichannel);
OR
double retune_semitones = MTS_RetuningInSemitones(client, midinote, midichannel);
OR
double retune_ratio = MTS_RetuningAsRatio(client, midinote, midichannel);
MIDI channel arguments should use the range [0,15] however if you dont know the MIDI
channel, use -1 (see step 6 for more on MIDI channels).
3. RECOMMENDED: Continuously query retuning whilst a note is held, allowing tuning to change
along the flight of a note. Do this if you can and as often as possible, ideally at the same
time as processing any other pitch modulation sources (envelopes, MIDI controllers, LFOs etc.).
4. RECOMMENDED: Provide an option to the user to select whether tuning is queried at note-on
only, as in step 2, or continuously, as in step 3. There are creative and practical
advantages to both, depending on the use case, and offering an option to the user will
provide the most useful MTS-ESP integration. If not offering such an option, continuous
retuning should be preferred over note-on only retuning.
5. RECOMMENDED: Query whether a note should be sounded when a note-on message is received.
The Scala .kbm keyboard mapping format allows for MIDI keys to be unmapped i.e. no frequency
is specified for them, and the MTS-ESP library supports this too. You can query whether a note
is unmapped and should be ignored with:
bool should_ignore_note = MTS_ShouldFilterNote(client, midinote, midichannel);
If this returns true, ignore the note-on and dont play anything. Calling this function is
recommended but optional and a valid value for frequency/retuning will be returned for an
unmapped note. MIDI channel arguments should use the range [0,15] however if you dont
know the MIDI channel, use -1.
6. RECOMMENDED: Always supply a MIDI channel when querying retuning or note filtering. Doing
so allows your plug-in to use multi-channel tuning tables, useful for microtonal MIDI controllers
with more than 128 keys or working with large scales. Even if multi-channel tables are not
in use, a master may still make use of channel-specific note filtering for functions such as
key switches to change tunings. If your plug-in supports MPE and has a switch for enabling MPE
support, it is recommended to NOT supply a MIDI channel if MPE is enabled.
7. RECOMMENDED: If you are adding MTS-ESP support to a plug-in that already has some kind
of microtuning support, e.g. loading .scl or .tun files, let the local tuning automatically
override MTS-ESP, or provide an option for MTS-ESP retuning to be explicitly disabled.
This affords a user the option to use a different tuning to the global MTS-ESP table
for a specific plug-in instance.
8. OPTIONAL: Add support for MIDI Tuning Standard (or MTS, from the MIDI specification) SysEx
messages to your plug-in. When not connected to an MTS-ESP master plug-in, these can be used
to retune it instead, providing microtuning support even when MTS-ESP is not in use.
When a SysEx message is received, call:
MTS_ParseMIDIData(client, buffer, len); // if buffer is signed char *
OR
MTS_ParseMIDIDataU(client, buffer, len); // if buffer is unsigned char *
These will update a local tuning table which is used when querying retuning as in steps 2
and 3. Check whether a valid MTS SysEx message has been received with:
bool MTS_SysEx_received = MTS_HasReceivedMTSSysEx(client);
9. OPTIONAL: If you want to display to the user whether the plug-in is "connected" to an
MTS-ESP master plug-in, call:
bool has_master = MTS_HasMaster(client);
10: OPTIONAL: It is possible to query the name of the current scale. This function is necessarily
supplied for the case where a client is sending MTS SysEx messages, however it can be used
to display the current scale name to the user on your UI too:
const char *name = MTS_GetScaleName(client);
11: OPTIONAL: After registering, let the user know if they have an older version of the libMTS dynamic library
installed which may not support some features in this version of the API:
bool should_update = MTS_Client_ShouldUpdateLibrary(client);
The latest version of libMTS will always be backward compatible with clients built with
an older version of the API. Users can update libMTS using the installers at
https://github.com/ODDSound/MTS-ESP/tree/main/libMTS.
12: EXTRAS: Helper functions are available which return the MIDI note whose pitch is nearest
a given frequency. The MIDI note returned is guaranteed to be mapped. If you intend to
generate a note-on message using the returned note number, you may already know which MIDI
channel it will be sent on, in which case you must specify this in the call, else the client
library can prescribe a channel for you. This is done so that multi-channel mapping
and note filtering can be respected. See below for further details.
*/
// Opaque datatype for MTSClient.
typedef struct MTSClient MTSClient;
// Register/deregister as a client. Call from the plug-in constructor and destructor.
extern MTSClient *MTS_RegisterClient();
extern void MTS_DeregisterClient(MTSClient *client);
// Check if the client is currently connected to a master plug-in.
extern bool MTS_HasMaster(MTSClient *client);
// Check if the MTS-ESP dynamic library needs to be updated to use all features in this version of the API.
extern bool MTS_Client_ShouldUpdateLibrary(MTSClient *client);
// Returns true if note should not be played. MIDI channel argument should be included if possible (0-15), else set to -1.
extern bool MTS_ShouldFilterNote(MTSClient *client, char midinote, char midichannel);
// Retuning a midi note. Pick the version that makes your life easiest! MIDI channel argument should be included if possible (0-15), else set to -1.
extern double MTS_NoteToFrequency(MTSClient *client, char midinote, char midichannel);
extern double MTS_RetuningInSemitones(MTSClient *client, char midinote, char midichannel);
extern double MTS_RetuningAsRatio(MTSClient *client, char midinote, char midichannel);
// MTS_FrequencyToNote() is a helper function returning the note number whose pitch is closest to the supplied frequency. Two versions are provided:
// The first is for the simplest case: supply a frequency and get a note number back.
// If you intend to use the returned note number to generate a note-on message on a specific, pre-determined MIDI channel, set the midichannel argument to the destination channel (0-15), else set to -1.
// If a MIDI channel is supplied, the corresponding multi-channel tuning table will be queried if in use, else multi-channel tables are ignored.
extern char MTS_FrequencyToNote(MTSClient *client, double freq, char midichannel);
// Use the second version if you intend to use the returned note number to generate a note-on message and where you have the possibility to send it on any MIDI channel.
// The midichannel argument is a pointer to a char which will receive the MIDI channel on which the note message should be sent (0-15).
// Multi-channel tuning tables are queried if in use.
extern char MTS_FrequencyToNoteAndChannel(MTSClient *client, double freq, char *midichannel);
// Returns the name of the current scale.
extern const char *MTS_GetScaleName(MTSClient *client);
// Returns the period of the current scale, or 2.0 (12 semitones) if not supplied by a master.
extern double MTS_GetPeriodRatio(MTSClient *client);
extern double MTS_GetPeriodSemitones(MTSClient *client);
// Query information about keyboard mapping.
// NOTE: negative values are invalid and these functions will return -1 if the information has not been supplied by a master.
// The return value must therefore be checked it is valid before being used.
extern char MTS_GetMapSize(MTSClient *client);
extern char MTS_GetMapStartKey(MTSClient *client);
extern char MTS_GetRefKey(MTSClient *client);
// Parse incoming MIDI data to update local tuning. All formats of MTS SysEx message accepted.
extern void MTS_ParseMIDIDataU(MTSClient *client, const unsigned char *buffer, int len);
extern void MTS_ParseMIDIData(MTSClient *client, const char *buffer, int len);
// Check if the client has received any valid MTS SysEx messages and will use local tuning if not connected to a master plug-in.
extern bool MTS_HasReceivedMTSSysEx(MTSClient *client);
#ifdef __cplusplus
}
#endif
#endif

26
External/readerwriterqueue/.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
*.ipch
*.suo
*.user
*.sdf
*.opensdf
*.exe
*.VC.db
.vs/
tests/stabtest/msvc*/Debug/
tests/stabtest/msvc*/Release/
tests/stabtest/msvc*/obj/
tests/stabtest/msvc*/log.txt
tests/stabtest/log.txt
tests/unittests/msvc*/Debug/
tests/unittests/msvc*/Release/
tests/unittests/msvc*/obj/
tests/CDSChecker/model-checker/
benchmarks/msvc*/Debug/
benchmarks/msvc*/Release/
benchmarks/msvc*/obj/
test/
# Linux binaries
benchmarks/benchmarks
tests/stabtest/stabtest
tests/unittests/unittests

View file

@ -0,0 +1,70 @@
# See https://discourse.cmake.org/t/how-to-fix-cmake-minimum-required-deprecation-warning/2487/2
# for more on setting the minimum required version.
cmake_minimum_required(VERSION 3.9...3.31.7)
project(readerwriterqueue VERSION 1.0.7)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(readerwriterqueue INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/>
)
install(FILES atomicops.h readerwriterqueue.h readerwritercircularbuffer.h LICENSE.md
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION
${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT
)
configure_package_config_file(${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/
)
install(EXPORT
${PROJECT_NAME}Targets
FILE
${PROJECT_NAME}Targets.cmake
NAMESPACE
"${PROJECT_NAME}::"
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
COMPONENT
Devel
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
COMPONENT
Devel
)
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VENDOR "Cameron Desrochers <cameron@moodycamel.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A single-producer, single-consumer lock-free queue for C++.")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${CPACK_PACKAGE_VENDOR})
set(CPACK_GENERATOR "RPM;DEB")
include(CPack)

28
External/readerwriterqueue/LICENSE.md vendored Normal file
View file

@ -0,0 +1,28 @@
This license applies to all the code in this repository except that written by third
parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff
Preshing's semaphore implementation (used in the blocking queues) which has a zlib
license (embedded in atomicops.h).
Simplified BSD License:
Copyright (c) 2013-2021, Cameron Desrochers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

186
External/readerwriterqueue/README.md vendored Normal file
View file

@ -0,0 +1,186 @@
# A single-producer, single-consumer lock-free queue for C++
This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though
you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!).
Note: If you need a general-purpose multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc].
This repository also includes a [circular-buffer SPSC queue][circular] which supports blocking on enqueue as well as dequeue.
## Features
- [Blazing fast][benchmarks]
- Compatible with C++11 (supports moving objects instead of making copies)
- Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself
(which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing)
- Allocates memory up front, in contiguous blocks
- Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity)
- Also provides an `enqueue` method which can dynamically grow the size of the queue as needed
- Also provides `try_emplace`/`emplace` convenience methods
- Has a blocking version with `wait_dequeue`
- Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation)
- On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches)
## Use
Simply drop the readerwriterqueue.h (or readerwritercircularbuffer.h) and atomicops.h files into your source code and include them :-)
A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work).
Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives
from working correctly.
Example:
```cpp
using namespace moodycamel;
ReaderWriterQueue<int> q(100); // Reserve space for at least 100 elements up front
q.enqueue(17); // Will allocate memory if the queue is full
bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates)
assert(succeeded);
int number;
succeeded = q.try_dequeue(number); // Returns false if the queue was empty
assert(succeeded && number == 17);
// You can also peek at the front item of the queue (consumer only)
int* front = q.peek();
assert(*front == 18);
succeeded = q.try_dequeue(number);
assert(succeeded && number == 18);
front = q.peek();
assert(front == nullptr); // Returns nullptr if the queue was empty
```
The blocking version has the exact same API, with the addition of `wait_dequeue` and
`wait_dequeue_timed` methods:
```cpp
BlockingReaderWriterQueue<int> q;
std::thread reader([&]() {
int item;
#if 1
for (int i = 0; i != 100; ++i) {
// Fully-blocking:
q.wait_dequeue(item);
}
#else
for (int i = 0; i != 100; ) {
// Blocking with timeout
if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5)))
++i;
}
#endif
});
std::thread writer([&]() {
for (int i = 0; i != 100; ++i) {
q.enqueue(i);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
writer.join();
reader.join();
assert(q.size_approx() == 0);
```
Note that `wait_dequeue` will block indefinitely while the queue is empty; this
means care must be taken to only call `wait_dequeue` if you're sure another element
will come along eventually, or if the queue has a static lifetime. This is because
destroying the queue while a thread is waiting on it will invoke undefined behaviour.
The blocking circular buffer has a fixed number of slots, but is otherwise quite similar to
use:
```cpp
BlockingReaderWriterCircularBuffer<int> q(1024); // pass initial capacity
q.try_enqueue(1);
int number;
q.try_dequeue(number);
assert(number == 1);
q.wait_enqueue(123);
q.wait_dequeue(number);
assert(number == 123);
q.wait_dequeue_timed(number, std::chrono::milliseconds(10));
```
## CMake
### Using targets in your project
Using this project as a part of an existing CMake project is easy.
In your CMakeLists.txt:
```
include(FetchContent)
FetchContent_Declare(
readerwriterqueue
GIT_REPOSITORY https://github.com/cameron314/readerwriterqueue
GIT_TAG master
)
FetchContent_MakeAvailable(readerwriterqueue)
add_library(my_target main.cpp)
target_link_libraries(my_target PUBLIC readerwriterqueue)
```
In main.cpp:
```cpp
#include <readerwriterqueue.h>
int main()
{
moodycamel::ReaderWriterQueue<int> q(100);
}
```
### Installing into system directories
As an alternative to including the source files in your project directly,
you can use CMake to install the library in your system's include directory:
```
mkdir build
cd build
cmake ..
make install
```
Then, you can include it from your source code:
```
#include <readerwriterqueue/readerwriterqueue.h>
```
## Disclaimers
The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that
includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-)
Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on
anything that's not x86-based.
## More info
See the [LICENSE.md][license] file for the license (simplified BSD).
My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious
about lock-free programming.
[blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
[license]: LICENSE.md
[benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks
[gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference
[mpmc]: https://github.com/cameron314/concurrentqueue
[circular]: readerwritercircularbuffer.h

772
External/readerwriterqueue/atomicops.h vendored Normal file
View file

@ -0,0 +1,772 @@
// ©2013-2016 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
// Uses Jeff Preshing's semaphore implementation (under the terms of its
// separate zlib license, embedded below).
#pragma once
// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation
// of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment).
// Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees).
// Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols.
#include <cerrno>
#include <cassert>
#include <type_traits>
#include <cerrno>
#include <cstdint>
#include <ctime>
// Platform detection
#if defined(__INTEL_COMPILER)
#define AE_ICC
#elif defined(_MSC_VER)
#define AE_VCPP
#elif defined(__GNUC__)
#define AE_GCC
#endif
#if defined(_M_IA64) || defined(__ia64__)
#define AE_ARCH_IA64
#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
#define AE_ARCH_X64
#elif defined(_M_IX86) || defined(__i386__)
#define AE_ARCH_X86
#elif defined(_M_PPC) || defined(__powerpc__)
#define AE_ARCH_PPC
#else
#define AE_ARCH_UNKNOWN
#endif
// AE_UNUSED
#define AE_UNUSED(x) ((void)x)
// AE_NO_TSAN/AE_TSAN_ANNOTATE_*
// For GCC
#if defined(__SANITIZE_THREAD__)
#define AE_TSAN_IS_ENABLED
#endif
// For clang
#if defined(__has_feature)
#if __has_feature(thread_sanitizer) && !defined(AE_TSAN_IS_ENABLED)
#define AE_TSAN_IS_ENABLED
#endif
#endif
#ifdef AE_TSAN_IS_ENABLED
#if __cplusplus >= 201703L // inline variables require C++17
namespace moodycamel { inline int ae_tsan_global; }
#define AE_TSAN_ANNOTATE_RELEASE() AnnotateHappensBefore(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global))
#define AE_TSAN_ANNOTATE_ACQUIRE() AnnotateHappensAfter(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global))
extern "C" void AnnotateHappensBefore(const char*, int, void*);
extern "C" void AnnotateHappensAfter(const char*, int, void*);
#else // when we can't work with tsan, attempt to disable its warnings
#define AE_NO_TSAN __attribute__((no_sanitize("thread")))
#endif
#endif
#ifndef AE_NO_TSAN
#define AE_NO_TSAN
#endif
#ifndef AE_TSAN_ANNOTATE_RELEASE
#define AE_TSAN_ANNOTATE_RELEASE()
#define AE_TSAN_ANNOTATE_ACQUIRE()
#endif
// AE_FORCEINLINE
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_FORCEINLINE __forceinline
#elif defined(AE_GCC)
//#define AE_FORCEINLINE __attribute__((always_inline))
#define AE_FORCEINLINE inline
#else
#define AE_FORCEINLINE inline
#endif
// AE_ALIGN
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_ALIGN(x) __declspec(align(x))
#elif defined(AE_GCC)
#define AE_ALIGN(x) __attribute__((aligned(x)))
#else
// Assume GCC compliant syntax...
#define AE_ALIGN(x) __attribute__((aligned(x)))
#endif
// Portable atomic fences implemented below:
namespace moodycamel {
enum memory_order {
memory_order_relaxed,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
// memory_order_sync: Forces a full sync:
// #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
memory_order_sync = memory_order_seq_cst
};
} // end namespace moodycamel
#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || (defined(AE_ICC) && __INTEL_COMPILER < 1600)
// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
#include <intrin.h>
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
#define AeFullSync _mm_mfence
#define AeLiteSync _mm_mfence
#elif defined(AE_ARCH_IA64)
#define AeFullSync __mf
#define AeLiteSync __mf
#elif defined(AE_ARCH_PPC)
#include <ppcintrinsics.h>
#define AeFullSync __sync
#define AeLiteSync __lwsync
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert`
#ifdef __cplusplus_cli
#pragma managed(push, off)
#endif
#endif
namespace moodycamel {
AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: _ReadBarrier(); break;
case memory_order_release: _WriteBarrier(); break;
case memory_order_acq_rel: _ReadWriteBarrier(); break;
case memory_order_seq_cst: _ReadWriteBarrier(); break;
default: assert(false);
}
}
// x86/x64 have a strong memory model -- all loads and stores have
// acquire and release semantics automatically (so only need compiler
// barriers for those).
#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: _ReadBarrier(); break;
case memory_order_release: _WriteBarrier(); break;
case memory_order_acq_rel: _ReadWriteBarrier(); break;
case memory_order_seq_cst:
_ReadWriteBarrier();
AeFullSync();
_ReadWriteBarrier();
break;
default: assert(false);
}
}
#else
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
{
// Non-specialized arch, use heavier memory barriers everywhere just in case :-(
switch (order) {
case memory_order_relaxed:
break;
case memory_order_acquire:
_ReadBarrier();
AeLiteSync();
_ReadBarrier();
break;
case memory_order_release:
_WriteBarrier();
AeLiteSync();
_WriteBarrier();
break;
case memory_order_acq_rel:
_ReadWriteBarrier();
AeLiteSync();
_ReadWriteBarrier();
break;
case memory_order_seq_cst:
_ReadWriteBarrier();
AeFullSync();
_ReadWriteBarrier();
break;
default: assert(false);
}
}
#endif
} // end namespace moodycamel
#else
// Use standard library of atomics
#include <atomic>
namespace moodycamel {
AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break;
case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break;
case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break;
case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break;
default: assert(false);
}
}
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: AE_TSAN_ANNOTATE_ACQUIRE(); std::atomic_thread_fence(std::memory_order_acquire); break;
case memory_order_release: AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_release); break;
case memory_order_acq_rel: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_acq_rel); break;
case memory_order_seq_cst: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_seq_cst); break;
default: assert(false);
}
}
} // end namespace moodycamel
#endif
#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli))
#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
#endif
#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
#include <atomic>
#endif
#include <utility>
// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
// Provides basic support for atomic variables -- no memory ordering guarantees are provided.
// The guarantee of atomicity is only made for types that already have atomic load and store guarantees
// at the hardware level -- on most platforms this generally means aligned pointers and integers (only).
namespace moodycamel {
template<typename T>
class weak_atomic
{
public:
AE_NO_TSAN weak_atomic() : value() { }
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning
#endif
template<typename U> AE_NO_TSAN weak_atomic(U&& x) : value(std::forward<U>(x)) { }
#ifdef __cplusplus_cli
// Work around bug with universal reference/nullptr combination that only appears when /clr is on
AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) { }
#endif
AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) { }
AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) { }
#ifdef AE_VCPP
#pragma warning(pop)
#endif
AE_FORCEINLINE operator T() const AE_NO_TSAN { return load(); }
#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
template<typename U> AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { value = std::forward<U>(x); return *this; }
AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value = other.value; return *this; }
AE_FORCEINLINE T load() const AE_NO_TSAN { return value; }
AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN
{
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
#if defined(_M_AMD64)
else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
#endif
#else
#error Unsupported platform
#endif
assert(false && "T must be either a 32 or 64 bit type");
return value;
}
AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN
{
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
#if defined(_M_AMD64)
else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
#endif
#else
#error Unsupported platform
#endif
assert(false && "T must be either a 32 or 64 bit type");
return value;
}
#else
template<typename U>
AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN
{
value.store(std::forward<U>(x), std::memory_order_relaxed);
return *this;
}
AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN
{
value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
return *this;
}
AE_FORCEINLINE T load() const AE_NO_TSAN { return value.load(std::memory_order_relaxed); }
AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN
{
return value.fetch_add(increment, std::memory_order_acquire);
}
AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN
{
return value.fetch_add(increment, std::memory_order_release);
}
#endif
private:
#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
// No std::atomic support, but still need to circumvent compiler optimizations.
// `volatile` will make memory access slow, but is guaranteed to be reliable.
volatile T value;
#else
std::atomic<T> value;
#endif
};
} // end namespace moodycamel
// Portable single-producer, single-consumer semaphore below:
#if defined(_WIN32)
// Avoid including windows.h in a header; we only need a handful of
// items, so we'll redeclare them here (this is relatively safe since
// the API generally has to remain stable between Windows versions).
// I know this is an ugly hack but it still beats polluting the global
// namespace with thousands of generic names or adding a .cpp for nothing.
extern "C" {
struct _SECURITY_ATTRIBUTES;
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
}
#elif defined(__MACH__)
#include <mach/mach.h>
#elif defined(__unix__)
#include <semaphore.h>
#elif defined(FREERTOS)
#include <FreeRTOS.h>
#include <semphr.h>
#include <task.h>
#endif
namespace moodycamel
{
// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's
// portable + lightweight semaphore implementations, originally from
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
// LICENSE:
// Copyright (c) 2015 Jeff Preshing
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
namespace spsc_sema
{
#if defined(_WIN32)
class Semaphore
{
private:
void* m_hSema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema()
{
assert(initialCount >= 0);
const long maxLong = 0x7fffffff;
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
assert(m_hSema);
}
AE_NO_TSAN ~Semaphore()
{
CloseHandle(m_hSema);
}
bool wait() AE_NO_TSAN
{
const unsigned long infinite = 0xffffffff;
return WaitForSingleObject(m_hSema, infinite) == 0;
}
bool try_wait() AE_NO_TSAN
{
return WaitForSingleObject(m_hSema, 0) == 0;
}
bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
{
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
}
void signal(int count = 1) AE_NO_TSAN
{
while (!ReleaseSemaphore(m_hSema, count, nullptr));
}
};
#elif defined(__MACH__)
//---------------------------------------------------------
// Semaphore (Apple iOS and OSX)
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
//---------------------------------------------------------
class Semaphore
{
private:
semaphore_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
{
assert(initialCount >= 0);
kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
assert(rc == KERN_SUCCESS);
AE_UNUSED(rc);
}
AE_NO_TSAN ~Semaphore()
{
semaphore_destroy(mach_task_self(), m_sema);
}
bool wait() AE_NO_TSAN
{
return semaphore_wait(m_sema) == KERN_SUCCESS;
}
bool try_wait() AE_NO_TSAN
{
return timed_wait(0);
}
bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN
{
mach_timespec_t ts;
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
kern_return_t rc = semaphore_timedwait(m_sema, ts);
return rc == KERN_SUCCESS;
}
void signal() AE_NO_TSAN
{
while (semaphore_signal(m_sema) != KERN_SUCCESS);
}
void signal(int count) AE_NO_TSAN
{
while (count-- > 0)
{
while (semaphore_signal(m_sema) != KERN_SUCCESS);
}
}
};
#elif defined(__unix__)
//---------------------------------------------------------
// Semaphore (POSIX, Linux)
//---------------------------------------------------------
class Semaphore
{
private:
sem_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
{
assert(initialCount >= 0);
int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
assert(rc == 0);
AE_UNUSED(rc);
}
AE_NO_TSAN ~Semaphore()
{
sem_destroy(&m_sema);
}
bool wait() AE_NO_TSAN
{
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
int rc;
do
{
rc = sem_wait(&m_sema);
}
while (rc == -1 && errno == EINTR);
return rc == 0;
}
bool try_wait() AE_NO_TSAN
{
int rc;
do {
rc = sem_trywait(&m_sema);
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
{
struct timespec ts;
const int usecs_in_1_sec = 1000000;
const int nsecs_in_1_sec = 1000000000;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += static_cast<time_t>(usecs / usecs_in_1_sec);
ts.tv_nsec += static_cast<long>(usecs % usecs_in_1_sec) * 1000;
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
// so we have to clean things up before passing it in
if (ts.tv_nsec >= nsecs_in_1_sec) {
ts.tv_nsec -= nsecs_in_1_sec;
++ts.tv_sec;
}
int rc;
do {
rc = sem_timedwait(&m_sema, &ts);
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
void signal() AE_NO_TSAN
{
while (sem_post(&m_sema) == -1);
}
void signal(int count) AE_NO_TSAN
{
while (count-- > 0)
{
while (sem_post(&m_sema) == -1);
}
}
};
#elif defined(FREERTOS)
//---------------------------------------------------------
// Semaphore (FreeRTOS)
//---------------------------------------------------------
class Semaphore
{
private:
SemaphoreHandle_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
{
assert(initialCount >= 0);
m_sema = xSemaphoreCreateCounting(static_cast<UBaseType_t>(~0ull), static_cast<UBaseType_t>(initialCount));
assert(m_sema);
}
AE_NO_TSAN ~Semaphore()
{
vSemaphoreDelete(m_sema);
}
bool wait() AE_NO_TSAN
{
return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE;
}
bool try_wait() AE_NO_TSAN
{
// Note: In an ISR context, if this causes a task to unblock,
// the caller won't know about it
if (xPortIsInsideInterrupt())
return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE;
return xSemaphoreTake(m_sema, 0) == pdTRUE;
}
bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
{
std::uint64_t msecs = usecs / 1000;
TickType_t ticks = static_cast<TickType_t>(msecs / portTICK_PERIOD_MS);
if (ticks == 0)
return try_wait();
return xSemaphoreTake(m_sema, ticks) == pdTRUE;
}
void signal() AE_NO_TSAN
{
// Note: In an ISR context, if this causes a task to unblock,
// the caller won't know about it
BaseType_t rc;
if (xPortIsInsideInterrupt())
rc = xSemaphoreGiveFromISR(m_sema, NULL);
else
rc = xSemaphoreGive(m_sema);
assert(rc == pdTRUE);
AE_UNUSED(rc);
}
void signal(int count) AE_NO_TSAN
{
while (count-- > 0)
signal();
}
};
#else
#error Unsupported platform! (No semaphore wrapper available)
#endif
//---------------------------------------------------------
// LightweightSemaphore
//---------------------------------------------------------
class LightweightSemaphore
{
public:
typedef std::make_signed<std::size_t>::type ssize_t;
private:
weak_atomic<ssize_t> m_count;
Semaphore m_sema;
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN
{
ssize_t oldCount;
// Is there a better way to set the initial spin count?
// If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
// as threads start hitting the kernel semaphore.
int spin = 1024;
while (--spin >= 0)
{
if (m_count.load() > 0)
{
m_count.fetch_add_acquire(-1);
return true;
}
compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop.
}
oldCount = m_count.fetch_add_acquire(-1);
if (oldCount > 0)
return true;
if (timeout_usecs < 0)
{
if (m_sema.wait())
return true;
}
if (timeout_usecs > 0 && m_sema.timed_wait(static_cast<uint64_t>(timeout_usecs)))
return true;
// At this point, we've timed out waiting for the semaphore, but the
// count is still decremented indicating we may still be waiting on
// it. So we have to re-adjust the count, but only if the semaphore
// wasn't signaled enough times for us too since then. If it was, we
// need to release the semaphore too.
while (true)
{
oldCount = m_count.fetch_add_release(1);
if (oldCount < 0)
return false; // successfully restored things to the way they were
// Oh, the producer thread just signaled the semaphore after all. Try again:
oldCount = m_count.fetch_add_acquire(-1);
if (oldCount > 0 && m_sema.try_wait())
return true;
}
}
public:
AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema()
{
assert(initialCount >= 0);
}
bool tryWait() AE_NO_TSAN
{
if (m_count.load() > 0)
{
m_count.fetch_add_acquire(-1);
return true;
}
return false;
}
bool wait() AE_NO_TSAN
{
return tryWait() || waitWithPartialSpinning();
}
bool wait(std::int64_t timeout_usecs) AE_NO_TSAN
{
return tryWait() || waitWithPartialSpinning(timeout_usecs);
}
void signal(ssize_t count = 1) AE_NO_TSAN
{
assert(count >= 0);
ssize_t oldCount = m_count.fetch_add_release(count);
assert(oldCount >= -1);
if (oldCount < 0)
{
m_sema.signal(1);
}
}
std::size_t availableApprox() const AE_NO_TSAN
{
ssize_t count = m_count.load();
return count > 0 ? static_cast<std::size_t>(count) : 0;
}
};
} // end namespace spsc_sema
} // end namespace moodycamel
#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))
#pragma warning(pop)
#ifdef __cplusplus_cli
#pragma managed(pop)
#endif
#endif

View file

@ -0,0 +1,472 @@
// ©2013-2015 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this file).
// Benchmarks for moodycamel::ReaderWriterQueue.
#if defined(_MSC_VER) && _MSC_VER < 1700
#define NO_FOLLY_SUPPORT
#endif
#if defined(_MSC_VER) && _MSC_VER < 1700
#define NO_CIRCULAR_BUFFER_SUPPORT
#endif
#if !defined(__amd64__) && !defined(_M_X64) && !defined(__x86_64__) && !defined(_M_IX86) && !defined(__i386__)
#define NO_SPSC_SUPPORT // SPSC implementation is for x86 only
#endif
#include "ext/1024cores/spscqueue.h" // Dmitry's (on Intel site)
#ifndef NO_FOLLY_SUPPORT
#include "ext/folly/ProducerConsumerQueue.h" // Facebook's folly (GitHub)
#endif
#include "../readerwriterqueue.h" // Mine
#ifndef NO_CIRCULAR_BUFFER_SUPPORT
#include "../readerwritercircularbuffer.h" // Mine
template<typename T>
class BlockingReaderWriterCircularBufferAdapter : public moodycamel::BlockingReaderWriterCircularBuffer<T> {
public:
BlockingReaderWriterCircularBufferAdapter(std::size_t capacity) : moodycamel::BlockingReaderWriterCircularBuffer<T>(capacity) { }
void enqueue(T const& x) { this->wait_enqueue(x); }
};
#endif
#include "systemtime.h"
#include "../tests/common/simplethread.h"
#include <iostream>
#include <iomanip>
#include <numeric> // For std::accumulate
#include <algorithm>
#include <random>
#include <ctime>
#ifndef UNUSED
#define UNUSED(x) ((void)x);
#endif
using namespace moodycamel;
#ifndef NO_FOLLY_SUPPORT
using namespace folly;
#endif
typedef std::minstd_rand RNG_t;
enum BenchmarkType {
bench_raw_add,
bench_raw_remove,
bench_empty_remove,
bench_single_threaded,
bench_mostly_add,
bench_mostly_remove,
bench_heavy_concurrent,
bench_random_concurrent,
BENCHMARK_COUNT
};
// Returns the number of seconds elapsed (high-precision), and the number of enqueue/dequeue
// operations performed (in the out_Ops parameter)
template<typename TQueue>
double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops);
const int BENCHMARK_NAME_MAX = 17; // Not including null terminator
const char* benchmarkName(BenchmarkType benchmark);
int main(int argc, char** argv)
{
#ifdef NDEBUG
const int TEST_COUNT = 25;
#else
const int TEST_COUNT = 2;
#endif
assert(TEST_COUNT >= 2);
const double FASTEST_PERCENT_CONSIDERED = 20; // Consider only the fastest runs in the top 20%
double rwqResults[BENCHMARK_COUNT][TEST_COUNT];
double brwcbResults[BENCHMARK_COUNT][TEST_COUNT];
double spscResults[BENCHMARK_COUNT][TEST_COUNT];
double follyResults[BENCHMARK_COUNT][TEST_COUNT];
// Also calculate a rough heuristic of "ops/s" (across all runs, not just fastest)
double rwqOps[BENCHMARK_COUNT][TEST_COUNT];
double brwcbOps[BENCHMARK_COUNT][TEST_COUNT];
double spscOps[BENCHMARK_COUNT][TEST_COUNT];
double follyOps[BENCHMARK_COUNT][TEST_COUNT];
// Make sure the randomness of each benchmark run is identical
unsigned int randSeeds[BENCHMARK_COUNT];
for (unsigned int i = 0; i != BENCHMARK_COUNT; ++i) {
randSeeds[i] = ((unsigned int)time(NULL)) * i;
}
// Run benchmarks
for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) {
for (int i = 0; i < TEST_COUNT; ++i) {
rwqResults[benchmark][i] = runBenchmark<ReaderWriterQueue<int>>((BenchmarkType)benchmark, randSeeds[benchmark], rwqOps[benchmark][i]);
}
#ifndef NO_CIRCULAR_BUFFER_SUPPORT
for (int i = 0; i < TEST_COUNT; ++i) {
brwcbResults[benchmark][i] = runBenchmark<BlockingReaderWriterCircularBufferAdapter<int>>((BenchmarkType)benchmark, randSeeds[benchmark], brwcbOps[benchmark][i]);
}
#else
for (int i = 0; i < TEST_COUNT; ++i) {
brwcbResults[benchmark][i] = 0;
brwcbOps[benchmark][i] = 0;
}
#endif
#ifndef NO_SPSC_SUPPORT
for (int i = 0; i < TEST_COUNT; ++i) {
spscResults[benchmark][i] = runBenchmark<spsc_queue<int>>((BenchmarkType)benchmark, randSeeds[benchmark], spscOps[benchmark][i]);
}
#else
for (int i = 0; i < TEST_COUNT; ++i) {
spscResults[benchmark][i] = 0;
spscOps[benchmark][i] = 0;
}
#endif
#ifndef NO_FOLLY_SUPPORT
for (int i = 0; i < TEST_COUNT; ++i) {
follyResults[benchmark][i] = runBenchmark<ProducerConsumerQueue<int>>((BenchmarkType)benchmark, randSeeds[benchmark], follyOps[benchmark][i]);
}
#else
for (int i = 0; i < TEST_COUNT; ++i) {
follyResults[benchmark][i] = 0;
follyOps[benchmark][i] = 0;
}
#endif
}
// Sort results
for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) {
std::sort(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT);
std::sort(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT);
std::sort(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT);
std::sort(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT);
}
// Display results
int max = std::max(2, (int)(TEST_COUNT * FASTEST_PERCENT_CONSIDERED / 100));
assert(max > 0);
#ifdef NO_CIRCULAR_BUFFER_SUPPORT
std::cout << "Note: BRWCB queue not supported on this platform, discount its timings" << std::endl;
#endif
#ifdef NO_SPSC_SUPPORT
std::cout << "Note: SPSC queue not supported on this platform, discount its timings" << std::endl;
#endif
#ifdef NO_FOLLY_SUPPORT
std::cout << "Note: Folly queue not supported by this compiler, discount its timings" << std::endl;
#endif
std::cout << std::setw(BENCHMARK_NAME_MAX) << " " << " |---------------- Min -----------------|----------------- Max -----------------|----------------- Avg -----------------|\n";
std::cout << std::left << std::setw(BENCHMARK_NAME_MAX) << "Benchmark" << " | RWQ | BRWCB | SPSC | Folly | RWQ | BRWCB | SPSC | Folly | RWQ | BRWCB | SPSC | Folly | xSPSC | xFolly\n";
std::cout.fill('-');
std::cout << std::setw(BENCHMARK_NAME_MAX) << "---------" << "-+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+-------\n";
std::cout.fill(' ');
double rwqOpsPerSec = 0, brwcbOpsPerSec = 0, spscOpsPerSec = 0, follyOpsPerSec = 0;
int opTimedBenchmarks = 0;
for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) {
double rwqMin = rwqResults[benchmark][0], rwqMax = rwqResults[benchmark][max - 1];
double brwcbMin = brwcbResults[benchmark][0], brwcbMax = brwcbResults[benchmark][max - 1];
double spscMin = spscResults[benchmark][0], spscMax = spscResults[benchmark][max - 1];
double follyMin = follyResults[benchmark][0], follyMax = follyResults[benchmark][max - 1];
double rwqAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + max, 0.0) / max;
double brwcbAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + max, 0.0) / max;
double spscAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + max, 0.0) / max;
double follyAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + max, 0.0) / max;
double spscMult = rwqAvg < 0.00001 ? 0 : spscAvg / rwqAvg;
double follyMult = follyAvg < 0.00001 ? 0 : follyAvg / rwqAvg;
if (rwqResults[benchmark][0] != -1) {
double rwqTotalAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
double brwcbTotalAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
double spscTotalAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
double follyTotalAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
rwqOpsPerSec += rwqTotalAvg == 0 ? 0 : std::accumulate(&rwqOps[benchmark][0], &rwqOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / rwqTotalAvg;
brwcbOpsPerSec += brwcbTotalAvg == 0 ? 0 : std::accumulate(&brwcbOps[benchmark][0], &brwcbOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / brwcbTotalAvg;
spscOpsPerSec += spscTotalAvg == 0 ? 0 : std::accumulate(&spscOps[benchmark][0], &spscOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / spscTotalAvg;
follyOpsPerSec += follyTotalAvg == 0 ? 0 : std::accumulate(&follyOps[benchmark][0], &follyOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / follyTotalAvg;
++opTimedBenchmarks;
}
std::cout
<< std::left << std::setw(BENCHMARK_NAME_MAX) << benchmarkName((BenchmarkType)benchmark) << " | "
<< std::fixed << std::setprecision(4) << rwqMin << "s | "
<< std::fixed << std::setprecision(4) << brwcbMin << "s | "
<< std::fixed << std::setprecision(4) << spscMin << "s | "
<< std::fixed << std::setprecision(4) << follyMin << "s | "
<< std::fixed << std::setprecision(4) << rwqMax << "s | "
<< std::fixed << std::setprecision(4) << brwcbMax << "s | "
<< std::fixed << std::setprecision(4) << spscMax << "s | "
<< std::fixed << std::setprecision(4) << follyMax << "s | "
<< std::fixed << std::setprecision(4) << rwqAvg << "s | "
<< std::fixed << std::setprecision(4) << brwcbAvg << "s | "
<< std::fixed << std::setprecision(4) << spscAvg << "s | "
<< std::fixed << std::setprecision(4) << follyAvg << "s | "
<< std::fixed << std::setprecision(2) << spscMult << "x | "
<< std::fixed << std::setprecision(2) << follyMult << "x"
<< "\n"
;
}
rwqOpsPerSec /= opTimedBenchmarks;
brwcbOpsPerSec /= opTimedBenchmarks;
spscOpsPerSec /= opTimedBenchmarks;
follyOpsPerSec /= opTimedBenchmarks;
std::cout
<< "\nAverage ops/s:\n"
<< " ReaderWriterQueue: " << std::fixed << std::setprecision(2) << rwqOpsPerSec / 1000000 << " million\n"
<< " BlockingReaderWriterCircularBuffer: " << std::fixed << std::setprecision(2) << brwcbOpsPerSec / 1000000 << " million\n"
<< " SPSC queue: " << std::fixed << std::setprecision(2) << spscOpsPerSec / 1000000 << " million\n"
<< " Folly queue: " << std::fixed << std::setprecision(2) << follyOpsPerSec / 1000000 << " million\n"
;
std::cout << std::endl;
return 0;
}
template<typename TQueue>
double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops)
{
typedef unsigned long long counter_t;
SystemTime start;
double result = 0;
volatile int forceNoOptimizeDummy;
switch (benchmark) {
case bench_raw_add: {
const counter_t MAX = 100 * 1000;
out_Ops = MAX;
TQueue q(MAX);
int num = 0;
start = getSystemTime();
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
result = getTimeDelta(start);
int temp = -1;
q.try_dequeue(temp);
forceNoOptimizeDummy = temp;
} break;
case bench_raw_remove: {
const counter_t MAX = 100 * 1000;
out_Ops = MAX;
TQueue q(MAX);
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
int element = -1;
int total = 0;
num = 0;
start = getSystemTime();
for (counter_t i = 0; i != MAX; ++i) {
bool success = q.try_dequeue(element);
assert(success && num++ == element);
UNUSED(success);
total += element;
}
result = getTimeDelta(start);
assert(!q.try_dequeue(element));
forceNoOptimizeDummy = total;
} break;
case bench_empty_remove: {
const counter_t MAX = 2000 * 1000;
out_Ops = MAX;
TQueue q(MAX);
int total = 0;
start = getSystemTime();
SimpleThread consumer([&]() {
int element;
for (counter_t i = 0; i != MAX; ++i) {
if (q.try_dequeue(element)) {
total += element;
}
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX / 2; ++i) {
if ((i & 32767) == 0) { // Just to make sure the loops aren't optimized out entirely
q.enqueue(num);
++num;
}
}
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = total;
} break;
case bench_single_threaded: {
const counter_t MAX = 200 * 1000;
out_Ops = MAX;
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 1);
TQueue q(MAX);
int num = 0;
int element = -1;
start = getSystemTime();
for (counter_t i = 0; i != MAX; ++i) {
if (rand(rng) == 1) {
q.enqueue(num);
++num;
}
else {
q.try_dequeue(element);
}
}
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
} break;
case bench_mostly_add: {
const counter_t MAX = 1200 * 1000;
out_Ops = MAX;
int readOps = 0;
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 3);
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
for (counter_t i = 0; i != MAX / 10; ++i) {
if (rand(rng) == 0) {
q.try_dequeue(element);
++readOps;
}
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
out_Ops += readOps;
} break;
case bench_mostly_remove: {
const counter_t MAX = 1200 * 1000;
out_Ops = MAX;
int writeOps = 0;
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 3);
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
for (counter_t i = 0; i != MAX; ++i) {
q.try_dequeue(element);
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX / 10; ++i) {
if (rand(rng) == 0) {
q.enqueue(num);
++num;
}
}
writeOps = num;
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
out_Ops += writeOps;
} break;
case bench_heavy_concurrent: {
const counter_t MAX = 1000 * 1000;
out_Ops = MAX * 2;
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
for (counter_t i = 0; i != MAX; ++i) {
q.try_dequeue(element);
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
} break;
case bench_random_concurrent: {
const counter_t MAX = 800 * 1000;
int readOps = 0, writeOps = 0;
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 15);
for (counter_t i = 0; i != MAX; ++i) {
if (rand(rng) == 0) {
q.try_dequeue(element);
++readOps;
}
}
});
SimpleThread producer([&]() {
RNG_t rng(randomSeed * 3 - 1);
std::uniform_int_distribution<int> rand(0, 15);
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
if (rand(rng) == 0) {
q.enqueue(num);
++num;
}
}
writeOps = num;
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
out_Ops = readOps + writeOps;
} break;
default:
assert(false);
out_Ops = 0;
return 0;
}
UNUSED(forceNoOptimizeDummy);
return result / 1000.0;
}
const char* benchmarkName(BenchmarkType benchmark)
{
switch (benchmark) {
case bench_raw_add: return "Raw add";
case bench_raw_remove: return "Raw remove";
case bench_empty_remove: return "Raw empty remove";
case bench_single_threaded: return "Single-threaded";
case bench_mostly_add: return "Mostly add";
case bench_mostly_remove: return "Mostly remove";
case bench_heavy_concurrent: return "Heavy concurrent";
case bench_random_concurrent: return "Random concurrent";
default: return "";
}
}

View file

@ -0,0 +1,139 @@
#include "../../../atomicops.h"
#include <cstdlib> // For std::size_t
// From http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue
// (and http://software.intel.com/en-us/articles/single-producer-single-consumer-queue)
// load with 'consume' (data-dependent) memory ordering
template<typename T>
T load_consume(T const* addr)
{
// hardware fence is implicit on x86
T v = *const_cast<T const volatile*>(addr);
moodycamel::compiler_fence(moodycamel::memory_order_seq_cst);
return v;
}
// store with 'release' memory ordering
template<typename T>
void store_release(T* addr, T v)
{
// hardware fence is implicit on x86
moodycamel::compiler_fence(moodycamel::memory_order_seq_cst);
*const_cast<T volatile*>(addr) = v;
}
// cache line size on modern x86 processors (in bytes)
size_t const cache_line_size = 64;
// single-producer/single-consumer queue
template<typename T>
class spsc_queue
{
public:
spsc_queue()
{
node* n = new node;
n->next_ = 0;
tail_ = head_ = first_= tail_copy_ = n;
}
explicit spsc_queue(size_t prealloc)
{
node* n = new node;
n->next_ = 0;
tail_ = head_ = first_ = tail_copy_ = n;
// [CD] Not (at all) the most efficient way to pre-allocate memory, but it works
T dummy = T();
for (size_t i = 0; i != prealloc; ++i) {
enqueue(dummy);
}
for (size_t i = 0; i != prealloc; ++i) {
try_dequeue(dummy);
}
}
~spsc_queue()
{
node* n = first_;
do
{
node* next = n->next_;
delete n;
n = next;
}
while (n);
}
void enqueue(T v)
{
node* n = alloc_node();
n->next_ = 0;
n->value_ = v;
store_release(&head_->next_, n);
head_ = n;
}
// returns 'false' if queue is empty
bool try_dequeue(T& v)
{
if (load_consume(&tail_->next_))
{
v = tail_->next_->value_;
store_release(&tail_, tail_->next_);
return true;
}
else
{
return false;
}
}
private:
// internal node structure
struct node
{
node* next_;
T value_;
};
// consumer part
// accessed mainly by consumer, infrequently be producer
node* tail_; // tail of the queue
// delimiter between consumer part and producer part,
// so that they situated on different cache lines
char cache_line_pad_ [cache_line_size];
// producer part
// accessed only by producer
node* head_; // head of the queue
node* first_; // last unused node (tail of node cache)
node* tail_copy_; // helper (points somewhere between first_ and tail_)
node* alloc_node()
{
// first tries to allocate node from internal node cache,
// if attempt fails, allocates node via ::operator new()
if (first_ != tail_copy_)
{
node* n = first_;
first_ = first_->next_;
return n;
}
tail_copy_ = load_consume(&tail_);
if (first_ != tail_copy_)
{
node* n = first_;
first_ = first_->next_;
return n;
}
node* n = new node;
return n;
}
spsc_queue(spsc_queue const&);
spsc_queue& operator = (spsc_queue const&);
};

View file

@ -0,0 +1,174 @@
// Adapted from https://github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @author Bo Hu (bhu@fb.com)
// @author Jordan DeLong (delong.j@fb.com)
#ifndef PRODUCER_CONSUMER_QUEUE_H_
#define PRODUCER_CONSUMER_QUEUE_H_
#include <new>
#include <atomic>
#include <cassert>
#include <cstdlib>
#include <stdexcept>
#include <type_traits>
#include <utility>
//#include <boost/noncopyable.hpp>
namespace folly {
/*
* ProducerConsumerQueue is a one producer and one consumer queue
* without locks.
*/
template<class T>
struct ProducerConsumerQueue {
typedef T value_type;
// size must be >= 1.
explicit ProducerConsumerQueue(uint32_t size)
: size_(size + 1) // +1 because one slot is always empty
, records_(static_cast<T*>(std::malloc(sizeof(T) * (size + 1))))
, readIndex_(0)
, writeIndex_(0)
{
assert(size >= 1);
if (!records_) {
throw std::bad_alloc();
}
}
~ProducerConsumerQueue() {
// We need to destruct anything that may still exist in our queue.
// (No real synchronization needed at destructor time: only one
// thread can be doing this.)
if (!std::is_trivially_destructible<T>::value) {
int read = readIndex_;
int end = writeIndex_;
while (read != end) {
records_[read].~T();
if (++read == size_) {
read = 0;
}
}
}
std::free(records_);
}
template<class ...Args>
bool enqueue(Args&&... recordArgs) {
auto const currentWrite = writeIndex_.load(std::memory_order_relaxed);
auto nextRecord = currentWrite + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
if (nextRecord != readIndex_.load(std::memory_order_acquire)) {
new (&records_[currentWrite]) T(std::forward<Args>(recordArgs)...);
writeIndex_.store(nextRecord, std::memory_order_release);
return true;
}
// queue is full
return false;
}
// move (or copy) the value at the front of the queue to given variable
bool try_dequeue(T& record) {
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
if (currentRead == writeIndex_.load(std::memory_order_acquire)) {
// queue is empty
return false;
}
auto nextRecord = currentRead + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
record = std::move(records_[currentRead]);
records_[currentRead].~T();
readIndex_.store(nextRecord, std::memory_order_release);
return true;
}
// pointer to the value at the front of the queue (for use in-place) or
// nullptr if empty.
T* frontPtr() {
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
if (currentRead == writeIndex_.load(std::memory_order_acquire)) {
// queue is empty
return nullptr;
}
return &records_[currentRead];
}
// queue must not be empty
void popFront() {
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
assert(currentRead != writeIndex_.load(std::memory_order_acquire));
auto nextRecord = currentRead + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
records_[currentRead].~T();
readIndex_.store(nextRecord, std::memory_order_release);
}
bool isEmpty() const {
return readIndex_.load(std::memory_order_consume) ==
writeIndex_.load(std::memory_order_consume);
}
bool isFull() const {
auto nextRecord = writeIndex_.load(std::memory_order_consume) + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
if (nextRecord != readIndex_.load(std::memory_order_consume)) {
return false;
}
// queue is full
return true;
}
// * If called by consumer, then true size may be more (because producer may
// be adding items concurrently).
// * If called by producer, then true size may be less (because consumer may
// be removing items concurrently).
// * It is undefined to call this from any other thread.
size_t sizeGuess() const {
int ret = writeIndex_.load(std::memory_order_consume) -
readIndex_.load(std::memory_order_consume);
if (ret < 0) {
ret += size_;
}
return ret;
}
private:
const uint32_t size_;
T* const records_;
std::atomic<int> readIndex_;
std::atomic<int> writeIndex_;
};
}
#endif

View file

@ -0,0 +1,22 @@
# ©2014 Cameron Desrochers
ifeq ($(OS),Windows_NT)
EXT=.exe
PLATFORM_OPTS=-static
else
EXT=
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
PLATFORM_OPTS=
else
PLATFORM_OPTS=-Wl,--no-as-needed -lrt
endif
endif
default: benchmarks$(EXT)
benchmarks$(EXT): bench.cpp ../readerwriterqueue.h ../readerwritercircularbuffer.h ../atomicops.h ext/1024cores/spscqueue.h ext/folly/ProducerConsumerQueue.h ../tests/common/simplethread.h ../tests/common/simplethread.cpp systemtime.h systemtime.cpp makefile
g++ -std=c++11 -Wpedantic -Wall -DNDEBUG -O3 -g bench.cpp ../tests/common/simplethread.cpp systemtime.cpp -o benchmarks$(EXT) -pthread $(PLATFORM_OPTS)
run: benchmarks$(EXT)
./benchmarks$(EXT)

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbenchintel</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbenchintel</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\tests\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\1024cores\spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbenchintel</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\tests\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\1024cores\spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,137 @@
// ©2013-2014 Cameron Desrochers
#include "systemtime.h"
#include <climits>
#if defined(_MSC_VER) && _MSC_VER < 1700
#include <intrin.h>
#define CompilerMemBar() _ReadWriteBarrier()
#else
#include <atomic>
#define CompilerMemBar() std::atomic_signal_fence(std::memory_order_seq_cst)
#endif
#if defined(ST_WINDOWS)
#include <windows.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::Sleep(milliseconds);
}
SystemTime getSystemTime()
{
LARGE_INTEGER t;
CompilerMemBar();
if (!QueryPerformanceCounter(&t)) {
return static_cast<SystemTime>(-1);
}
CompilerMemBar();
return static_cast<SystemTime>(t.QuadPart);
}
double getTimeDelta(SystemTime start)
{
LARGE_INTEGER t;
CompilerMemBar();
if (start == static_cast<SystemTime>(-1) || !QueryPerformanceCounter(&t)) {
return -1;
}
CompilerMemBar();
auto now = static_cast<SystemTime>(t.QuadPart);
LARGE_INTEGER f;
if (!QueryPerformanceFrequency(&f)) {
return -1;
}
return static_cast<double>(static_cast<__int64>(now - start)) / f.QuadPart * 1000;
}
} // end namespace moodycamel
#elif defined(ST_APPLE)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <unistd.h>
#include <time.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::usleep(milliseconds * 1000);
}
SystemTime getSystemTime()
{
CompilerMemBar();
std::uint64_t result = mach_absolute_time();
CompilerMemBar();
return result;
}
double getTimeDelta(SystemTime start)
{
CompilerMemBar();
std::uint64_t end = mach_absolute_time();
CompilerMemBar();
mach_timebase_info_data_t tb = { 0 };
mach_timebase_info(&tb);
double toNano = static_cast<double>(tb.numer) / tb.denom;
return static_cast<double>(end - start) * toNano * 0.000001;
}
} // end namespace moodycamel
#elif defined(ST_NIX)
#include <unistd.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::usleep(milliseconds * 1000);
}
SystemTime getSystemTime()
{
timespec t;
CompilerMemBar();
if (clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) {
t.tv_sec = (time_t)-1;
t.tv_nsec = -1;
}
CompilerMemBar();
return t;
}
double getTimeDelta(SystemTime start)
{
timespec t;
CompilerMemBar();
if ((start.tv_sec == (time_t)-1 && start.tv_nsec == -1) || clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) {
return -1;
}
CompilerMemBar();
return static_cast<double>(static_cast<long>(t.tv_sec) - static_cast<long>(start.tv_sec)) * 1000 + double(t.tv_nsec - start.tv_nsec) / 1000000;
}
} // end namespace moodycamel
#endif

View file

@ -0,0 +1,33 @@
// ©2013-2014 Cameron Desrochers
#pragma once
#if defined(_WIN32)
#define ST_WINDOWS
#elif defined(__APPLE__) && defined(__MACH__)
#define ST_APPLE
#elif defined(__linux__) || defined(__FreeBSD__) || defined(BSD)
#define ST_NIX
#else
#error "Unknown platform"
#endif
#if defined(ST_WINDOWS)
namespace moodycamel { typedef unsigned long long SystemTime; }
#elif defined(ST_APPLE)
#include <cstdint>
namespace moodycamel { typedef std::uint64_t SystemTime; }
#elif defined(ST_NIX)
#include <time.h>
namespace moodycamel { typedef timespec SystemTime; }
#endif
namespace moodycamel
{
void sleep(int milliseconds);
SystemTime getSystemTime();
// Returns the delta time, in milliseconds
double getTimeDelta(SystemTime start);
}

View file

@ -0,0 +1,321 @@
// ©2020 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
// Provides a C++11 implementation of a single-producer, single-consumer wait-free concurrent
// circular buffer (fixed-size queue).
#pragma once
#include <utility>
#include <chrono>
#include <memory>
#include <cstdlib>
#include <cstdint>
#include <cassert>
// Note that this implementation is fully modern C++11 (not compatible with old MSVC versions)
// but we still include atomicops.h for its LightweightSemaphore implementation.
#include "atomicops.h"
#ifndef MOODYCAMEL_CACHE_LINE_SIZE
#define MOODYCAMEL_CACHE_LINE_SIZE 64
#endif
namespace moodycamel {
template<typename T>
class BlockingReaderWriterCircularBuffer
{
public:
typedef T value_type;
public:
explicit BlockingReaderWriterCircularBuffer(std::size_t capacity)
: maxcap(capacity), mask(), rawData(), data(),
slots_(new spsc_sema::LightweightSemaphore(static_cast<spsc_sema::LightweightSemaphore::ssize_t>(capacity))),
items(new spsc_sema::LightweightSemaphore(0)),
nextSlot(0), nextItem(0)
{
// Round capacity up to power of two to compute modulo mask.
// Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--capacity;
capacity |= capacity >> 1;
capacity |= capacity >> 2;
capacity |= capacity >> 4;
for (std::size_t i = 1; i < sizeof(std::size_t); i <<= 1)
capacity |= capacity >> (i << 3);
mask = capacity++;
rawData = static_cast<char*>(std::malloc(capacity * sizeof(T) + std::alignment_of<T>::value - 1));
data = align_for<T>(rawData);
}
BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer&& other)
: maxcap(0), mask(0), rawData(nullptr), data(nullptr),
slots_(new spsc_sema::LightweightSemaphore(0)),
items(new spsc_sema::LightweightSemaphore(0)),
nextSlot(), nextItem()
{
swap(other);
}
BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer const&) = delete;
// Note: The queue should not be accessed concurrently while it's
// being deleted. It's up to the user to synchronize this.
~BlockingReaderWriterCircularBuffer()
{
for (std::size_t i = 0, n = items->availableApprox(); i != n; ++i)
reinterpret_cast<T*>(data)[(nextItem + i) & mask].~T();
std::free(rawData);
}
BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer&& other) noexcept
{
swap(other);
return *this;
}
BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer const&) = delete;
// Swaps the contents of this buffer with the contents of another.
// Not thread-safe.
void swap(BlockingReaderWriterCircularBuffer& other) noexcept
{
std::swap(maxcap, other.maxcap);
std::swap(mask, other.mask);
std::swap(rawData, other.rawData);
std::swap(data, other.data);
std::swap(slots_, other.slots_);
std::swap(items, other.items);
std::swap(nextSlot, other.nextSlot);
std::swap(nextItem, other.nextItem);
}
// Enqueues a single item (by copying it).
// Fails if not enough room to enqueue.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool try_enqueue(T const& item)
{
if (!slots_->tryWait())
return false;
inner_enqueue(item);
return true;
}
// Enqueues a single item (by moving it, if possible).
// Fails if not enough room to enqueue.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool try_enqueue(T&& item)
{
if (!slots_->tryWait())
return false;
inner_enqueue(std::move(item));
return true;
}
// Blocks the current thread until there's enough space to enqueue the given item,
// then enqueues it (via copy).
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
void wait_enqueue(T const& item)
{
while (!slots_->wait());
inner_enqueue(item);
}
// Blocks the current thread until there's enough space to enqueue the given item,
// then enqueues it (via move, if possible).
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
void wait_enqueue(T&& item)
{
while (!slots_->wait());
inner_enqueue(std::move(item));
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via copy) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool wait_enqueue_timed(T const& item, std::int64_t timeout_usecs)
{
if (!slots_->wait(timeout_usecs))
return false;
inner_enqueue(item);
return true;
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via move, if possible) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool wait_enqueue_timed(T&& item, std::int64_t timeout_usecs)
{
if (!slots_->wait(timeout_usecs))
return false;
inner_enqueue(std::move(item));
return true;
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via copy) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
template<typename Rep, typename Period>
inline bool wait_enqueue_timed(T const& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_enqueue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via move, if possible) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
template<typename Rep, typename Period>
inline bool wait_enqueue_timed(T&& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_enqueue_timed(std::move(item), std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Attempts to dequeue a single item.
// Returns false if the buffer is empty.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U>
bool try_dequeue(U& item)
{
if (!items->tryWait())
return false;
inner_dequeue(item);
return true;
}
// Blocks the current thread until there's something to dequeue, then dequeues it.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U>
void wait_dequeue(U& item)
{
while (!items->wait());
inner_dequeue(item);
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U>
bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
{
if (!items->wait(timeout_usecs))
return false;
inner_dequeue(item);
return true;
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Returns a pointer to the next element in the queue (the one that would
// be removed next by a call to `try_dequeue` or `try_pop`). If the queue
// appears empty at the time the method is called, returns nullptr instead.
// Thread-safe when called by consumer thread.
inline T* peek()
{
if (!items->availableApprox())
return nullptr;
return inner_peek();
}
// Pops the next element from the queue, if there is one.
// Thread-safe when called by consumer thread.
inline bool try_pop()
{
if (!items->tryWait())
return false;
inner_pop();
return true;
}
// Returns a (possibly outdated) snapshot of the total number of elements currently in the buffer.
// Thread-safe.
inline std::size_t size_approx() const
{
return items->availableApprox();
}
// Returns the maximum number of elements that this circular buffer can hold at once.
// Thread-safe.
inline std::size_t max_capacity() const
{
return maxcap;
}
private:
template<typename U>
void inner_enqueue(U&& item)
{
std::size_t i = nextSlot++;
new (reinterpret_cast<T*>(data) + (i & mask)) T(std::forward<U>(item));
items->signal();
}
template<typename U>
void inner_dequeue(U& item)
{
std::size_t i = nextItem++;
T& element = reinterpret_cast<T*>(data)[i & mask];
item = std::move(element);
element.~T();
slots_->signal();
}
T* inner_peek()
{
return reinterpret_cast<T*>(data) + (nextItem & mask);
}
void inner_pop()
{
std::size_t i = nextItem++;
reinterpret_cast<T*>(data)[i & mask].~T();
slots_->signal();
}
template<typename U>
static inline char* align_for(char* ptr)
{
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
private:
std::size_t maxcap; // actual (non-power-of-two) capacity
std::size_t mask; // circular buffer capacity mask (for cheap modulo)
char* rawData; // raw circular buffer memory
char* data; // circular buffer memory aligned to element alignment
std::unique_ptr<spsc_sema::LightweightSemaphore> slots_; // number of slots currently free (named with underscore to accommodate Qt's 'slots' macro)
std::unique_ptr<spsc_sema::LightweightSemaphore> items; // number of elements currently enqueued
char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(char*) * 2 - sizeof(std::size_t) * 2 - sizeof(std::unique_ptr<spsc_sema::LightweightSemaphore>) * 2];
std::size_t nextSlot; // index of next free slot to enqueue into
char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(std::size_t)];
std::size_t nextItem; // index of next element to dequeue from
};
}

View file

@ -0,0 +1,979 @@
// ©2013-2020 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
#pragma once
#include "atomicops.h"
#include <new>
#include <type_traits>
#include <utility>
#include <cassert>
#include <stdexcept>
#include <new>
#include <cstdint>
#include <cstdlib> // For malloc/free/abort & size_t
#include <memory>
#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
#include <chrono>
#endif
// A lock-free queue for a single-consumer, single-producer architecture.
// The queue is also wait-free in the common path (except if more memory
// needs to be allocated, in which case malloc is called).
// Allocates memory sparingly, and only once if the original maximum size
// estimate is never exceeded.
// Tested on x86/x64 processors, but semantics should be correct for all
// architectures (given the right implementations in atomicops.h), provided
// that aligned integer and pointer accesses are naturally atomic.
// Note that there should only be one consumer thread and producer thread;
// Switching roles of the threads, or using multiple consecutive threads for
// one role, is not safe unless properly synchronized.
// Using the queue exclusively from one thread is fine, though a bit silly.
#ifndef MOODYCAMEL_CACHE_LINE_SIZE
#define MOODYCAMEL_CACHE_LINE_SIZE 64
#endif
#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
#define MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#endif
#ifndef MOODYCAMEL_HAS_EMPLACE
#if !defined(_MSC_VER) || _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
#define MOODYCAMEL_HAS_EMPLACE 1
#endif
#endif
#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#if defined (__APPLE__) && defined (__MACH__) && __cplusplus >= 201703L
// This is required to find out what deployment target we are using
#include <AvailabilityMacros.h>
#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(MAC_OS_X_VERSION_10_14) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14
// C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we can't support over-alignment in this case
#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#endif
#endif
#endif
#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE)
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4324) // structure was padded due to __declspec(align())
#pragma warning(disable: 4820) // padding was added
#pragma warning(disable: 4127) // conditional expression is constant
#endif
namespace moodycamel {
template<typename T, size_t MAX_BLOCK_SIZE = 512>
class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue
{
// Design: Based on a queue-of-queues. The low-level queues are just
// circular buffers with front and tail indices indicating where the
// next element to dequeue is and where the next element can be enqueued,
// respectively. Each low-level queue is called a "block". Each block
// wastes exactly one element's worth of space to keep the design simple
// (if front == tail then the queue is empty, and can't be full).
// The high-level queue is a circular linked list of blocks; again there
// is a front and tail, but this time they are pointers to the blocks.
// The front block is where the next element to be dequeued is, provided
// the block is not empty. The back block is where elements are to be
// enqueued, provided the block is not full.
// The producer thread owns all the tail indices/pointers. The consumer
// thread owns all the front indices/pointers. Both threads read each
// other's variables, but only the owning thread updates them. E.g. After
// the consumer reads the producer's tail, the tail may change before the
// consumer is done dequeuing an object, but the consumer knows the tail
// will never go backwards, only forwards.
// If there is no room to enqueue an object, an additional block (of
// equal size to the last block) is added. Blocks are never removed.
public:
typedef T value_type;
// Constructs a queue that can hold at least `size` elements without further
// allocations. If more than MAX_BLOCK_SIZE elements are requested,
// then several blocks of MAX_BLOCK_SIZE each are reserved (including
// at least one extra buffer block).
AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15)
#ifndef NDEBUG
: enqueuing(false)
,dequeuing(false)
#endif
{
assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2");
assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
Block* firstBlock = nullptr;
largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block
if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
// We need a spare block in case the producer is writing to a different block the consumer is reading from, and
// wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity
// between front == tail meaning "empty" and "full".
// So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the
// number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying):
size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
largestBlockSize = MAX_BLOCK_SIZE;
Block* lastBlock = nullptr;
for (size_t i = 0; i != initialBlockCount; ++i) {
auto block = make_block(largestBlockSize);
if (block == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
if (firstBlock == nullptr) {
firstBlock = block;
}
else {
lastBlock->next = block;
}
lastBlock = block;
block->next = firstBlock;
}
}
else {
firstBlock = make_block(largestBlockSize);
if (firstBlock == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
firstBlock->next = firstBlock;
}
frontBlock = firstBlock;
tailBlock = firstBlock;
// Make sure the reader/writer threads will have the initialized memory setup above:
fence(memory_order_sync);
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other)
: frontBlock(other.frontBlock.load()),
tailBlock(other.tailBlock.load()),
largestBlockSize(other.largestBlockSize)
#ifndef NDEBUG
,enqueuing(false)
,dequeuing(false)
#endif
{
other.largestBlockSize = 32;
Block* b = other.make_block(other.largestBlockSize);
if (b == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
b->next = b;
other.frontBlock = b;
other.tailBlock = b;
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN
{
Block* b = frontBlock.load();
frontBlock = other.frontBlock.load();
other.frontBlock = b;
b = tailBlock.load();
tailBlock = other.tailBlock.load();
other.tailBlock = b;
std::swap(largestBlockSize, other.largestBlockSize);
return *this;
}
// Note: The queue should not be accessed concurrently while it's
// being deleted. It's up to the user to synchronize this.
AE_NO_TSAN ~ReaderWriterQueue()
{
// Make sure we get the latest version of all variables from other CPUs:
fence(memory_order_sync);
// Destroy any remaining objects in queue and free memory
Block* frontBlock_ = frontBlock;
Block* block = frontBlock_;
do {
Block* nextBlock = block->next;
size_t blockFront = block->front;
size_t blockTail = block->tail;
for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
element->~T();
(void)element;
}
auto rawBlock = block->rawThis;
block->~Block();
std::free(rawBlock);
block = nextBlock;
} while (block != frontBlock_);
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN
{
return inner_enqueue<CannotAlloc>(element);
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN
{
return inner_enqueue<CannotAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN
{
return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN
{
return inner_enqueue<CanAlloc>(element);
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN
{
return inner_enqueue<CanAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN
{
return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template<typename U>
bool try_dequeue(U& result) AE_NO_TSAN
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// High-level pseudocode:
// Remember where the tail block is
// If the front block has an element in it, dequeue it
// Else
// If front block was the tail block when we entered the function, return false
// Else advance to next block and dequeue the item there
// Note that we have to use the value of the tail block from before we check if the front
// block is full or not, in case the front block is empty and then, before we check if the
// tail block is at the front block or not, the producer fills up the front block *and
// moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
// reproducible in practice.
// In order to avoid overhead in the common case, though, we do a double-checked pattern
// where we have the fast path if the front block is not empty, then read the tail block,
// then re-read the front block and check if it's not empty again, then check if the tail
// block has advanced.
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
// Front block not empty, dequeue from here
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
result = std::move(*element);
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
// Oh look, the front block isn't empty after all
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
// Don't need an acquire fence here since next can only ever be set on the tailBlock,
// and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which
// ensures next is up-to-date on this CPU in case we recently were at tailBlock.
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
// Since the tailBlock is only ever advanced after being written to,
// we know there's for sure an element to dequeue on it
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
// We're done with this block, let the producer use it if it needs
fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release); // Not strictly needed
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
result = std::move(*element);
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
}
else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
T* peek() const AE_NO_TSAN
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlock->tail.load());
return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
}
return nullptr;
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
bool pop() AE_NO_TSAN
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
fence(memory_order_release);
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release);
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
}
else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
inline size_t size_approx() const AE_NO_TSAN
{
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
size_t blockFront = block->front.load();
size_t blockTail = block->tail.load();
result += (blockTail - blockFront) & block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
// Returns the total number of items that could be enqueued without incurring
// an allocation when this queue is empty.
// Safe to call from both the producer and consumer threads.
//
// NOTE: The actual capacity during usage may be different depending on the consumer.
// If the consumer is removing elements concurrently, the producer cannot add to
// the block the consumer is removing from until it's completely empty, except in
// the case where the producer was writing to the same block the consumer was
// reading from the whole time.
inline size_t max_capacity() const {
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
result += block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
private:
enum AllocationMode { CanAlloc, CannotAlloc };
#if MOODYCAMEL_HAS_EMPLACE
template<AllocationMode canAlloc, typename... Args>
bool inner_enqueue(Args&&... args) AE_NO_TSAN
#else
template<AllocationMode canAlloc, typename U>
bool inner_enqueue(U&& element) AE_NO_TSAN
#endif
{
#ifndef NDEBUG
ReentrantGuard guard(this->enqueuing);
#endif
// High-level pseudocode (assuming we're allowed to alloc a new block):
// If room in tail block, add to tail
// Else check next block
// If next block is not the head block, enqueue on next block
// Else create a new block and enqueue there
// Advance tail to the block we just enqueued to
Block* tailBlock_ = tailBlock.load();
size_t blockFront = tailBlock_->localFront;
size_t blockTail = tailBlock_->tail.load();
size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
fence(memory_order_acquire);
// This block has room for at least one more element
char* location = tailBlock_->data + blockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
fence(memory_order_release);
tailBlock_->tail = nextBlockTail;
}
else {
fence(memory_order_acquire);
if (tailBlock_->next.load() != frontBlock) {
// Note that the reason we can't advance to the frontBlock and start adding new entries there
// is because if we did, then dequeue would stay in that block, eventually reading the new values,
// instead of advancing to the next full block (whose values were enqueued first and so should be
// consumed first).
fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock
// tailBlock is full, but there's a free block ahead, use it
Block* tailBlockNext = tailBlock_->next.load();
size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
nextBlockTail = tailBlockNext->tail.load();
fence(memory_order_acquire);
// This block must be empty since it's not the head block and we
// go through the blocks in a circle
assert(nextBlockFront == nextBlockTail);
tailBlockNext->localFront = nextBlockFront;
char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
fence(memory_order_release);
tailBlock = tailBlockNext;
}
else if (canAlloc == CanAlloc) {
// tailBlock is full and there's no free block ahead; create a new block
auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
auto newBlock = make_block(newBlockSize);
if (newBlock == nullptr) {
// Could not allocate a block!
return false;
}
largestBlockSize = newBlockSize;
#if MOODYCAMEL_HAS_EMPLACE
new (newBlock->data) T(std::forward<Args>(args)...);
#else
new (newBlock->data) T(std::forward<U>(element));
#endif
assert(newBlock->front == 0);
newBlock->tail = newBlock->localTail = 1;
newBlock->next = tailBlock_->next.load();
tailBlock_->next = newBlock;
// Might be possible for the dequeue thread to see the new tailBlock->next
// *without* seeing the new tailBlock value, but this is OK since it can't
// advance to the next block until tailBlock is set anyway (because the only
// case where it could try to read the next is if it's already at the tailBlock,
// and it won't advance past tailBlock in any circumstance).
fence(memory_order_release);
tailBlock = newBlock;
}
else if (canAlloc == CannotAlloc) {
// Would have had to allocate a new block to enqueue, but not allowed
return false;
}
else {
assert(false && "Should be unreachable code");
return false;
}
}
return true;
}
// Disable copying
ReaderWriterQueue(ReaderWriterQueue const&) { }
// Disable assignment
ReaderWriterQueue& operator=(ReaderWriterQueue const&) { }
AE_FORCEINLINE static size_t ceilToPow2(size_t x)
{
// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
x |= x >> (i << 3);
}
++x;
return x;
}
template<typename U>
static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN
{
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
private:
#ifndef NDEBUG
struct ReentrantGuard
{
AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection)
: inSection(_inSection)
{
assert(!inSection && "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)");
inSection = true;
}
AE_NO_TSAN ~ReentrantGuard() { inSection = false; }
private:
ReentrantGuard& operator=(ReentrantGuard const&);
private:
weak_atomic<bool>& inSection;
};
#endif
struct Block
{
// Avoid false-sharing by putting highly contended variables on their own cache lines
weak_atomic<size_t> front; // (Atomic) Elements are read from here
size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)];
weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
size_t localFront;
char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is)
weak_atomic<Block*> next; // (Atomic)
char* data; // Contents (on heap) are aligned to T's alignment
const size_t sizeMask;
// size must be a power of two (and greater than 0)
AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data)
: front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis)
{
}
private:
// C4512 - Assignment operator could not be generated
Block& operator=(Block const&);
public:
char* rawThis;
};
static Block* make_block(size_t capacity) AE_NO_TSAN
{
// Allocate enough memory for the block itself, as well as all the elements it will contain
auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
auto newBlockRaw = static_cast<char*>(std::malloc(size));
if (newBlockRaw == nullptr) {
return nullptr;
}
auto newBlockAligned = align_for<Block>(newBlockRaw);
auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
}
private:
weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block
char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block
size_t largestBlockSize;
#ifndef NDEBUG
weak_atomic<bool> enqueuing;
mutable weak_atomic<bool> dequeuing;
#endif
};
// Like ReaderWriterQueue, but also providees blocking operations
template<typename T, size_t MAX_BLOCK_SIZE = 512>
class BlockingReaderWriterQueue
{
private:
typedef ::moodycamel::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
public:
explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN
: inner(size), sema(new spsc_sema::LightweightSemaphore())
{ }
BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN
: inner(std::move(other.inner)), sema(std::move(other.sema))
{ }
BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN
{
std::swap(sema, other.sema);
std::swap(inner, other.inner);
return *this;
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN
{
if (inner.try_enqueue(element)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN
{
if (inner.try_enqueue(std::forward<T>(element))) {
sema->signal();
return true;
}
return false;
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN
{
if (inner.try_emplace(std::forward<Args>(args)...)) {
sema->signal();
return true;
}
return false;
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN
{
if (inner.enqueue(element)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN
{
if (inner.enqueue(std::forward<T>(element))) {
sema->signal();
return true;
}
return false;
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN
{
if (inner.emplace(std::forward<Args>(args)...)) {
sema->signal();
return true;
}
return false;
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template<typename U>
bool try_dequeue(U& result) AE_NO_TSAN
{
if (sema->tryWait()) {
bool success = inner.try_dequeue(result);
assert(success);
AE_UNUSED(success);
return true;
}
return false;
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available, then dequeues it.
template<typename U>
void wait_dequeue(U& result) AE_NO_TSAN
{
while (!sema->wait());
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template<typename U>
bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN
{
if (!sema->wait(timeout_usecs)) {
return false;
}
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
return true;
}
#if __cplusplus > 199711L || _MSC_VER >= 1700
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& result, std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN
{
return wait_dequeue_timed(result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
#endif
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
AE_FORCEINLINE T* peek() const AE_NO_TSAN
{
return inner.peek();
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
AE_FORCEINLINE bool pop() AE_NO_TSAN
{
if (sema->tryWait()) {
bool result = inner.pop();
assert(result);
AE_UNUSED(result);
return true;
}
return false;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN
{
return sema->availableApprox();
}
// Returns the total number of items that could be enqueued without incurring
// an allocation when this queue is empty.
// Safe to call from both the producer and consumer threads.
//
// NOTE: The actual capacity during usage may be different depending on the consumer.
// If the consumer is removing elements concurrently, the producer cannot add to
// the block the consumer is removing from until it's completely empty, except in
// the case where the producer was writing to the same block the consumer was
// reading from the whole time.
AE_FORCEINLINE size_t max_capacity() const {
return inner.max_capacity();
}
private:
// Disable copying & assignment
BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) { }
BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) { }
private:
ReaderWriterQueue inner;
std::unique_ptr<spsc_sema::LightweightSemaphore> sema;
};
} // end namespace moodycamel
#ifdef AE_VCPP
#pragma warning(pop)
#endif

View file

@ -0,0 +1,3 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake)

View file

@ -0,0 +1,82 @@
#include "simplethread.h"
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct SimpleThread::ThreadRef
{
HANDLE handle;
static DWORD WINAPI ThreadProc(LPVOID param)
{
auto threadRef = static_cast<ThreadRef*>(param);
threadRef->callbackFunc(threadRef->callbackObj);
return 0;
}
ThreadRef(void* callbackObj, CallbackFunc callbackFunc)
: callbackObj(callbackObj), callbackFunc(callbackFunc)
{
}
void* callbackObj;
CallbackFunc callbackFunc;
};
void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc)
{
thread = new ThreadRef(callbackObj, callbackFunc);
thread->handle = CreateThread(NULL, StackSize, &ThreadRef::ThreadProc, thread, 0, NULL);
}
void SimpleThread::join()
{
if (thread != nullptr && thread->handle != NULL) {
WaitForSingleObject(thread->handle, INFINITE);
CloseHandle(thread->handle);
thread->handle = NULL;
}
}
#else
#include <thread>
struct SimpleThread::ThreadRef
{
std::thread thread;
static void threadProc(ThreadRef* threadRef)
{
threadRef->callbackFunc(threadRef->callbackObj);
}
ThreadRef(void* callbackObj, CallbackFunc callbackFunc)
: callbackObj(callbackObj), callbackFunc(callbackFunc)
{
}
void* callbackObj;
CallbackFunc callbackFunc;
};
void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc)
{
thread = new ThreadRef(callbackObj, callbackFunc);
thread->thread = std::thread(&ThreadRef::threadProc, thread);
}
void SimpleThread::join()
{
if (thread != nullptr && thread->thread.joinable()) {
thread->thread.join();
}
}
#endif
SimpleThread::~SimpleThread()
{
if (thread != nullptr) {
join();
delete thread;
}
}

View file

@ -0,0 +1,154 @@
#pragma once
// Like C++11's std::thread, but with a reduced API, and works on Windows with MSVC2010+.
// Wraps std::thread on other OSes. Perhaps the most significant departure between
// std::thread and this mini-library is that join() is called implicitly in the destructor,
// if the thread is joinable. The thread callback functions should not throw exceptions.
#include <utility>
#include <type_traits>
namespace details
{
template<typename TArg1 = void, typename TArg2 = void, typename TArg3 = void>
struct ArgWrapper
{
typename std::remove_reference<TArg1>::type arg1;
typename std::remove_reference<TArg2>::type arg2;
typename std::remove_reference<TArg3>::type arg3;
template<typename T, typename U, typename V>
ArgWrapper(T&& a1, U&& a2, V&& a3) : arg1(std::forward<T>(a1)), arg2(std::forward<U>(a2)), arg3(std::forward<V>(a3)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1), std::move(arg2), std::move(arg3)); }
};
template<typename TArg1, typename TArg2>
struct ArgWrapper<TArg1, TArg2, void>
{
typename std::remove_reference<TArg1>::type arg1;
typename std::remove_reference<TArg2>::type arg2;
template<typename T, typename U>
ArgWrapper(T&& a1, U&& a2) : arg1(std::forward<T>(a1)), arg2(std::forward<U>(a2)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1), std::move(arg2)); }
};
template<typename TArg1>
struct ArgWrapper<TArg1, void, void>
{
typename std::remove_reference<TArg1>::type arg1;
template<typename T>
ArgWrapper(T&& a1) : arg1(std::forward<T>(a1)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1)); }
};
template<> struct ArgWrapper<void, void, void>
{
template<typename TCallback> void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(); }
};
}
class SimpleThread
{
private:
struct ThreadRef;
template<typename TCallback, typename TArgs>
struct CallbackWrapper
{
template<typename U>
CallbackWrapper(TCallback&& callback, U&& args)
: callback(std::forward<TCallback>(callback)), args(std::forward<U>(args))
{
}
static void callAndDelete(void* wrapper)
{
auto typedWrapper = static_cast<CallbackWrapper*>(wrapper);
typedWrapper->args.callCallback(std::move(typedWrapper->callback));
delete typedWrapper;
}
typename std::decay<TCallback>::type callback;
TArgs args;
};
typedef void (*CallbackFunc)(void*);
void startThread(void* callbackObj, CallbackFunc callbackFunc);
public:
static const int StackSize = 4 * 1024; // bytes
SimpleThread() : thread(nullptr) { }
SimpleThread(SimpleThread&& other)
: thread(other.thread)
{
other.thread = nullptr;
}
SimpleThread& operator=(SimpleThread&& other)
{
thread = other.thread;
other.thread = nullptr;
return *this;
}
// Disable copying and copy-assignment
private:
SimpleThread(SimpleThread const&);
SimpleThread& operator=(SimpleThread const&);
public:
template<typename TCallback>
explicit SimpleThread(TCallback&& callback)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<>>(
std::forward<TCallback>(callback),
details::ArgWrapper<>()
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<>>::callAndDelete);
}
template<typename TCallback, typename TArg1>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<TArg1>>(
std::forward<TCallback>(callback),
details::ArgWrapper<TArg1>(std::forward<TArg1>(arg1))
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<TArg1>>::callAndDelete);
}
template<typename TCallback, typename TArg1, typename TArg2>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2>>(
std::forward<TCallback>(callback),
details::ArgWrapper<TArg1, TArg2>(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2))
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2>>::callAndDelete);
}
template<typename TCallback, typename TArg1, typename TArg2, typename TArg3>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2, TArg3&& arg3)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2, TArg3>>(
std::forward<TCallback>(callback),
details::ArgWrapper<TArg1, TArg2, TArg3>(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), std::forward<TArg3>(arg3))
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2, TArg3>>::callAndDelete);
}
~SimpleThread();
void join();
private:
ThreadRef* thread;
};

View file

@ -0,0 +1,24 @@
ifeq ($(OS),Windows_NT)
EXT=.exe
PLATFORM_OPTS=-static
PLATFORM_LD_OPTS=-Wl,--no-as-needed
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
EXT=
PLATFORM_OPTS=
PLATFORM_LD_OPTS=
else
EXT=
PLATFORM_OPTS=
PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed
endif
endif
default: stabtest$(EXT)
stabtest$(EXT): stabtest.cpp ../../readerwriterqueue.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp makefile
g++ $(PLATFORM_OPTS) -std=c++11 -Wsign-conversion -Wpedantic -Wall -DNDEBUG -O3 stabtest.cpp ../common/simplethread.cpp -o stabtest$(EXT) -pthread $(PLATFORM_LD_OPTS)
run: stabtest$(EXT)
./stabtest$(EXT)

View file

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{16E74A53-972D-4762-BC18-8946FB1EF452}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>stabtest</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp" />
<ClCompile Include="..\stabtest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h" />
<ClInclude Include="..\..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\common\simplethread.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\stabtest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{16E74A53-972D-4762-BC18-8946FB1EF452}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>stabtest</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp" />
<ClCompile Include="..\stabtest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h" />
<ClInclude Include="..\..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\common\simplethread.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\stabtest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,80 @@
#include "../../readerwriterqueue.h"
#include "../common/simplethread.h"
using namespace moodycamel;
#include <cstdlib>
#include <exception>
#include <fstream>
#include <cstdlib> // rand()
//#include <unistd.h> // usleep()
void unpredictableDelay(int extra = 0)
{
/* if ((rand() & 4095) == 0) {
usleep(2000 + extra); // in microseconds
}*/
}
int main(int argc, char** argv)
{
// Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written)
std::setvbuf(stdout, nullptr, _IONBF, 0);
std::printf("Running stability test for moodycamel::ReaderWriterQueue.\n");
std::printf("Logging to 'log.txt'. Press CTRL+C to quit.\n\n");
std::ofstream log("log.txt");
try {
for (unsigned int i = 0; true; ++i) {
log << "Test #" << i << std::endl;
std::printf("Test #%d\n", i);
ReaderWriterQueue<unsigned long long> q((rand() % 32) + 1);
SimpleThread writer([&]() {
for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL; ++j) {
unpredictableDelay(500);
q.enqueue(j);
}
});
SimpleThread reader([&]() {
bool canLog = true;
unsigned long long element;
for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL;) {
if (canLog && (j & (1024 * 1024 * 16 - 1)) == 0) {
log << " ... iteration " << j << std::endl;
std::printf(" ... iteration %llu\n", j);
canLog = false;
}
unpredictableDelay();
if (q.try_dequeue(element)) {
if (element != j) {
log << " ERROR DETECTED: Expected to read " << j << " but found " << element << std::endl;
std::printf(" ERROR DETECTED: Expected to read %llu but found %llu", j, element);
}
++j;
canLog = true;
}
}
if (q.try_dequeue(element)) {
log << " ERROR DETECTED: Expected queue to be empty" << std::endl;
std::printf(" ERROR DETECTED: Expected queue to be empty\n");
}
});
writer.join();
reader.join();
}
}
catch (std::exception const& ex) {
log << " ERROR DETECTED: Exception thrown: " << ex.what() << std::endl;
std::printf(" ERROR DETECTED: Exception thrown: %s\n", ex.what());
}
return 0;
}

Some files were not shown because too many files have changed in this diff Show more