Compare commits
205 Commits
Author | SHA1 | Date | |
---|---|---|---|
e876c43ce9 | |||
8639245533 | |||
d6b86a6629 | |||
8f2b7b5b8e | |||
fc4b7cba2c | |||
08831eecf7 | |||
c6a139a6e7 | |||
02714a1291 | |||
09c9686498 | |||
b6614c6ad5 | |||
b1d4b174a6 | |||
2b23463daa | |||
9dfe81770a | |||
52c115a1f8 | |||
e20abbf9cc | |||
76671d63d4 | |||
3d1a0bf374 | |||
c20f3fbebd | |||
0d3b82477e | |||
470be03c2f | |||
c963b3c804 | |||
a4fdfb5f94 | |||
37d27c4c99 | |||
f906eb06c2 | |||
219f63ff4e | |||
1cca3e99ab | |||
55a23e5ec8 | |||
479d1fd8b0 | |||
cb70e7bb30 | |||
c200a7b7c6 | |||
6268170a10 | |||
ee0f9b03a4 | |||
f93c5f006a | |||
295fbd0542 | |||
d7310d7a1c | |||
8c50943a2e | |||
ec4cd57ccf | |||
5a085cba0f | |||
1a1d33a018 | |||
0fbcd630bc | |||
f4d731ae20 | |||
8ac53c66b4 | |||
0f50de72be | |||
df758eddd1 | |||
5f32a8ed94 | |||
535fbec675 | |||
6fe88115a3 | |||
475fa4d390 | |||
edf7e628ca | |||
ba5c0cf5d8 | |||
403e67d983 | |||
c6f1908e0f | |||
851d81d24a | |||
459c4caeba | |||
539b22ef7b | |||
872f036d64 | |||
dca96122bf | |||
e752959109 | |||
cf01664698 | |||
b283a4adcd | |||
8428bb6541 | |||
9a0330f7f8 | |||
57fc996337 | |||
1f3b860f06 | |||
abe3c02ab4 | |||
45b417b2b4 | |||
d076339e3e | |||
837836431d | |||
9f555db5cd | |||
bf7fa60dfc | |||
752b93d3b7 | |||
f23b2878cc | |||
e211c3f00a | |||
d3709a753f | |||
ab676d58ea | |||
2372c194f1 | |||
40311310d1 | |||
dde9bb5c69 | |||
266338a7c9 | |||
90156eea4c | |||
071c01c235 | |||
de06ffb0f7 | |||
8a7de35e3f | |||
121296834a | |||
bbb24d8c7e | |||
4da44e09cb | |||
ae13f0ab4d | |||
a2a35f1be6 | |||
aedfadaaf7 | |||
5c0fb0cec3 | |||
17a1cab5d2 | |||
73aed239c3 | |||
9ac66336a2 | |||
4965681e06 | |||
3868a00206 | |||
933e5144a9 | |||
73a42c85c4 | |||
39ba11054b | |||
c250e3392c | |||
e56b069081 | |||
204c031fef | |||
d9053bbe37 | |||
c25e8427aa | |||
21a081b185 | |||
b540ea80d1 | |||
d692a9b83e | |||
9677ddaa5d | |||
ce92e8cd04 | |||
456fc04007 | |||
458452279c | |||
817b89767a | |||
3fb583c98c | |||
d2686e0a5b | |||
4905101df1 | |||
8750b90a7f | |||
af01100050 | |||
c0821fee1f | |||
a5c2aead67 | |||
d41c95dcff | |||
fbf2b09706 | |||
1fc0f569de | |||
dff138229c | |||
472119c8da | |||
1865ea87e5 | |||
18b61aff59 | |||
cb22629ac1 | |||
6f0f99ee2b | |||
70f2da8fdf | |||
5d3ef7761b | |||
476b4683cf | |||
5fb5079730 | |||
3fbacd0f49 | |||
7aa6abc120 | |||
548bfd60a2 | |||
65778a6b78 | |||
f4e879a1e6 | |||
a1ddaa2736 | |||
008286b79f | |||
a0c77f8d11 | |||
ece36b274d | |||
f3cc2e5703 | |||
5a39d3c4a1 | |||
cc51a03af9 | |||
567c64e149 | |||
36f00985d3 | |||
748d87adcc | |||
0fd47ff490 | |||
f088c3d344 | |||
905a191e28 | |||
ab0491817e | |||
5de6ae426e | |||
69ced3a6e8 | |||
2e43d01d36 | |||
7373ec5792 | |||
de162a648b | |||
131baebe2a | |||
187372cbde | |||
022d495335 | |||
c1372ed775 | |||
a16682cfd3 | |||
7c53b69c30 | |||
33a4d7d1ba | |||
391e08dd27 | |||
b5cf8b8af9 | |||
55043c8afc | |||
5d73a9f5fc | |||
2c9ab5e45f | |||
d536cc8ae6 | |||
d751da84f9 | |||
11aae9cfbc | |||
b96794e72b | |||
f1d1670b0b | |||
b8de72de8f | |||
eebc39228d | |||
9daf029f35 | |||
51a27032f0 | |||
a6a67a2b7a | |||
c6d05301aa | |||
647de4cd31 | |||
f82309fa2d | |||
7d8e198c33 | |||
3d98e1361b | |||
141cf61ff7 | |||
3fe3598d41 | |||
59cdf310bd | |||
4e34170a84 | |||
d540af5dc0 | |||
f7c7b66fc0 | |||
28ba55598d | |||
9719b6a112 | |||
f70236f947 | |||
eafadf10c7 | |||
9b06ee7736 | |||
baba2c2467 | |||
286e5d39b2 | |||
dc529c1181 | |||
c7cf1cbc35 | |||
d8e487d018 | |||
5fdc46ac7f | |||
1e5b45f580 | |||
62585755fd | |||
56621615b1 | |||
2099a3e84b | |||
7d26e4ac7b | |||
8d41402fa6 |
@ -89,6 +89,7 @@ csharp_style_conditional_delegate_call = true:suggestion
|
||||
# Modifier preferences
|
||||
csharp_prefer_static_local_function = true:suggestion
|
||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||
csharp_style_prefer_readonly_struct = true
|
||||
|
||||
# Code-block preferences
|
||||
csharp_prefer_braces = true:silent
|
||||
|
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
4
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression.
|
||||
about: Something doesn't work correctly in Ryujinx. Game-specific issues should be posted at https://github.com/Ryujinx/Ryujinx-Games-List instead, unless it is a provable regression.
|
||||
#assignees:
|
||||
---
|
||||
|
||||
@ -34,7 +34,7 @@ about: Something doesn't work correctly in Ryujinx. Note that game-specific issu
|
||||
- OS: *(e.g. Windows 10)*
|
||||
- CPU: *(e.g. i7-6700)*
|
||||
- GPU: *(e.g. NVIDIA RTX 2070)*
|
||||
- RAM: *(e.g. 16GB)*
|
||||
- RAM: *(e.g. 16GiB)*
|
||||
- Applied Mods : [ Yes (Which ones) / No ]
|
||||
|
||||
### Additional context?
|
||||
|
24
.github/dependabot.yml
vendored
Normal file
24
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- "infra"
|
||||
reviewers:
|
||||
- marysaka
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
|
||||
- package-ecosystem: nuget
|
||||
directory: /
|
||||
open-pull-requests-limit: 5
|
||||
schedule:
|
||||
interval: daily
|
||||
labels:
|
||||
- "infra"
|
||||
reviewers:
|
||||
- marysaka
|
||||
commit-message:
|
||||
prefix: nuget
|
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
@ -48,44 +48,41 @@ jobs:
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
RYUJINX_BASE_VERSION: "1.1.0"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Ensure NuGet Source
|
||||
uses: fabriciomurta/ensure-nuget-source@v1
|
||||
dotnet-version: 7.0.x
|
||||
- name: Get git short hash
|
||||
id: git_short_hash
|
||||
run: echo "::set-output name=result::$(git rev-parse --short "${{ github.sha }}")"
|
||||
- name: Clear
|
||||
run: dotnet clean && dotnet nuget locals all --clear
|
||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- name: Build
|
||||
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
|
||||
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
|
||||
- name: Test
|
||||
run: dotnet test -c "${{ matrix.configuration }}"
|
||||
run: dotnet test --no-build -c "${{ matrix.configuration }}"
|
||||
- name: Publish Ryujinx
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Publish Ryujinx.Headless.SDL2
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Publish Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava /p:Version="1.0.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava
|
||||
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Upload Ryujinx artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Upload Ryujinx.Headless.SDL2 artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_sdl2_headless
|
||||
if: github.event_name == 'pull_request'
|
||||
- name: Upload Ryujinx.Ava artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
|
||||
path: publish_ava
|
||||
|
12
.github/workflows/nightly_pr_comment.yml
vendored
12
.github/workflows/nightly_pr_comment.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v3
|
||||
- uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const {owner, repo} = context.repo;
|
||||
@ -16,7 +16,7 @@ jobs:
|
||||
const pull_head_sha = '${{github.event.workflow_run.head_sha}}';
|
||||
|
||||
const issue_number = await (async () => {
|
||||
const pulls = await github.pulls.list({owner, repo});
|
||||
const pulls = await github.rest.pulls.list({owner, repo});
|
||||
for await (const {data} of github.paginate.iterator(pulls)) {
|
||||
for (const pull of data) {
|
||||
if (pull.head.sha === pull_head_sha) {
|
||||
@ -31,7 +31,7 @@ jobs:
|
||||
return core.error(`No matching pull request found`);
|
||||
}
|
||||
|
||||
const {data: {artifacts}} = await github.actions.listWorkflowRunArtifacts({owner, repo, run_id});
|
||||
const {data: {artifacts}} = await github.rest.actions.listWorkflowRunArtifacts({owner, repo, run_id});
|
||||
if (!artifacts.length) {
|
||||
return core.error(`No artifacts found`);
|
||||
}
|
||||
@ -57,12 +57,12 @@ jobs:
|
||||
body += hidden_headless_artifacts;
|
||||
body += hidden_debug_artifacts;
|
||||
|
||||
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});
|
||||
const {data: comments} = await github.rest.issues.listComments({repo, owner, issue_number});
|
||||
const existing_comment = comments.find((c) => c.user.login === 'github-actions[bot]');
|
||||
if (existing_comment) {
|
||||
core.info(`Updating comment ${existing_comment.id}`);
|
||||
await github.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
|
||||
await github.rest.issues.updateComment({repo, owner, comment_id: existing_comment.id, body});
|
||||
} else {
|
||||
core.info(`Creating a comment`);
|
||||
await github.issues.createComment({repo, owner, issue_number, body});
|
||||
await github.rest.issues.createComment({repo, owner, issue_number, body});
|
||||
}
|
||||
|
27
.github/workflows/release.yml
vendored
27
.github/workflows/release.yml
vendored
@ -11,6 +11,7 @@ on:
|
||||
- '*.yml'
|
||||
- 'README.md'
|
||||
|
||||
concurrency: release
|
||||
|
||||
jobs:
|
||||
release:
|
||||
@ -25,19 +26,15 @@ jobs:
|
||||
RYUJINX_TARGET_RELEASE_CHANNEL_REPO: "release-channel-master"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-dotnet@v1
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
- name: Ensure NuGet Source
|
||||
uses: fabriciomurta/ensure-nuget-source@v1
|
||||
- name: Clear
|
||||
run: dotnet clean && dotnet nuget locals all --clear
|
||||
dotnet-version: 7.0.x
|
||||
- name: Get version info
|
||||
id: version_info
|
||||
run: |
|
||||
echo "::set-output name=build_version::${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}"
|
||||
echo "::set-output name=git_short_hash::$(git rev-parse --short "${{ github.sha }}")"
|
||||
echo "build_version=${{ env.RYUJINX_BASE_VERSION }}.${{ github.run_number }}" >> $GITHUB_OUTPUT
|
||||
echo "git_short_hash=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
- name: Configure for release
|
||||
run: |
|
||||
@ -51,9 +48,9 @@ jobs:
|
||||
run: "mkdir release_output"
|
||||
- name: Publish Windows
|
||||
run: |
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
||||
- name: Packing Windows builds
|
||||
run: |
|
||||
pushd publish_windows
|
||||
@ -71,9 +68,9 @@ jobs:
|
||||
|
||||
- name: Publish Linux
|
||||
run: |
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx --self-contained
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish /p:Version="${{ steps.version_info.outputs.build_version }}" /p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" /p:DebugType=embedded Ryujinx.Ava --self-contained
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
|
||||
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
|
||||
|
||||
- name: Packing Linux builds
|
||||
run: |
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -125,6 +125,9 @@ ClientBin/
|
||||
packages/*
|
||||
*.config
|
||||
|
||||
# Include nuget.config
|
||||
!nuget.config
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
struct AllocationResult
|
||||
readonly struct AllocationResult
|
||||
{
|
||||
public int IntUsedRegisters { get; }
|
||||
public int VecUsedRegisters { get; }
|
||||
|
@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
private class ParallelCopy
|
||||
{
|
||||
private struct Copy
|
||||
private readonly struct Copy
|
||||
{
|
||||
public Register Dest { get; }
|
||||
public Register Source { get; }
|
||||
|
@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
class HybridAllocator : IRegisterAllocator
|
||||
{
|
||||
private struct BlockInfo
|
||||
private readonly struct BlockInfo
|
||||
{
|
||||
public bool HasCall { get; }
|
||||
|
||||
|
@ -3,7 +3,7 @@ using System;
|
||||
|
||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||
{
|
||||
struct RegisterMasks
|
||||
readonly struct RegisterMasks
|
||||
{
|
||||
public int IntAvailableRegisters { get; }
|
||||
public int VecAvailableRegisters { get; }
|
||||
|
@ -1587,6 +1587,12 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
|
||||
|
||||
// We can eliminate the move if source is already 32-bit and the registers are the same.
|
||||
if (dest.Value == source.Value && source.Type == OperandType.I32)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
context.Assembler.Mov(dest, source, OperandType.I32);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace ARMeilleure.CodeGen.X86
|
||||
{
|
||||
struct IntrinsicInfo
|
||||
readonly struct IntrinsicInfo
|
||||
{
|
||||
public X86Instruction Inst { get; }
|
||||
public IntrinsicType Type { get; }
|
||||
|
@ -80,10 +80,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
@ -100,10 +97,7 @@ namespace ARMeilleure.Common
|
||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||
public AddressTable(Level[] levels)
|
||||
{
|
||||
if (levels == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(levels));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(levels);
|
||||
|
||||
if (levels.Length < 2)
|
||||
{
|
||||
@ -141,10 +135,7 @@ namespace ARMeilleure.Common
|
||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||
public ref TEntry GetValue(ulong address)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
if (!IsValid(address))
|
||||
{
|
||||
|
@ -1,15 +1,11 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
static class BitUtils
|
||||
{
|
||||
private static readonly sbyte[] HbsNibbleLut;
|
||||
|
||||
static BitUtils()
|
||||
{
|
||||
HbsNibbleLut = new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
||||
}
|
||||
private static ReadOnlySpan<sbyte> HbsNibbleLut => new sbyte[] { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 };
|
||||
|
||||
public static long FillWithOnes(int bits)
|
||||
{
|
||||
|
@ -49,10 +49,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return ref _countTable.GetValue(_index);
|
||||
}
|
||||
|
@ -53,10 +53,7 @@ namespace ARMeilleure.Common
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
public int Allocate()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
lock (_allocated)
|
||||
{
|
||||
@ -83,10 +80,7 @@ namespace ARMeilleure.Common
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
public void Free(int index)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
lock (_allocated)
|
||||
{
|
||||
@ -108,10 +102,7 @@ namespace ARMeilleure.Common
|
||||
/// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
|
||||
public ref TEntry GetValue(int index)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
lock (_allocated)
|
||||
{
|
||||
|
@ -2,7 +2,7 @@ using ARMeilleure.Instructions;
|
||||
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
struct InstDescriptor
|
||||
readonly struct InstDescriptor
|
||||
{
|
||||
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);
|
||||
|
||||
|
44
ARMeilleure/Decoders/OpCode32SimdCvtTB.cs
Normal file
44
ARMeilleure/Decoders/OpCode32SimdCvtTB.cs
Normal file
@ -0,0 +1,44 @@
|
||||
namespace ARMeilleure.Decoders
|
||||
{
|
||||
class OpCode32SimdCvtTB : OpCode32, IOpCode32Simd
|
||||
{
|
||||
public int Vd { get; }
|
||||
public int Vm { get; }
|
||||
public bool Op { get; } // Convert to Half / Convert from Half
|
||||
public bool T { get; } // Top / Bottom
|
||||
public int Size { get; } // Double / Single
|
||||
|
||||
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, false);
|
||||
public static OpCode CreateT32(InstDescriptor inst, ulong address, int opCode) => new OpCode32SimdCvtTB(inst, address, opCode, true);
|
||||
|
||||
public OpCode32SimdCvtTB(InstDescriptor inst, ulong address, int opCode, bool isThumb) : base(inst, address, opCode)
|
||||
{
|
||||
IsThumb = isThumb;
|
||||
|
||||
Op = ((opCode >> 16) & 0x1) != 0;
|
||||
T = ((opCode >> 7) & 0x1) != 0;
|
||||
Size = ((opCode >> 8) & 0x1);
|
||||
|
||||
RegisterSize = Size == 1 ? RegisterSize.Int64 : RegisterSize.Int32;
|
||||
|
||||
if (Size == 1)
|
||||
{
|
||||
if (Op)
|
||||
{
|
||||
Vm = ((opCode >> 1) & 0x10) | ((opCode >> 0) & 0xf);
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||
Vd = ((opCode >> 18) & 0x10) | ((opCode >> 12) & 0xf);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vm = ((opCode >> 5) & 0x1) | ((opCode << 1) & 0x1e);
|
||||
Vd = ((opCode >> 22) & 0x1) | ((opCode >> 11) & 0x1e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
|
||||
|
||||
private const int FastLookupSize = 0x1000;
|
||||
|
||||
private struct InstInfo
|
||||
private readonly struct InstInfo
|
||||
{
|
||||
public int Mask { get; }
|
||||
public int Value { get; }
|
||||
@ -828,6 +828,7 @@ namespace ARMeilleure.Decoders
|
||||
SetVfp("<<<<11101x11110xxxxx101x11x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // FP32 to int.
|
||||
SetVfp("<<<<11101x111000xxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_FI, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // Int to FP32.
|
||||
SetVfp("111111101x1111xxxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_RM, OpCode32SimdCvtFI.Create, OpCode32SimdCvtFI.CreateT32); // The many FP32 to int encodings (fp).
|
||||
SetVfp("<<<<11101x11001xxxxx101xx1x0xxxx", InstName.Vcvt, InstEmit32.Vcvt_TB, OpCode32SimdCvtTB.Create, OpCode32SimdCvtTB.CreateT32);
|
||||
SetVfp("<<<<11101x00xxxxxxxx101xx0x0xxxx", InstName.Vdiv, InstEmit32.Vdiv_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
SetVfp("<<<<11101xx0xxxxxxxx1011x0x10000", InstName.Vdup, InstEmit32.Vdup, OpCode32SimdDupGP.Create, OpCode32SimdDupGP.CreateT32);
|
||||
SetVfp("<<<<11101x10xxxxxxxx101xx0x0xxxx", InstName.Vfma, InstEmit32.Vfma_S, OpCode32SimdRegS.Create, OpCode32SimdRegS.CreateT32);
|
||||
@ -1340,7 +1341,7 @@ namespace ARMeilleure.Decoders
|
||||
{
|
||||
string reversedEncoding = encoding.Substring(16) + encoding.Substring(0, 16);
|
||||
MakeOp reversedMakeOp =
|
||||
(InstDescriptor inst, ulong address, int opCode)
|
||||
(inst, address, opCode)
|
||||
=> makeOp(inst, address, (int)BitOperations.RotateRight((uint)opCode, 16));
|
||||
Set(reversedEncoding, AllInstT32, new InstDescriptor(name, emitter), reversedMakeOp);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ namespace ARMeilleure.Diagnostics
|
||||
{
|
||||
static class Symbols
|
||||
{
|
||||
private struct RangedSymbol
|
||||
private readonly struct RangedSymbol
|
||||
{
|
||||
public readonly ulong Start;
|
||||
public readonly ulong End;
|
||||
|
@ -25,13 +25,13 @@ namespace ARMeilleure.Diagnostics
|
||||
_funcTabSizeCounter = new PollingCounter("addr-tab-alloc", this, () => _funcTabSize / 1024d / 1024d)
|
||||
{
|
||||
DisplayName = "AddressTable Total Bytes Allocated",
|
||||
DisplayUnits = "MB"
|
||||
DisplayUnits = "MiB"
|
||||
};
|
||||
|
||||
_funcTabLeafSizeCounter = new PollingCounter("addr-tab-leaf-alloc", this, () => _funcTabLeafSize / 1024d / 1024d)
|
||||
{
|
||||
DisplayName = "AddressTable Total Leaf Bytes Allocated",
|
||||
DisplayUnits = "MB"
|
||||
DisplayUnits = "MiB"
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@ namespace ARMeilleure.Instructions
|
||||
|
||||
for (int index = 0; index < elems; index++)
|
||||
{
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
|
||||
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
|
||||
|
||||
if (sizeF == 0)
|
||||
{
|
||||
@ -389,8 +389,6 @@ namespace ARMeilleure.Instructions
|
||||
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
|
||||
context.LoadFromContext();
|
||||
|
||||
e = context.ZeroExtend16(OperandType.I64, e);
|
||||
|
||||
res = EmitVectorInsert(context, res, e, part + index, 1);
|
||||
}
|
||||
else /* if (sizeF == 1) */
|
||||
|
@ -261,6 +261,74 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
public static void Vcvt_TB(ArmEmitterContext context)
|
||||
{
|
||||
OpCode32SimdCvtTB op = (OpCode32SimdCvtTB)context.CurrOp;
|
||||
|
||||
if (Optimizations.UseF16c)
|
||||
{
|
||||
Debug.Assert(!Optimizations.ForceLegacySse);
|
||||
|
||||
if (op.Op)
|
||||
{
|
||||
Operand res = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
|
||||
if (op.Size == 1)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Cvtsd2ss, context.VectorZero(), res);
|
||||
}
|
||||
res = context.AddIntrinsic(Intrinsic.X86Vcvtps2ph, res, Const(X86GetRoundControl(FPRoundingMode.ToNearest)));
|
||||
res = context.VectorExtract16(res, 0);
|
||||
InsertScalar16(context, op.Vd, op.T, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
Operand res = context.VectorCreateScalar(ExtractScalar16(context, op.Vm, op.T));
|
||||
res = context.AddIntrinsic(Intrinsic.X86Vcvtph2ps, res);
|
||||
if (op.Size == 1)
|
||||
{
|
||||
res = context.AddIntrinsic(Intrinsic.X86Cvtss2sd, context.VectorZero(), res);
|
||||
}
|
||||
res = context.VectorExtract(op.Size == 1 ? OperandType.I64 : OperandType.I32, res, 0);
|
||||
InsertScalar(context, op.Vd, res);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.Op)
|
||||
{
|
||||
// Convert to half.
|
||||
|
||||
Operand src = ExtractScalar(context, op.Size == 1 ? OperandType.FP64 : OperandType.FP32, op.Vm);
|
||||
|
||||
MethodInfo method = op.Size == 1
|
||||
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
|
||||
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
|
||||
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(method, src);
|
||||
context.LoadFromContext();
|
||||
|
||||
InsertScalar16(context, op.Vd, op.T, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert from half.
|
||||
|
||||
Operand src = ExtractScalar16(context, op.Vm, op.T);
|
||||
|
||||
MethodInfo method = op.Size == 1
|
||||
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
|
||||
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
|
||||
|
||||
context.StoreToContext();
|
||||
Operand res = context.Call(method, src);
|
||||
context.LoadFromContext();
|
||||
|
||||
InsertScalar(context, op.Vd, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// VRINTA/M/N/P (floating-point).
|
||||
public static void Vrint_RM(ArmEmitterContext context)
|
||||
{
|
||||
|
@ -70,6 +70,22 @@ namespace ARMeilleure.Instructions
|
||||
context.Copy(vec, insert);
|
||||
}
|
||||
|
||||
public static Operand ExtractScalar16(ArmEmitterContext context, int reg, bool top)
|
||||
{
|
||||
return context.VectorExtract16(GetVecA32(reg >> 2), ((reg & 3) << 1) | (top ? 1 : 0));
|
||||
}
|
||||
|
||||
public static void InsertScalar16(ArmEmitterContext context, int reg, bool top, Operand value)
|
||||
{
|
||||
Debug.Assert(value.Type == OperandType.FP32 || value.Type == OperandType.I32);
|
||||
|
||||
Operand vec, insert;
|
||||
vec = GetVecA32(reg >> 2);
|
||||
insert = context.VectorInsert16(vec, value, ((reg & 3) << 1) | (top ? 1 : 0));
|
||||
|
||||
context.Copy(vec, insert);
|
||||
}
|
||||
|
||||
public static Operand ExtractElement(ArmEmitterContext context, int reg, int size, bool signed)
|
||||
{
|
||||
return EmitVectorExtract32(context, reg >> (4 - size), reg & ((16 >> size) - 1), size, signed);
|
||||
|
@ -48,10 +48,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
|
||||
public void AddSuccessor(BasicBlock block)
|
||||
{
|
||||
if (block == null)
|
||||
{
|
||||
ThrowNull(nameof(block));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(block);
|
||||
|
||||
if ((uint)_succCount + 1 > MaxSuccessors)
|
||||
{
|
||||
@ -100,10 +97,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
|
||||
public void SetSuccessor(int index, BasicBlock block)
|
||||
{
|
||||
if (block == null)
|
||||
{
|
||||
ThrowNull(nameof(block));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(block);
|
||||
|
||||
if ((uint)index >= (uint)_succCount)
|
||||
{
|
||||
@ -144,7 +138,6 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
}
|
||||
}
|
||||
|
||||
private static void ThrowNull(string name) => throw new ArgumentNullException(name);
|
||||
private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
|
||||
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
|
||||
|
||||
|
@ -378,14 +378,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (Kind == OperandKind.LocalVariable)
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return (int)Value ^ ((int)Kind << 16) ^ ((int)Type << 20);
|
||||
}
|
||||
return ((ulong)_data).GetHashCode();
|
||||
}
|
||||
|
||||
public bool Equals(Operand operand)
|
||||
|
@ -3,7 +3,7 @@ using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
struct PhiOperation
|
||||
readonly struct PhiOperation
|
||||
{
|
||||
private readonly Operation _operation;
|
||||
|
||||
|
@ -2,7 +2,7 @@ using System;
|
||||
|
||||
namespace ARMeilleure.IntermediateRepresentation
|
||||
{
|
||||
struct Register : IEquatable<Register>
|
||||
readonly struct Register : IEquatable<Register>
|
||||
{
|
||||
public int Index { get; }
|
||||
|
||||
|
@ -18,17 +18,17 @@ namespace ARMeilleure.Signal
|
||||
public IntPtr sa_restorer;
|
||||
}
|
||||
|
||||
static class UnixSignalHandlerRegistration
|
||||
static partial class UnixSignalHandlerRegistration
|
||||
{
|
||||
private const int SIGSEGV = 11;
|
||||
private const int SIGBUS = 10;
|
||||
private const int SA_SIGINFO = 0x00000004;
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
||||
|
||||
[DllImport("libc", SetLastError = true)]
|
||||
private static extern int sigemptyset(ref SigSet set);
|
||||
[LibraryImport("libc", SetLastError = true)]
|
||||
private static partial int sigemptyset(ref SigSet set);
|
||||
|
||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
||||
{
|
||||
|
@ -3,19 +3,19 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
{
|
||||
unsafe class WindowsSignalHandlerRegistration
|
||||
unsafe partial class WindowsSignalHandlerRegistration
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
|
||||
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
|
||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
|
||||
private static IntPtr _getCurrentThreadIdPtr;
|
||||
|
||||
|
@ -6,7 +6,6 @@ using ARMeilleure.Instructions;
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.State;
|
||||
using ARMeilleure.Translation.PTC;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
@ -44,14 +43,13 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public IMemoryManager Memory { get; }
|
||||
|
||||
public bool HasPtc { get; }
|
||||
|
||||
public EntryTable<uint> CountTable { get; }
|
||||
public AddressTable<ulong> FunctionTable { get; }
|
||||
public TranslatorStubs Stubs { get; }
|
||||
|
||||
public ulong EntryAddress { get; }
|
||||
public bool HighCq { get; }
|
||||
public bool HasPtc { get; }
|
||||
public Aarch32Mode Mode { get; }
|
||||
|
||||
private int _ifThenBlockStateIndex = 0;
|
||||
@ -66,15 +64,16 @@ namespace ARMeilleure.Translation
|
||||
TranslatorStubs stubs,
|
||||
ulong entryAddress,
|
||||
bool highCq,
|
||||
bool hasPtc,
|
||||
Aarch32Mode mode)
|
||||
{
|
||||
HasPtc = Ptc.State != PtcState.Disabled;
|
||||
Memory = memory;
|
||||
CountTable = countTable;
|
||||
FunctionTable = funcTable;
|
||||
Stubs = stubs;
|
||||
EntryAddress = entryAddress;
|
||||
HighCq = highCq;
|
||||
HasPtc = hasPtc;
|
||||
Mode = mode;
|
||||
|
||||
_labels = new Dictionary<ulong, Operand>();
|
||||
|
@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
struct CacheEntry : IComparable<CacheEntry>
|
||||
readonly struct CacheEntry : IComparable<CacheEntry>
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Size { get; }
|
||||
|
@ -6,7 +6,7 @@ namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
class CacheMemoryAllocator
|
||||
{
|
||||
private struct MemoryBlock : IComparable<MemoryBlock>
|
||||
private readonly struct MemoryBlock : IComparable<MemoryBlock>
|
||||
{
|
||||
public int Offset { get; }
|
||||
public int Size { get; }
|
||||
|
@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
static class JitUnwindWindows
|
||||
static partial class JitUnwindWindows
|
||||
{
|
||||
private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
|
||||
|
||||
@ -42,14 +42,15 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
||||
private static unsafe extern bool RtlInstallFunctionTableCallback(
|
||||
[LibraryImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static unsafe partial bool RtlInstallFunctionTableCallback(
|
||||
ulong tableIdentifier,
|
||||
ulong baseAddress,
|
||||
uint length,
|
||||
GetRuntimeFunctionCallback callback,
|
||||
IntPtr context,
|
||||
string outOfProcessCallbackDll);
|
||||
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
||||
|
||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||
|
||||
|
@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
|
||||
|
||||
namespace ARMeilleure.Translation
|
||||
{
|
||||
struct CompilerContext
|
||||
readonly struct CompilerContext
|
||||
{
|
||||
public ControlFlowGraph Cfg { get; }
|
||||
|
||||
|
@ -25,10 +25,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public static Delegate GetDelegate(MethodInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
||||
Type returnType = info.ReturnType;
|
||||
|
@ -35,10 +35,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
string key = GetKey(info);
|
||||
|
||||
@ -52,10 +49,7 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public static int GetDelegateIndex(MethodInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(info);
|
||||
|
||||
string key = GetKey(info);
|
||||
|
||||
|
@ -67,10 +67,7 @@ namespace ARMeilleure.Translation
|
||||
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
|
||||
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
|
||||
}
|
||||
@ -85,10 +82,7 @@ namespace ARMeilleure.Translation
|
||||
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
|
||||
public V GetOrAdd(K start, K end, V value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
|
||||
return node.Value;
|
||||
@ -152,10 +146,7 @@ namespace ARMeilleure.Translation
|
||||
/// <returns>Node reference in the tree</returns>
|
||||
private IntervalTreeNode<K, V> GetNode(K key)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
ArgumentNullException.ThrowIfNull(key);
|
||||
|
||||
IntervalTreeNode<K, V> node = _root;
|
||||
while (node != null)
|
||||
|
10
ARMeilleure/Translation/PTC/IPtcLoadState.cs
Normal file
10
ARMeilleure/Translation/PTC/IPtcLoadState.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public interface IPtcLoadState
|
||||
{
|
||||
event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
void Continue();
|
||||
}
|
||||
}
|
@ -22,12 +22,12 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public static class Ptc
|
||||
class Ptc : IPtcLoadState
|
||||
{
|
||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||
|
||||
private const uint InternalVersion = 3713; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
private const uint InternalVersion = 4159; //! To be incremented manually for each change to the ARMeilleure project.
|
||||
|
||||
private const string ActualDir = "0";
|
||||
private const string BackupDir = "1";
|
||||
@ -35,45 +35,49 @@ namespace ARMeilleure.Translation.PTC
|
||||
private const string TitleIdTextDefault = "0000000000000000";
|
||||
private const string DisplayVersionDefault = "0";
|
||||
|
||||
internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||
internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||
internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||
|
||||
private const byte FillingByte = 0x00;
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
||||
public PtcProfiler Profiler { get; }
|
||||
|
||||
// Carriers.
|
||||
private static MemoryStream _infosStream;
|
||||
private static List<byte[]> _codesList;
|
||||
private static MemoryStream _relocsStream;
|
||||
private static MemoryStream _unwindInfosStream;
|
||||
private MemoryStream _infosStream;
|
||||
private List<byte[]> _codesList;
|
||||
private MemoryStream _relocsStream;
|
||||
private MemoryStream _unwindInfosStream;
|
||||
|
||||
private static readonly ulong _outerHeaderMagic;
|
||||
private static readonly ulong _innerHeaderMagic;
|
||||
private readonly ulong _outerHeaderMagic;
|
||||
private readonly ulong _innerHeaderMagic;
|
||||
|
||||
private static readonly ManualResetEvent _waitEvent;
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private static readonly object _lock;
|
||||
private readonly object _lock;
|
||||
|
||||
private static bool _disposed;
|
||||
private bool _disposed;
|
||||
|
||||
internal static string TitleIdText { get; private set; }
|
||||
internal static string DisplayVersion { get; private set; }
|
||||
public string TitleIdText { get; private set; }
|
||||
public string DisplayVersion { get; private set; }
|
||||
|
||||
private static MemoryManagerMode _memoryMode;
|
||||
private MemoryManagerType _memoryMode;
|
||||
|
||||
internal static string CachePathActual { get; private set; }
|
||||
internal static string CachePathBackup { get; private set; }
|
||||
public string CachePathActual { get; private set; }
|
||||
public string CachePathBackup { get; private set; }
|
||||
|
||||
internal static PtcState State { get; private set; }
|
||||
public PtcState State { get; private set; }
|
||||
|
||||
// Progress reporting helpers.
|
||||
private static volatile int _translateCount;
|
||||
private static volatile int _translateTotalCount;
|
||||
public static event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
private volatile int _translateCount;
|
||||
private volatile int _translateTotalCount;
|
||||
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||
|
||||
static Ptc()
|
||||
public Ptc()
|
||||
{
|
||||
Profiler = new PtcProfiler(this);
|
||||
|
||||
InitializeCarriers();
|
||||
|
||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||
@ -94,12 +98,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
Disable();
|
||||
}
|
||||
|
||||
public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode)
|
||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
|
||||
{
|
||||
Wait();
|
||||
|
||||
PtcProfiler.Wait();
|
||||
PtcProfiler.ClearEntries();
|
||||
Profiler.Wait();
|
||||
Profiler.ClearEntries();
|
||||
|
||||
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
|
||||
|
||||
@ -137,12 +141,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
||||
|
||||
PreLoad();
|
||||
PtcProfiler.PreLoad();
|
||||
Profiler.PreLoad();
|
||||
|
||||
Enable();
|
||||
}
|
||||
|
||||
private static void InitializeCarriers()
|
||||
private void InitializeCarriers()
|
||||
{
|
||||
_infosStream = new MemoryStream();
|
||||
_codesList = new List<byte[]>();
|
||||
@ -150,7 +154,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_unwindInfosStream = new MemoryStream();
|
||||
}
|
||||
|
||||
private static void DisposeCarriers()
|
||||
private void DisposeCarriers()
|
||||
{
|
||||
_infosStream.Dispose();
|
||||
_codesList.Clear();
|
||||
@ -158,12 +162,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
_unwindInfosStream.Dispose();
|
||||
}
|
||||
|
||||
private static bool AreCarriersEmpty()
|
||||
private bool AreCarriersEmpty()
|
||||
{
|
||||
return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
|
||||
}
|
||||
|
||||
private static void ResetCarriersIfNeeded()
|
||||
private void ResetCarriersIfNeeded()
|
||||
{
|
||||
if (AreCarriersEmpty())
|
||||
{
|
||||
@ -175,7 +179,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
InitializeCarriers();
|
||||
}
|
||||
|
||||
private static void PreLoad()
|
||||
private void PreLoad()
|
||||
{
|
||||
string fileNameActual = string.Concat(CachePathActual, ".cache");
|
||||
string fileNameBackup = string.Concat(CachePathBackup, ".cache");
|
||||
@ -199,7 +203,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe bool Load(string fileName, bool isBackup)
|
||||
private unsafe bool Load(string fileName, bool isBackup)
|
||||
{
|
||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||
@ -376,12 +380,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
||||
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||
{
|
||||
compressedStream.SetLength(0L);
|
||||
}
|
||||
|
||||
private static void PreSave()
|
||||
private void PreSave()
|
||||
{
|
||||
_waitEvent.Reset();
|
||||
|
||||
@ -409,7 +413,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
private static unsafe void Save(string fileName)
|
||||
private unsafe void Save(string fileName)
|
||||
{
|
||||
int translatedFuncsCount;
|
||||
|
||||
@ -517,7 +521,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LoadTranslations(Translator translator)
|
||||
public void LoadTranslations(Translator translator)
|
||||
{
|
||||
if (AreCarriersEmpty())
|
||||
{
|
||||
@ -550,7 +554,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
||||
|
||||
if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||
{
|
||||
infoEntry.Stubbed = true;
|
||||
infoEntry.CodeLength = 0;
|
||||
@ -601,38 +605,38 @@ namespace ARMeilleure.Translation.PTC
|
||||
Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
|
||||
}
|
||||
|
||||
private static int GetEntriesCount()
|
||||
private int GetEntriesCount()
|
||||
{
|
||||
return _codesList.Count;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private static void SkipCode(int index, int codeLength)
|
||||
private void SkipCode(int index, int codeLength)
|
||||
{
|
||||
Debug.Assert(_codesList[index].Length == 0);
|
||||
Debug.Assert(codeLength == 0);
|
||||
}
|
||||
|
||||
private static void SkipReloc(int relocEntriesCount)
|
||||
private void SkipReloc(int relocEntriesCount)
|
||||
{
|
||||
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||
private void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
private static byte[] ReadCode(int index, int codeLength)
|
||||
private byte[] ReadCode(int index, int codeLength)
|
||||
{
|
||||
Debug.Assert(_codesList[index].Length == codeLength);
|
||||
|
||||
return _codesList[index];
|
||||
}
|
||||
|
||||
private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
|
||||
private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
|
||||
{
|
||||
RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
|
||||
|
||||
@ -648,7 +652,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
return relocEntries;
|
||||
}
|
||||
|
||||
private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
||||
private void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
||||
{
|
||||
callCounter = null;
|
||||
|
||||
@ -702,7 +706,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
|
||||
private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
@ -723,7 +727,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
return new UnwindInfo(pushEntries, prologueSize);
|
||||
}
|
||||
|
||||
private static TranslatedFunction FastTranslate(
|
||||
private TranslatedFunction FastTranslate(
|
||||
byte[] code,
|
||||
Counter<uint> callCounter,
|
||||
ulong guestSize,
|
||||
@ -736,19 +740,19 @@ namespace ARMeilleure.Translation.PTC
|
||||
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
|
||||
}
|
||||
|
||||
private static void UpdateInfo(InfoEntry infoEntry)
|
||||
private void UpdateInfo(InfoEntry infoEntry)
|
||||
{
|
||||
_infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
|
||||
|
||||
SerializeStructure(_infosStream, infoEntry);
|
||||
}
|
||||
|
||||
private static void StubCode(int index)
|
||||
private void StubCode(int index)
|
||||
{
|
||||
_codesList[index] = Array.Empty<byte>();
|
||||
}
|
||||
|
||||
private static void StubReloc(int relocEntriesCount)
|
||||
private void StubReloc(int relocEntriesCount)
|
||||
{
|
||||
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
|
||||
{
|
||||
@ -756,7 +760,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||
private void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||
{
|
||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||
|
||||
@ -766,9 +770,9 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MakeAndSaveTranslations(Translator translator)
|
||||
public void MakeAndSaveTranslations(Translator translator)
|
||||
{
|
||||
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||
|
||||
_translateCount = 0;
|
||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||
@ -811,7 +815,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
ulong address = item.address;
|
||||
|
||||
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
||||
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
|
||||
|
||||
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
||||
|
||||
@ -861,7 +865,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
preSaveThread.Start();
|
||||
}
|
||||
|
||||
private static void ReportProgress(object state)
|
||||
private void ReportProgress(object state)
|
||||
{
|
||||
const int refreshRate = 50; // ms.
|
||||
|
||||
@ -882,12 +886,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
while (!endEvent.WaitOne(refreshRate));
|
||||
}
|
||||
|
||||
internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
|
||||
public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
|
||||
{
|
||||
return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
|
||||
}
|
||||
|
||||
internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
|
||||
public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@ -936,12 +940,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteCode(ReadOnlySpan<byte> code)
|
||||
private void WriteCode(ReadOnlySpan<byte> code)
|
||||
{
|
||||
_codesList.Add(code.ToArray());
|
||||
}
|
||||
|
||||
internal static bool GetEndianness()
|
||||
public static bool GetEndianness()
|
||||
{
|
||||
return BitConverter.IsLittleEndian;
|
||||
}
|
||||
@ -955,7 +959,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
(uint)HardwareCapabilities.FeatureInfo7Ecx);
|
||||
}
|
||||
|
||||
private static byte GetMemoryManagerMode()
|
||||
private byte GetMemoryManagerMode()
|
||||
{
|
||||
return (byte)_memoryMode;
|
||||
}
|
||||
@ -1050,12 +1054,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
public int RelocEntriesCount;
|
||||
}
|
||||
|
||||
private static void Enable()
|
||||
private void Enable()
|
||||
{
|
||||
State = PtcState.Enabled;
|
||||
}
|
||||
|
||||
public static void Continue()
|
||||
public void Continue()
|
||||
{
|
||||
if (State == PtcState.Enabled)
|
||||
{
|
||||
@ -1063,7 +1067,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
public static void Close()
|
||||
public void Close()
|
||||
{
|
||||
if (State == PtcState.Enabled ||
|
||||
State == PtcState.Continuing)
|
||||
@ -1072,17 +1076,17 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Disable()
|
||||
public void Disable()
|
||||
{
|
||||
State = PtcState.Disabled;
|
||||
}
|
||||
|
||||
private static void Wait()
|
||||
private void Wait()
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
|
@ -16,7 +16,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
|
||||
|
||||
namespace ARMeilleure.Translation.PTC
|
||||
{
|
||||
public static class PtcProfiler
|
||||
class PtcProfiler
|
||||
{
|
||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||
|
||||
@ -26,27 +26,31 @@ namespace ARMeilleure.Translation.PTC
|
||||
|
||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||
|
||||
private static readonly System.Timers.Timer _timer;
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
private static readonly ulong _outerHeaderMagic;
|
||||
private readonly System.Timers.Timer _timer;
|
||||
|
||||
private static readonly ManualResetEvent _waitEvent;
|
||||
private readonly ulong _outerHeaderMagic;
|
||||
|
||||
private static readonly object _lock;
|
||||
private readonly ManualResetEvent _waitEvent;
|
||||
|
||||
private static bool _disposed;
|
||||
private readonly object _lock;
|
||||
|
||||
private static Hash128 _lastHash;
|
||||
private bool _disposed;
|
||||
|
||||
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
||||
private Hash128 _lastHash;
|
||||
|
||||
internal static bool Enabled { get; private set; }
|
||||
public Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
||||
|
||||
public static ulong StaticCodeStart { internal get; set; }
|
||||
public static ulong StaticCodeSize { internal get; set; }
|
||||
public bool Enabled { get; private set; }
|
||||
|
||||
static PtcProfiler()
|
||||
public ulong StaticCodeStart { get; set; }
|
||||
public ulong StaticCodeSize { get; set; }
|
||||
|
||||
public PtcProfiler(Ptc ptc)
|
||||
{
|
||||
_ptc = ptc;
|
||||
|
||||
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
||||
_timer.Elapsed += PreSave;
|
||||
|
||||
@ -63,7 +67,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
Enabled = false;
|
||||
}
|
||||
|
||||
internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
{
|
||||
@ -76,7 +80,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||
{
|
||||
if (IsAddressInStaticCodeRange(address))
|
||||
{
|
||||
@ -91,12 +95,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsAddressInStaticCodeRange(ulong address)
|
||||
public bool IsAddressInStaticCodeRange(ulong address)
|
||||
{
|
||||
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
|
||||
}
|
||||
|
||||
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||
{
|
||||
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
||||
|
||||
@ -111,18 +115,18 @@ namespace ARMeilleure.Translation.PTC
|
||||
return profiledFuncsToTranslate;
|
||||
}
|
||||
|
||||
internal static void ClearEntries()
|
||||
public void ClearEntries()
|
||||
{
|
||||
ProfiledFuncs.Clear();
|
||||
ProfiledFuncs.TrimExcess();
|
||||
}
|
||||
|
||||
internal static void PreLoad()
|
||||
public void PreLoad()
|
||||
{
|
||||
_lastHash = default;
|
||||
|
||||
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
|
||||
string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
|
||||
|
||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
|
||||
@ -143,7 +147,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static bool Load(string fileName, bool isBackup)
|
||||
private bool Load(string fileName, bool isBackup)
|
||||
{
|
||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||
@ -228,22 +232,22 @@ namespace ARMeilleure.Translation.PTC
|
||||
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
|
||||
}
|
||||
|
||||
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||
private ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||
{
|
||||
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
||||
}
|
||||
|
||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
||||
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||
{
|
||||
compressedStream.SetLength(0L);
|
||||
}
|
||||
|
||||
private static void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
||||
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
_waitEvent.Reset();
|
||||
|
||||
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
|
||||
string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
|
||||
string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
|
||||
|
||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||
|
||||
@ -257,7 +261,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
private static void Save(string fileName)
|
||||
private void Save(string fileName)
|
||||
{
|
||||
int profiledFuncsCount;
|
||||
|
||||
@ -329,7 +333,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||
private void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||
{
|
||||
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
|
||||
}
|
||||
@ -361,7 +365,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
||||
internal struct FuncProfile
|
||||
public struct FuncProfile
|
||||
{
|
||||
public ExecutionMode Mode;
|
||||
public bool HighCq;
|
||||
@ -373,10 +377,10 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Start()
|
||||
public void Start()
|
||||
{
|
||||
if (Ptc.State == PtcState.Enabled ||
|
||||
Ptc.State == PtcState.Continuing)
|
||||
if (_ptc.State == PtcState.Enabled ||
|
||||
_ptc.State == PtcState.Continuing)
|
||||
{
|
||||
Enabled = true;
|
||||
|
||||
@ -384,7 +388,7 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
public void Stop()
|
||||
{
|
||||
Enabled = false;
|
||||
|
||||
@ -394,12 +398,12 @@ namespace ARMeilleure.Translation.PTC
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Wait()
|
||||
public void Wait()
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
}
|
||||
|
||||
public static void Dispose()
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
|
@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
|
||||
private const int RegsCount = 32;
|
||||
private const int RegsMask = RegsCount - 1;
|
||||
|
||||
private struct RegisterMask : IEquatable<RegisterMask>
|
||||
private readonly struct RegisterMask : IEquatable<RegisterMask>
|
||||
{
|
||||
public long IntMask => Mask.GetElement(0);
|
||||
public long VecMask => Mask.GetElement(1);
|
||||
|
@ -140,7 +140,7 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
}
|
||||
|
||||
Array.Clear(localDefs, 0, localDefs.Length);
|
||||
Array.Clear(localDefs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,8 @@ namespace ARMeilleure.Translation
|
||||
private readonly IJitMemoryAllocator _allocator;
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
internal AddressTable<ulong> FunctionTable { get; }
|
||||
internal EntryTable<uint> CountTable { get; }
|
||||
@ -63,6 +65,8 @@ namespace ARMeilleure.Translation
|
||||
|
||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||
|
||||
_ptc = new Ptc();
|
||||
|
||||
Queue = new TranslatorQueue();
|
||||
|
||||
JitCache.Initialize(allocator);
|
||||
@ -80,22 +84,37 @@ namespace ARMeilleure.Translation
|
||||
}
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
{
|
||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
||||
return _ptc;
|
||||
}
|
||||
|
||||
public void PrepareCodeRange(ulong address, ulong size)
|
||||
{
|
||||
if (_ptc.Profiler.StaticCodeSize == 0)
|
||||
{
|
||||
_ptc.Profiler.StaticCodeStart = address;
|
||||
_ptc.Profiler.StaticCodeSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(State.ExecutionContext context, ulong address)
|
||||
{
|
||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||
{
|
||||
IsReadyForTranslation.WaitOne();
|
||||
|
||||
if (Ptc.State == PtcState.Enabled)
|
||||
if (_ptc.State == PtcState.Enabled)
|
||||
{
|
||||
Debug.Assert(Functions.Count == 0);
|
||||
Ptc.LoadTranslations(this);
|
||||
Ptc.MakeAndSaveTranslations(this);
|
||||
_ptc.LoadTranslations(this);
|
||||
_ptc.MakeAndSaveTranslations(this);
|
||||
}
|
||||
|
||||
PtcProfiler.Start();
|
||||
_ptc.Profiler.Start();
|
||||
|
||||
Ptc.Disable();
|
||||
_ptc.Disable();
|
||||
|
||||
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
|
||||
// etc). All threads are normal priority except from the last, which just fills as much of the last core
|
||||
@ -148,6 +167,12 @@ namespace ARMeilleure.Translation
|
||||
Stubs.Dispose();
|
||||
FunctionTable.Dispose();
|
||||
CountTable.Dispose();
|
||||
|
||||
_ptc.Close();
|
||||
_ptc.Profiler.Stop();
|
||||
|
||||
_ptc.Dispose();
|
||||
_ptc.Profiler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@ -189,9 +214,9 @@ namespace ARMeilleure.Translation
|
||||
func = oldFunc;
|
||||
}
|
||||
|
||||
if (PtcProfiler.Enabled)
|
||||
if (_ptc.Profiler.Enabled)
|
||||
{
|
||||
PtcProfiler.AddEntry(address, mode, highCq: false);
|
||||
_ptc.Profiler.AddEntry(address, mode, highCq: false);
|
||||
}
|
||||
|
||||
RegisterFunction(address, func);
|
||||
@ -217,6 +242,7 @@ namespace ARMeilleure.Translation
|
||||
Stubs,
|
||||
address,
|
||||
highCq,
|
||||
_ptc.State != PtcState.Disabled,
|
||||
mode: Aarch32Mode.User);
|
||||
|
||||
Logger.StartPass(PassName.Decoding);
|
||||
@ -262,7 +288,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
||||
|
||||
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||
}
|
||||
|
||||
GuestFunction func = compiledFunc.Map<GuestFunction>();
|
||||
@ -284,16 +310,16 @@ namespace ARMeilleure.Translation
|
||||
return func;
|
||||
});
|
||||
|
||||
if (PtcProfiler.Enabled)
|
||||
if (_ptc.Profiler.Enabled)
|
||||
{
|
||||
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
||||
_ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
||||
}
|
||||
|
||||
RegisterFunction(request.Address, func);
|
||||
}
|
||||
}
|
||||
|
||||
private struct Range
|
||||
private readonly struct Range
|
||||
{
|
||||
public ulong Start { get; }
|
||||
public ulong End { get; }
|
||||
@ -455,13 +481,16 @@ namespace ARMeilleure.Translation
|
||||
|
||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
||||
{
|
||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
||||
ClearRejitQueue(allowRequeue: true);
|
||||
|
||||
ulong[] overlapAddresses = Array.Empty<ulong>();
|
||||
|
||||
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
|
||||
|
||||
if (overlapsCount != 0)
|
||||
{
|
||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
||||
ClearRejitQueue(allowRequeue: true);
|
||||
}
|
||||
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
ulong overlapAddress = overlapAddresses[index];
|
||||
|
@ -30,10 +30,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return _dispatchStub.Value;
|
||||
}
|
||||
@ -47,10 +44,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return _slowDispatchStub.Value;
|
||||
}
|
||||
@ -64,10 +58,7 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(null);
|
||||
}
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
||||
return _dispatchLoop.Value;
|
||||
}
|
||||
@ -81,7 +72,9 @@ namespace ARMeilleure.Translation
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(Translator translator)
|
||||
{
|
||||
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
|
||||
ArgumentNullException.ThrowIfNull(translator);
|
||||
|
||||
_translator = translator;
|
||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||
}
|
||||
|
54
Directory.Packages.props
Normal file
54
Directory.Packages.props
Normal file
@ -0,0 +1,54 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<PackageVersion Include="Crc32.NET" Version="1.2.0" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||
<PackageVersion Include="DynamicData" Version="7.12.11" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||
<PackageVersion Include="LibHac" Version="0.17.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
|
||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.24.2-build21" />
|
||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||
<PackageVersion Include="SharpZipLib" Version="1.4.1" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
|
||||
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||
<PackageVersion Include="SPB" Version="0.0.4-build28" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
|
||||
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Management" Version="7.0.0" />
|
||||
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
|
||||
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
|
||||
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
16
README.md
16
README.md
@ -21,6 +21,10 @@
|
||||
<img src="https://github.com/Ryujinx/Ryujinx/actions/workflows/release.yml/badge.svg"
|
||||
alt="">
|
||||
</a>
|
||||
<a href="https://crwd.in/ryujinx">
|
||||
<img src="https://badges.crowdin.net/ryujinx/localized.svg"
|
||||
alt="">
|
||||
</a>
|
||||
<a href="https://discord.com/invite/VkQYXAZ">
|
||||
<img src="https://img.shields.io/discord/410208534861447168?color=5865F2&label=Ryujinx&logo=discord&logoColor=white"
|
||||
alt="Discord">
|
||||
@ -36,18 +40,20 @@
|
||||
|
||||
## Compatibility
|
||||
|
||||
As of October 2022, Ryujinx has been tested on approximately 3,600 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable. You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues).
|
||||
Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable.
|
||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||
|
||||
## Usage
|
||||
|
||||
To run this emulator, your PC must be equipped with at least 8GB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
||||
To run this emulator, your PC must be equipped with at least 8GiB of RAM; failing to meet this requirement may result in a poor gameplay experience or unexpected crashes.
|
||||
|
||||
See our [Setup & Configuration Guide](https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide) on how to set up the emulator.
|
||||
|
||||
For our Local Wireless and LAN builds, see our [Multiplayer: Local Play/Local Wireless Guide
|
||||
](https://github.com/Ryujinx/Ryujinx/wiki/Multiplayer-(LDN-Local-Wireless)-Guide).
|
||||
|
||||
Avalonia UI comes with translations for various languages. See [Crowdin](https://crwd.in/ryujinx) for more information.
|
||||
|
||||
## Latest build
|
||||
|
||||
These builds are compiled automatically for each commit on the master branch. While we strive to ensure optimal stability and performance prior to pushing an update, our automated builds **may be unstable or completely broken.**
|
||||
@ -62,7 +68,7 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
|
||||
If you wish to build the emulator yourself, follow these steps:
|
||||
|
||||
### Step 1
|
||||
Install the X64 version of [.NET 6.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/6.0).
|
||||
Install the X64 version of [.NET 7.0 (or higher) SDK](https://dotnet.microsoft.com/download/dotnet/7.0).
|
||||
|
||||
### Step 2
|
||||
Either use `git clone https://github.com/Ryujinx/Ryujinx` on the command line to clone the repository or use Code --> Download zip button to get the files.
|
||||
@ -90,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
||||
|
||||
- **GPU**
|
||||
|
||||
The GPU emulator emulates the Switch's Maxwell GPU using the OpenGL API (version 4.5 minimum) through a custom build of OpenTK. There are currently four graphics enhancements available to the end user in Ryujinx: disk shader caching, resolution scaling, aspect ratio adjustment and anisotropic filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||
|
||||
- **Input**
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.2" />
|
||||
<PackageReference Include="OpenTK.OpenAL" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using System;
|
||||
@ -112,6 +113,9 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
if (device == 0)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"SDL2 open audio device initialization failed with error \"{SDL_GetError()}\"");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -119,6 +123,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, "SDL2 open audio device is not valid");
|
||||
SDL_CloseAudioDevice(device);
|
||||
|
||||
return 0;
|
||||
|
178
Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs
Normal file
178
Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public static partial class SoundIo
|
||||
{
|
||||
private const string LibraryName = "libsoundio";
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public unsafe delegate void OnDeviceChangeNativeDelegate(IntPtr ctx);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public unsafe delegate void OnBackendDisconnectedDelegate(IntPtr ctx, SoundIoError err);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public unsafe delegate void OnEventsSignalDelegate(IntPtr ctx);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public unsafe delegate void EmitRtPrioWarningDelegate();
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public unsafe delegate void JackCallbackDelegate(IntPtr msg);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SoundIoStruct
|
||||
{
|
||||
public IntPtr UserData;
|
||||
public IntPtr OnDeviceChange;
|
||||
public IntPtr OnBackendDisconnected;
|
||||
public IntPtr OnEventsSignal;
|
||||
public SoundIoBackend CurrentBackend;
|
||||
public IntPtr ApplicationName;
|
||||
public IntPtr EmitRtPrioWarning;
|
||||
public IntPtr JackInfoCallback;
|
||||
public IntPtr JackErrorCallback;
|
||||
}
|
||||
|
||||
public struct SoundIoChannelLayout
|
||||
{
|
||||
public IntPtr Name;
|
||||
public int ChannelCount;
|
||||
public Array24<SoundIoChannelId> Channels;
|
||||
|
||||
public static IntPtr GetDefault(int channelCount)
|
||||
{
|
||||
return soundio_channel_layout_get_default(channelCount);
|
||||
}
|
||||
|
||||
public static unsafe SoundIoChannelLayout GetDefaultValue(int channelCount)
|
||||
{
|
||||
return Unsafe.AsRef<SoundIoChannelLayout>((SoundIoChannelLayout*)GetDefault(channelCount));
|
||||
}
|
||||
}
|
||||
|
||||
public struct SoundIoSampleRateRange
|
||||
{
|
||||
public int Min;
|
||||
public int Max;
|
||||
}
|
||||
|
||||
public struct SoundIoDevice
|
||||
{
|
||||
public IntPtr SoundIo;
|
||||
public IntPtr Id;
|
||||
public IntPtr Name;
|
||||
public SoundIoDeviceAim Aim;
|
||||
public IntPtr Layouts;
|
||||
public int LayoutCount;
|
||||
public SoundIoChannelLayout CurrentLayout;
|
||||
public IntPtr Formats;
|
||||
public int FormatCount;
|
||||
public SoundIoFormat CurrentFormat;
|
||||
public IntPtr SampleRates;
|
||||
public int SampleRateCount;
|
||||
public int SampleRateCurrent;
|
||||
public double SoftwareLatencyMin;
|
||||
public double SoftwareLatencyMax;
|
||||
public double SoftwareLatencyCurrent;
|
||||
public bool IsRaw;
|
||||
public int RefCount;
|
||||
public SoundIoError ProbeError;
|
||||
}
|
||||
|
||||
public struct SoundIoOutStream
|
||||
{
|
||||
public IntPtr Device;
|
||||
public SoundIoFormat Format;
|
||||
public int SampleRate;
|
||||
public SoundIoChannelLayout Layout;
|
||||
public double SoftwareLatency;
|
||||
public float Volume;
|
||||
public IntPtr UserData;
|
||||
public IntPtr WriteCallback;
|
||||
public IntPtr UnderflowCallback;
|
||||
public IntPtr ErrorCallback;
|
||||
public IntPtr Name;
|
||||
public bool NonTerminalHint;
|
||||
public int BytesPerFrame;
|
||||
public int BytesPerSample;
|
||||
public SoundIoError LayoutError;
|
||||
}
|
||||
|
||||
public struct SoundIoChannelArea
|
||||
{
|
||||
public IntPtr Pointer;
|
||||
public int Step;
|
||||
}
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial IntPtr soundio_create();
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_connect(IntPtr ctx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial void soundio_disconnect(IntPtr ctx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial void soundio_flush_events(IntPtr ctx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial int soundio_output_device_count(IntPtr ctx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial int soundio_default_output_device_index(IntPtr ctx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial IntPtr soundio_get_output_device(IntPtr ctx, int index);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool soundio_device_supports_format(IntPtr devCtx, SoundIoFormat format);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool soundio_device_supports_layout(IntPtr devCtx, IntPtr layout);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool soundio_device_supports_sample_rate(IntPtr devCtx, int sampleRate);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial IntPtr soundio_outstream_create(IntPtr devCtx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_outstream_open(IntPtr outStreamCtx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_outstream_start(IntPtr outStreamCtx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_outstream_begin_write(IntPtr outStreamCtx, IntPtr areas, IntPtr frameCount);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_outstream_end_write(IntPtr outStreamCtx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_outstream_pause(IntPtr devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial SoundIoError soundio_outstream_set_volume(IntPtr devCtx, double volume);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial void soundio_outstream_destroy(IntPtr streamCtx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial void soundio_destroy(IntPtr ctx);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial IntPtr soundio_channel_layout_get_default(int channelCount);
|
||||
|
||||
[LibraryImport(LibraryName)]
|
||||
public static partial IntPtr soundio_strerror(SoundIoError err);
|
||||
}
|
||||
}
|
13
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs
Normal file
13
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public enum SoundIoBackend : int
|
||||
{
|
||||
None = 0,
|
||||
Jack = 1,
|
||||
PulseAudio = 2,
|
||||
Alsa = 3,
|
||||
CoreAudio = 4,
|
||||
Wasapi = 5,
|
||||
Dummy = 6
|
||||
}
|
||||
}
|
75
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs
Normal file
75
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs
Normal file
@ -0,0 +1,75 @@
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public enum SoundIoChannelId
|
||||
{
|
||||
Invalid = 0,
|
||||
FrontLeft = 1,
|
||||
FrontRight = 2,
|
||||
FrontCenter = 3,
|
||||
Lfe = 4,
|
||||
BackLeft = 5,
|
||||
BackRight = 6,
|
||||
FrontLeftCenter = 7,
|
||||
FrontRightCenter = 8,
|
||||
BackCenter = 9,
|
||||
SideLeft = 10,
|
||||
SideRight = 11,
|
||||
TopCenter = 12,
|
||||
TopFrontLeft = 13,
|
||||
TopFrontCenter = 14,
|
||||
TopFrontRight = 15,
|
||||
TopBackLeft = 16,
|
||||
TopBackCenter = 17,
|
||||
TopBackRight = 18,
|
||||
BackLeftCenter = 19,
|
||||
BackRightCenter = 20,
|
||||
FrontLeftWide = 21,
|
||||
FrontRightWide = 22,
|
||||
FrontLeftHigh = 23,
|
||||
FrontCenterHigh = 24,
|
||||
FrontRightHigh = 25,
|
||||
TopFrontLeftCenter = 26,
|
||||
TopFrontRightCenter = 27,
|
||||
TopSideLeft = 28,
|
||||
TopSideRight = 29,
|
||||
LeftLfe = 30,
|
||||
RightLfe = 31,
|
||||
Lfe2 = 32,
|
||||
BottomCenter = 33,
|
||||
BottomLeftCenter = 34,
|
||||
BottomRightCenter = 35,
|
||||
MsMid = 36,
|
||||
MsSide = 37,
|
||||
AmbisonicW = 38,
|
||||
AmbisonicX = 39,
|
||||
AmbisonicY = 40,
|
||||
AmbisonicZ = 41,
|
||||
XyX = 42,
|
||||
XyY = 43,
|
||||
HeadphonesLeft = 44,
|
||||
HeadphonesRight = 45,
|
||||
ClickTrack = 46,
|
||||
ForeignLanguage = 47,
|
||||
HearingImpaired = 48,
|
||||
Narration = 49,
|
||||
Haptic = 50,
|
||||
DialogCentricMix = 51,
|
||||
Aux = 52,
|
||||
Aux0 = 53,
|
||||
Aux1 = 54,
|
||||
Aux2 = 55,
|
||||
Aux3 = 56,
|
||||
Aux4 = 57,
|
||||
Aux5 = 58,
|
||||
Aux6 = 59,
|
||||
Aux7 = 60,
|
||||
Aux8 = 61,
|
||||
Aux9 = 62,
|
||||
Aux10 = 63,
|
||||
Aux11 = 64,
|
||||
Aux12 = 65,
|
||||
Aux13 = 66,
|
||||
Aux14 = 67,
|
||||
Aux15 = 68,
|
||||
}
|
||||
}
|
107
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs
Normal file
107
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs
Normal file
@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public class SoundIoContext : IDisposable
|
||||
{
|
||||
private IntPtr _context;
|
||||
private Action<SoundIoError> _onBackendDisconnect;
|
||||
private OnBackendDisconnectedDelegate _onBackendDisconnectNative;
|
||||
|
||||
public IntPtr Context => _context;
|
||||
|
||||
internal SoundIoContext(IntPtr context)
|
||||
{
|
||||
_context = context;
|
||||
_onBackendDisconnect = null;
|
||||
_onBackendDisconnectNative = null;
|
||||
}
|
||||
|
||||
public SoundIoError Connect() => soundio_connect(_context);
|
||||
public void Disconnect() => soundio_disconnect(_context);
|
||||
|
||||
public void FlushEvents() => soundio_flush_events(_context);
|
||||
|
||||
public int OutputDeviceCount => soundio_output_device_count(_context);
|
||||
|
||||
public int DefaultOutputDeviceIndex => soundio_default_output_device_index(_context);
|
||||
|
||||
public Action<SoundIoError> OnBackendDisconnect
|
||||
{
|
||||
get { return _onBackendDisconnect; }
|
||||
set
|
||||
{
|
||||
_onBackendDisconnect = value;
|
||||
|
||||
if (_onBackendDisconnect == null)
|
||||
{
|
||||
_onBackendDisconnectNative = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_onBackendDisconnectNative = (ctx, err) => _onBackendDisconnect(err);
|
||||
}
|
||||
|
||||
GetContext().OnBackendDisconnected = Marshal.GetFunctionPointerForDelegate(_onBackendDisconnectNative);
|
||||
}
|
||||
}
|
||||
|
||||
private ref SoundIoStruct GetContext()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return ref Unsafe.AsRef<SoundIoStruct>((SoundIoStruct*)_context);
|
||||
}
|
||||
}
|
||||
|
||||
public SoundIoDeviceContext GetOutputDevice(int index)
|
||||
{
|
||||
IntPtr deviceContext = soundio_get_output_device(_context, index);
|
||||
|
||||
if (deviceContext == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SoundIoDeviceContext(deviceContext);
|
||||
}
|
||||
|
||||
public static SoundIoContext Create()
|
||||
{
|
||||
IntPtr context = soundio_create();
|
||||
|
||||
if (context == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SoundIoContext(context);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
IntPtr currentContext = Interlocked.Exchange(ref _context, IntPtr.Zero);
|
||||
|
||||
if (currentContext != IntPtr.Zero)
|
||||
{
|
||||
soundio_destroy(currentContext);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~SoundIoContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public enum SoundIoDeviceAim
|
||||
{
|
||||
SoundIoDeviceAimInput = 0,
|
||||
SoundIoDeviceAimOutput = 1
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public class SoundIoDeviceContext
|
||||
{
|
||||
private readonly IntPtr _context;
|
||||
|
||||
public IntPtr Context => _context;
|
||||
|
||||
internal SoundIoDeviceContext(IntPtr context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private ref SoundIoDevice GetDeviceContext()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return ref Unsafe.AsRef<SoundIoDevice>((SoundIoDevice*)_context);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRaw => GetDeviceContext().IsRaw;
|
||||
|
||||
public string Id => Marshal.PtrToStringAnsi(GetDeviceContext().Id);
|
||||
|
||||
public bool SupportsSampleRate(int sampleRate) => soundio_device_supports_sample_rate(_context, sampleRate);
|
||||
|
||||
public bool SupportsFormat(SoundIoFormat format) => soundio_device_supports_format(_context, format);
|
||||
|
||||
public bool SupportsChannelCount(int channelCount) => soundio_device_supports_layout(_context, SoundIoChannelLayout.GetDefault(channelCount));
|
||||
|
||||
public SoundIoOutStreamContext CreateOutStream()
|
||||
{
|
||||
IntPtr context = soundio_outstream_create(_context);
|
||||
|
||||
if (context == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SoundIoOutStreamContext(context);
|
||||
}
|
||||
}
|
||||
}
|
22
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs
Normal file
22
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs
Normal file
@ -0,0 +1,22 @@
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public enum SoundIoError
|
||||
{
|
||||
None = 0,
|
||||
NoMem = 1,
|
||||
InitAudioBackend = 2,
|
||||
SystemResources = 3,
|
||||
OpeningDevice = 4,
|
||||
NoSuchDevice = 5,
|
||||
Invalid = 6,
|
||||
BackendUnavailable = 7,
|
||||
Streaming = 8,
|
||||
IncompatibleDevice = 9,
|
||||
NoSuchClient = 10,
|
||||
IncompatibleBackend = 11,
|
||||
BackendDisconnected = 12,
|
||||
Interrupted = 13,
|
||||
Underflow = 14,
|
||||
EncodingString = 15,
|
||||
}
|
||||
}
|
11
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs
Normal file
11
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
internal class SoundIoException : Exception
|
||||
{
|
||||
internal SoundIoException(SoundIoError error) : base(Marshal.PtrToStringAnsi(soundio_strerror(error))) { }
|
||||
}
|
||||
}
|
25
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs
Normal file
25
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public enum SoundIoFormat
|
||||
{
|
||||
Invalid = 0,
|
||||
S8 = 1,
|
||||
U8 = 2,
|
||||
S16LE = 3,
|
||||
S16BE = 4,
|
||||
U16LE = 5,
|
||||
U16BE = 6,
|
||||
S24LE = 7,
|
||||
S24BE = 8,
|
||||
U24LE = 9,
|
||||
U24BE = 10,
|
||||
S32LE = 11,
|
||||
S32BE = 12,
|
||||
U32LE = 13,
|
||||
U32BE = 14,
|
||||
Float32LE = 15,
|
||||
Float32BE = 16,
|
||||
Float64LE = 17,
|
||||
Float64BE = 18,
|
||||
}
|
||||
}
|
164
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs
Normal file
164
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs
Normal file
@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
{
|
||||
public class SoundIoOutStreamContext : IDisposable
|
||||
{
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private unsafe delegate void WriteCallbackDelegate(IntPtr ctx, int frameCountMin, int frameCountMax);
|
||||
|
||||
private IntPtr _context;
|
||||
private IntPtr _nameStored;
|
||||
private Action<int, int> _writeCallback;
|
||||
private WriteCallbackDelegate _writeCallbackNative;
|
||||
|
||||
public IntPtr Context => _context;
|
||||
|
||||
internal SoundIoOutStreamContext(IntPtr context)
|
||||
{
|
||||
_context = context;
|
||||
_nameStored = IntPtr.Zero;
|
||||
_writeCallback = null;
|
||||
_writeCallbackNative = null;
|
||||
}
|
||||
|
||||
private ref SoundIoOutStream GetOutContext()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
return ref Unsafe.AsRef<SoundIoOutStream>((SoundIoOutStream*)_context);
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get => Marshal.PtrToStringAnsi(GetOutContext().Name);
|
||||
set
|
||||
{
|
||||
var context = GetOutContext();
|
||||
|
||||
if (_nameStored != IntPtr.Zero && context.Name == _nameStored)
|
||||
{
|
||||
Marshal.FreeHGlobal(_nameStored);
|
||||
}
|
||||
|
||||
_nameStored = Marshal.StringToHGlobalAnsi(value);
|
||||
GetOutContext().Name = _nameStored;
|
||||
}
|
||||
}
|
||||
|
||||
public SoundIoChannelLayout Layout
|
||||
{
|
||||
get => GetOutContext().Layout;
|
||||
set => GetOutContext().Layout = value;
|
||||
}
|
||||
|
||||
public SoundIoFormat Format
|
||||
{
|
||||
get => GetOutContext().Format;
|
||||
set => GetOutContext().Format = value;
|
||||
}
|
||||
|
||||
public int SampleRate
|
||||
{
|
||||
get => GetOutContext().SampleRate;
|
||||
set => GetOutContext().SampleRate = value;
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get => GetOutContext().Volume;
|
||||
set => GetOutContext().Volume = value;
|
||||
}
|
||||
|
||||
public int BytesPerFrame
|
||||
{
|
||||
get => GetOutContext().BytesPerFrame;
|
||||
set => GetOutContext().BytesPerFrame = value;
|
||||
}
|
||||
|
||||
public int BytesPerSample
|
||||
{
|
||||
get => GetOutContext().BytesPerSample;
|
||||
set => GetOutContext().BytesPerSample = value;
|
||||
}
|
||||
|
||||
public Action<int, int> WriteCallback
|
||||
{
|
||||
get { return _writeCallback; }
|
||||
set
|
||||
{
|
||||
_writeCallback = value;
|
||||
|
||||
if (_writeCallback == null)
|
||||
{
|
||||
_writeCallbackNative = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
_writeCallbackNative = (ctx, frameCountMin, frameCountMax) => _writeCallback(frameCountMin, frameCountMax);
|
||||
}
|
||||
|
||||
GetOutContext().WriteCallback = Marshal.GetFunctionPointerForDelegate(_writeCallbackNative);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CheckError(SoundIoError error)
|
||||
{
|
||||
if (error != SoundIoError.None)
|
||||
{
|
||||
throw new SoundIoException(error);
|
||||
}
|
||||
}
|
||||
|
||||
public void Open() => CheckError(soundio_outstream_open(_context));
|
||||
|
||||
public void Start() => CheckError(soundio_outstream_start(_context));
|
||||
|
||||
public void Pause(bool pause) => CheckError(soundio_outstream_pause(_context, pause));
|
||||
|
||||
public void SetVolume(double volume) => CheckError(soundio_outstream_set_volume(_context, volume));
|
||||
|
||||
public Span<SoundIoChannelArea> BeginWrite(ref int frameCount)
|
||||
{
|
||||
IntPtr arenas = default;
|
||||
int nativeFrameCount = frameCount;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var frameCountPtr = &nativeFrameCount;
|
||||
var arenasPtr = &arenas;
|
||||
CheckError(soundio_outstream_begin_write(_context, (IntPtr)arenasPtr, (IntPtr)frameCountPtr));
|
||||
|
||||
frameCount = *frameCountPtr;
|
||||
|
||||
return new Span<SoundIoChannelArea>((void*)arenas, Layout.ChannelCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndWrite() => CheckError(soundio_outstream_end_write(_context));
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (_context != IntPtr.Zero)
|
||||
{
|
||||
soundio_outstream_destroy(_context);
|
||||
_context = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~SoundIoOutStreamContext()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public static class MarshalEx
|
||||
{
|
||||
public static double ReadDouble(IntPtr handle, int offset = 0)
|
||||
{
|
||||
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(handle, offset));
|
||||
}
|
||||
|
||||
public static void WriteDouble(IntPtr handle, double value)
|
||||
{
|
||||
WriteDouble(handle, 0, value);
|
||||
}
|
||||
|
||||
public static void WriteDouble(IntPtr handle, int offset, double value)
|
||||
{
|
||||
Marshal.WriteInt64(handle, offset, BitConverter.DoubleToInt64Bits(value));
|
||||
}
|
||||
|
||||
public static float ReadFloat(IntPtr handle, int offset = 0)
|
||||
{
|
||||
return BitConverter.Int32BitsToSingle(Marshal.ReadInt32(handle, offset));
|
||||
}
|
||||
|
||||
public static void WriteFloat(IntPtr handle, float value)
|
||||
{
|
||||
WriteFloat(handle, 0, value);
|
||||
}
|
||||
|
||||
public static void WriteFloat(IntPtr handle, int offset, float value)
|
||||
{
|
||||
Marshal.WriteInt32(handle, offset, BitConverter.SingleToInt32Bits(value));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,386 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public class SoundIO : IDisposable
|
||||
{
|
||||
Pointer<SoundIo> handle;
|
||||
|
||||
public SoundIO()
|
||||
{
|
||||
handle = Natives.soundio_create();
|
||||
}
|
||||
|
||||
internal SoundIO(Pointer<SoundIo> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
foreach (var h in allocated_hglobals)
|
||||
{
|
||||
Marshal.FreeHGlobal(h);
|
||||
}
|
||||
|
||||
Natives.soundio_destroy(handle);
|
||||
}
|
||||
|
||||
// Equality (based on handle)
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
var d = other as SoundIO;
|
||||
|
||||
return d != null && this.handle == d.handle;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)(IntPtr)handle;
|
||||
}
|
||||
|
||||
public static bool operator == (SoundIO obj1, SoundIO obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator != (SoundIO obj1, SoundIO obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
// fields
|
||||
|
||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
||||
// this kind of code anywhere we need string marshaling.
|
||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
||||
|
||||
public string ApplicationName {
|
||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, app_name_offset)); }
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var existing = Marshal.ReadIntPtr(handle, app_name_offset);
|
||||
if (allocated_hglobals.Contains (existing))
|
||||
{
|
||||
allocated_hglobals.Remove(existing);
|
||||
Marshal.FreeHGlobal(existing);
|
||||
}
|
||||
|
||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
||||
Marshal.WriteIntPtr(handle, app_name_offset, ptr);
|
||||
allocated_hglobals.Add(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int app_name_offset = (int)Marshal.OffsetOf<SoundIo>("app_name");
|
||||
|
||||
public SoundIOBackend CurrentBackend
|
||||
{
|
||||
get { return (SoundIOBackend)Marshal.ReadInt32(handle, current_backend_offset); }
|
||||
}
|
||||
|
||||
static readonly int current_backend_offset = (int)Marshal.OffsetOf<SoundIo>("current_backend");
|
||||
|
||||
// emit_rtprio_warning
|
||||
public Action EmitRealtimePriorityWarning
|
||||
{
|
||||
get { return emit_rtprio_warning; }
|
||||
set
|
||||
{
|
||||
emit_rtprio_warning = value;
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change);
|
||||
|
||||
Marshal.WriteIntPtr(handle, emit_rtprio_warning_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int emit_rtprio_warning_offset = (int)Marshal.OffsetOf<SoundIo>("emit_rtprio_warning");
|
||||
|
||||
Action emit_rtprio_warning;
|
||||
|
||||
// jack_error_callback
|
||||
public Action<string> JackErrorCallback
|
||||
{
|
||||
get { return jack_error_callback; }
|
||||
set
|
||||
{
|
||||
jack_error_callback = value;
|
||||
if (value == null)
|
||||
{
|
||||
jack_error_callback = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
jack_error_callback_native = msg => jack_error_callback(msg);
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(jack_error_callback_native);
|
||||
Marshal.WriteIntPtr(handle, jack_error_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int jack_error_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_error_callback");
|
||||
|
||||
Action<string> jack_error_callback;
|
||||
delegate void jack_error_delegate(string message);
|
||||
jack_error_delegate jack_error_callback_native;
|
||||
|
||||
// jack_info_callback
|
||||
public Action<string> JackInfoCallback
|
||||
{
|
||||
get { return jack_info_callback; }
|
||||
set
|
||||
{
|
||||
jack_info_callback = value;
|
||||
if (value == null)
|
||||
{
|
||||
jack_info_callback = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
jack_info_callback_native = msg => jack_info_callback(msg);
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(jack_info_callback_native);
|
||||
Marshal.WriteIntPtr(handle, jack_info_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int jack_info_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_info_callback");
|
||||
|
||||
Action<string> jack_info_callback;
|
||||
delegate void jack_info_delegate(string message);
|
||||
jack_info_delegate jack_info_callback_native;
|
||||
|
||||
// on_backend_disconnect
|
||||
public Action<int> OnBackendDisconnect
|
||||
{
|
||||
get { return on_backend_disconnect; }
|
||||
set
|
||||
{
|
||||
on_backend_disconnect = value;
|
||||
if (value == null)
|
||||
{
|
||||
on_backend_disconnect_native = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
on_backend_disconnect_native = (sio, err) => on_backend_disconnect(err);
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_backend_disconnect_native);
|
||||
Marshal.WriteIntPtr(handle, on_backend_disconnect_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int on_backend_disconnect_offset = (int)Marshal.OffsetOf<SoundIo>("on_backend_disconnect");
|
||||
|
||||
Action<int> on_backend_disconnect;
|
||||
delegate void on_backend_disconnect_delegate(IntPtr handle, int errorCode);
|
||||
on_backend_disconnect_delegate on_backend_disconnect_native;
|
||||
|
||||
// on_devices_change
|
||||
public Action OnDevicesChange
|
||||
{
|
||||
get { return on_devices_change; }
|
||||
set
|
||||
{
|
||||
on_devices_change = value;
|
||||
if (value == null)
|
||||
{
|
||||
on_devices_change_native = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
on_devices_change_native = sio => on_devices_change();
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change_native);
|
||||
Marshal.WriteIntPtr(handle, on_devices_change_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int on_devices_change_offset = (int)Marshal.OffsetOf<SoundIo>("on_devices_change");
|
||||
|
||||
Action on_devices_change;
|
||||
delegate void on_devices_change_delegate(IntPtr handle);
|
||||
on_devices_change_delegate on_devices_change_native;
|
||||
|
||||
// on_events_signal
|
||||
public Action OnEventsSignal
|
||||
{
|
||||
get { return on_events_signal; }
|
||||
set
|
||||
{
|
||||
on_events_signal = value;
|
||||
if (value == null)
|
||||
{
|
||||
on_events_signal_native = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
on_events_signal_native = sio => on_events_signal();
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_events_signal_native);
|
||||
Marshal.WriteIntPtr(handle, on_events_signal_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int on_events_signal_offset = (int)Marshal.OffsetOf<SoundIo>("on_events_signal");
|
||||
|
||||
Action on_events_signal;
|
||||
delegate void on_events_signal_delegate(IntPtr handle);
|
||||
on_events_signal_delegate on_events_signal_native;
|
||||
|
||||
|
||||
// functions
|
||||
|
||||
public int BackendCount
|
||||
{
|
||||
get { return Natives.soundio_backend_count(handle); }
|
||||
}
|
||||
|
||||
public int InputDeviceCount
|
||||
{
|
||||
get { return Natives.soundio_input_device_count(handle); }
|
||||
}
|
||||
|
||||
public int OutputDeviceCount
|
||||
{
|
||||
get { return Natives.soundio_output_device_count(handle); }
|
||||
}
|
||||
|
||||
public int DefaultInputDeviceIndex
|
||||
{
|
||||
get { return Natives.soundio_default_input_device_index(handle); }
|
||||
}
|
||||
|
||||
public int DefaultOutputDeviceIndex
|
||||
{
|
||||
get { return Natives.soundio_default_output_device_index(handle); }
|
||||
}
|
||||
|
||||
public SoundIOBackend GetBackend(int index)
|
||||
{
|
||||
return (SoundIOBackend)Natives.soundio_get_backend(handle, index);
|
||||
}
|
||||
|
||||
public SoundIODevice GetInputDevice(int index)
|
||||
{
|
||||
return new SoundIODevice(Natives.soundio_get_input_device(handle, index));
|
||||
}
|
||||
|
||||
public SoundIODevice GetOutputDevice(int index)
|
||||
{
|
||||
return new SoundIODevice(Natives.soundio_get_output_device(handle, index));
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_connect(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public void ConnectBackend(SoundIOBackend backend)
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_connect_backend(handle, (SoundIoBackend)backend);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
Natives.soundio_disconnect(handle);
|
||||
}
|
||||
|
||||
public void FlushEvents()
|
||||
{
|
||||
Natives.soundio_flush_events(handle);
|
||||
}
|
||||
|
||||
public void WaitEvents()
|
||||
{
|
||||
Natives.soundio_wait_events(handle);
|
||||
}
|
||||
|
||||
public void Wakeup()
|
||||
{
|
||||
Natives.soundio_wakeup(handle);
|
||||
}
|
||||
|
||||
public void ForceDeviceScan()
|
||||
{
|
||||
Natives.soundio_force_device_scan(handle);
|
||||
}
|
||||
|
||||
public SoundIORingBuffer CreateRingBuffer(int capacity)
|
||||
{
|
||||
return new SoundIORingBuffer(Natives.soundio_ring_buffer_create(handle, capacity));
|
||||
}
|
||||
|
||||
// static methods
|
||||
|
||||
public static string VersionString
|
||||
{
|
||||
get { return Marshal.PtrToStringAnsi(Natives.soundio_version_string()); }
|
||||
}
|
||||
|
||||
public static int VersionMajor
|
||||
{
|
||||
get { return Natives.soundio_version_major(); }
|
||||
}
|
||||
|
||||
public static int VersionMinor
|
||||
{
|
||||
get { return Natives.soundio_version_minor(); }
|
||||
}
|
||||
|
||||
public static int VersionPatch
|
||||
{
|
||||
get { return Natives.soundio_version_patch(); }
|
||||
}
|
||||
|
||||
public static string GetBackendName(SoundIOBackend backend)
|
||||
{
|
||||
return Marshal.PtrToStringAnsi(Natives.soundio_backend_name((SoundIoBackend)backend));
|
||||
}
|
||||
|
||||
public static bool HaveBackend(SoundIOBackend backend)
|
||||
{
|
||||
return Natives.soundio_have_backend((SoundIoBackend)backend);
|
||||
}
|
||||
|
||||
public static int GetBytesPerSample(SoundIOFormat format)
|
||||
{
|
||||
return Natives.soundio_get_bytes_per_sample((SoundIoFormat)format);
|
||||
}
|
||||
|
||||
public static int GetBytesPerFrame(SoundIOFormat format, int channelCount)
|
||||
{
|
||||
return Natives.soundio_get_bytes_per_frame((SoundIoFormat)format, channelCount);
|
||||
}
|
||||
|
||||
public static int GetBytesPerSecond(SoundIOFormat format, int channelCount, int sampleRate)
|
||||
{
|
||||
return Natives.soundio_get_bytes_per_second((SoundIoFormat)format, channelCount, sampleRate);
|
||||
}
|
||||
|
||||
public static string GetSoundFormatName(SoundIOFormat format)
|
||||
{
|
||||
return Marshal.PtrToStringAnsi(Natives.soundio_format_string((SoundIoFormat)format));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public enum SoundIOBackend
|
||||
{
|
||||
None,
|
||||
Jack,
|
||||
PulseAudio,
|
||||
Alsa,
|
||||
CoreAudio,
|
||||
Wasapi,
|
||||
Dummy
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public struct SoundIOChannelArea
|
||||
{
|
||||
internal SoundIOChannelArea(Pointer<SoundIoChannelArea> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
Pointer<SoundIoChannelArea> handle;
|
||||
|
||||
public IntPtr Pointer
|
||||
{
|
||||
get { return Marshal.ReadIntPtr(handle, ptr_offset); }
|
||||
set { Marshal.WriteIntPtr(handle, ptr_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int ptr_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("ptr");
|
||||
|
||||
public int Step
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, step_offset); }
|
||||
}
|
||||
|
||||
static readonly int step_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("step");
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public struct SoundIOChannelAreas
|
||||
{
|
||||
static readonly int native_size = Marshal.SizeOf<SoundIoChannelArea>();
|
||||
|
||||
internal SoundIOChannelAreas(IntPtr head, int channelCount, int frameCount)
|
||||
{
|
||||
this.head = head;
|
||||
this.channel_count = channelCount;
|
||||
this.frame_count = frameCount;
|
||||
}
|
||||
|
||||
IntPtr head;
|
||||
int channel_count;
|
||||
int frame_count;
|
||||
|
||||
public bool IsEmpty
|
||||
{
|
||||
get { return head == IntPtr.Zero; }
|
||||
}
|
||||
|
||||
public SoundIOChannelArea GetArea(int channel)
|
||||
{
|
||||
return new SoundIOChannelArea(head + native_size * channel);
|
||||
}
|
||||
|
||||
public int ChannelCount => channel_count;
|
||||
public int FrameCount => frame_count;
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public enum SoundIOChannelId
|
||||
{
|
||||
Invalid,
|
||||
FrontLeft,
|
||||
FrontRight,
|
||||
FrontCenter,
|
||||
Lfe,
|
||||
BackLeft,
|
||||
BackRight,
|
||||
FrontLeftCenter,
|
||||
FrontRightCenter,
|
||||
BackCenter,
|
||||
SideLeft,
|
||||
SideRight,
|
||||
TopCenter,
|
||||
TopFrontLeft,
|
||||
TopFrontCenter,
|
||||
TopFrontRight,
|
||||
TopBackLeft,
|
||||
TopBackCenter,
|
||||
TopBackRight,
|
||||
BackLeftCenter,
|
||||
BackRightCenter,
|
||||
FrontLeftWide,
|
||||
FrontRightWide,
|
||||
FrontLeftHigh,
|
||||
FrontCenterHigh,
|
||||
FrontRightHigh,
|
||||
TopFrontLeftCenter,
|
||||
TopFrontRightCenter,
|
||||
TopSideLeft,
|
||||
TopSideRight,
|
||||
LeftLfe,
|
||||
RightLfe,
|
||||
Lfe2,
|
||||
BottomCenter,
|
||||
BottomLeftCenter,
|
||||
BottomRightCenter,
|
||||
MsMid,
|
||||
MsSide,
|
||||
AmbisonicW,
|
||||
AmbisonicX,
|
||||
AmbisonicY,
|
||||
AmbisonicZ,
|
||||
XyX,
|
||||
XyY,
|
||||
HeadphonesLeft,
|
||||
HeadphonesRight,
|
||||
ClickTrack,
|
||||
ForeignLanguage,
|
||||
HearingImpaired,
|
||||
Narration,
|
||||
Haptic,
|
||||
DialogCentricMix,
|
||||
Aux,
|
||||
Aux0,
|
||||
Aux1,
|
||||
Aux2,
|
||||
Aux3,
|
||||
Aux4,
|
||||
Aux5,
|
||||
Aux6,
|
||||
Aux7,
|
||||
Aux8,
|
||||
Aux9,
|
||||
Aux10,
|
||||
Aux11,
|
||||
Aux12,
|
||||
Aux13,
|
||||
Aux14,
|
||||
Aux15
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public struct SoundIOChannelLayout
|
||||
{
|
||||
public static int BuiltInCount
|
||||
{
|
||||
get { return Natives.soundio_channel_layout_builtin_count(); }
|
||||
}
|
||||
|
||||
public static SoundIOChannelLayout GetBuiltIn(int index)
|
||||
{
|
||||
return new SoundIOChannelLayout(Natives.soundio_channel_layout_get_builtin(index));
|
||||
}
|
||||
|
||||
public static SoundIOChannelLayout GetDefault(int channelCount)
|
||||
{
|
||||
var handle = Natives.soundio_channel_layout_get_default(channelCount);
|
||||
|
||||
return new SoundIOChannelLayout (handle);
|
||||
}
|
||||
|
||||
public static SoundIOChannelId ParseChannelId(string name)
|
||||
{
|
||||
var ptr = Marshal.StringToHGlobalAnsi(name);
|
||||
|
||||
try
|
||||
{
|
||||
return (SoundIOChannelId)Natives.soundio_parse_channel_id(ptr, name.Length);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
// instance members
|
||||
|
||||
internal SoundIOChannelLayout(Pointer<SoundIoChannelLayout> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
readonly Pointer<SoundIoChannelLayout> handle;
|
||||
|
||||
public bool IsNull
|
||||
{
|
||||
get { return handle.Handle == IntPtr.Zero; }
|
||||
}
|
||||
|
||||
internal IntPtr Handle
|
||||
{
|
||||
get { return handle; }
|
||||
}
|
||||
|
||||
public int ChannelCount
|
||||
{
|
||||
get { return IsNull ? 0 : Marshal.ReadInt32((IntPtr)handle + channel_count_offset); }
|
||||
}
|
||||
|
||||
static readonly int channel_count_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channel_count");
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return IsNull ? null : Marshal.PtrToStringAnsi(Marshal.ReadIntPtr((IntPtr)handle + name_offset)); }
|
||||
}
|
||||
|
||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("name");
|
||||
|
||||
public IEnumerable<SoundIOChannelId> Channels
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsNull) yield break;
|
||||
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
yield return (SoundIOChannelId)Marshal.ReadInt32((IntPtr)handle + channels_offset + sizeof(SoundIoChannelId) * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int channels_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channels");
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
if (!(other is SoundIOChannelLayout)) return false;
|
||||
|
||||
var s = (SoundIOChannelLayout) other;
|
||||
|
||||
return handle == s.handle || Natives.soundio_channel_layout_equal(handle, s.handle);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return handle.GetHashCode();
|
||||
}
|
||||
|
||||
public string DetectBuiltInName()
|
||||
{
|
||||
if (IsNull) throw new InvalidOperationException();
|
||||
|
||||
return Natives.soundio_channel_layout_detect_builtin(handle) ? Name : null;
|
||||
}
|
||||
|
||||
public int FindChannel(SoundIOChannelId channel)
|
||||
{
|
||||
if (IsNull) throw new InvalidOperationException();
|
||||
|
||||
return Natives.soundio_channel_layout_find_channel(handle, (SoundIoChannelId)channel);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,267 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public class SoundIODevice
|
||||
{
|
||||
public static SoundIOChannelLayout BestMatchingChannelLayout(SoundIODevice device1, SoundIODevice device2)
|
||||
{
|
||||
var ptr1 = Marshal.ReadIntPtr(device1.handle, layouts_offset);
|
||||
var ptr2 = Marshal.ReadIntPtr(device2.handle, layouts_offset);
|
||||
|
||||
return new SoundIOChannelLayout(Natives.soundio_best_matching_channel_layout(ptr1, device1.LayoutCount, ptr2, device2.LayoutCount));
|
||||
}
|
||||
|
||||
internal SoundIODevice(Pointer<SoundIoDevice> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
readonly Pointer<SoundIoDevice> handle;
|
||||
|
||||
// Equality (based on handle and native func)
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
var d = other as SoundIODevice;
|
||||
|
||||
return d != null && (this.handle == d.handle || Natives.soundio_device_equal (this.handle, d.handle));
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)(IntPtr)handle;
|
||||
}
|
||||
|
||||
public static bool operator == (SoundIODevice obj1, SoundIODevice obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator != (SoundIODevice obj1, SoundIODevice obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
// fields
|
||||
|
||||
public SoundIODeviceAim Aim
|
||||
{
|
||||
get { return (SoundIODeviceAim)Marshal.ReadInt32(handle, aim_offset); }
|
||||
}
|
||||
|
||||
static readonly int aim_offset = (int)Marshal.OffsetOf<SoundIoDevice>("aim");
|
||||
|
||||
public SoundIOFormat CurrentFormat
|
||||
{
|
||||
get { return (SoundIOFormat)Marshal.ReadInt32(handle, current_format_offset); }
|
||||
}
|
||||
|
||||
static readonly int current_format_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_format");
|
||||
|
||||
public SoundIOChannelLayout CurrentLayout
|
||||
{
|
||||
get { return new SoundIOChannelLayout((IntPtr)handle + current_layout_offset); }
|
||||
}
|
||||
|
||||
static readonly int current_layout_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_layout");
|
||||
|
||||
public int FormatCount
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, format_count_offset); }
|
||||
}
|
||||
|
||||
static readonly int format_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("format_count");
|
||||
|
||||
public IEnumerable<SoundIOFormat> Formats
|
||||
{
|
||||
get
|
||||
{
|
||||
var ptr = Marshal.ReadIntPtr(handle, formats_offset);
|
||||
|
||||
for (int i = 0; i < FormatCount; i++)
|
||||
{
|
||||
yield return (SoundIOFormat)Marshal.ReadInt32(ptr, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int formats_offset = (int)Marshal.OffsetOf<SoundIoDevice>("formats");
|
||||
|
||||
public string Id
|
||||
{
|
||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, id_offset)); }
|
||||
}
|
||||
|
||||
static readonly int id_offset = (int)Marshal.OffsetOf<SoundIoDevice>("id");
|
||||
|
||||
public bool IsRaw
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, is_raw_offset) != 0; }
|
||||
}
|
||||
|
||||
static readonly int is_raw_offset = (int)Marshal.OffsetOf<SoundIoDevice>("is_raw");
|
||||
|
||||
public int LayoutCount
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, layout_count_offset); }
|
||||
}
|
||||
|
||||
static readonly int layout_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layout_count");
|
||||
|
||||
public IEnumerable<SoundIOChannelLayout> Layouts
|
||||
{
|
||||
get
|
||||
{
|
||||
var ptr = Marshal.ReadIntPtr (handle, layouts_offset);
|
||||
|
||||
for (int i = 0; i < LayoutCount; i++)
|
||||
{
|
||||
yield return new SoundIOChannelLayout(ptr + i * Marshal.SizeOf<SoundIoChannelLayout>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int layouts_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layouts");
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
||||
}
|
||||
|
||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoDevice>("name");
|
||||
|
||||
public int ProbeError
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, probe_error_offset); }
|
||||
}
|
||||
|
||||
static readonly int probe_error_offset = (int)Marshal.OffsetOf<SoundIoDevice>("probe_error");
|
||||
|
||||
public int ReferenceCount
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, ref_count_offset); }
|
||||
}
|
||||
|
||||
static readonly int ref_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("ref_count");
|
||||
|
||||
public int SampleRateCount
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, sample_rate_count_offset); }
|
||||
}
|
||||
|
||||
static readonly int sample_rate_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rate_count");
|
||||
|
||||
public IEnumerable<SoundIOSampleRateRange> SampleRates
|
||||
{
|
||||
get
|
||||
{
|
||||
var ptr = Marshal.ReadIntPtr(handle, sample_rates_offset);
|
||||
|
||||
for (int i = 0; i < SampleRateCount; i++)
|
||||
{
|
||||
yield return new SoundIOSampleRateRange(Marshal.ReadInt32(ptr, i * 2), Marshal.ReadInt32(ptr, i * 2 + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int sample_rates_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rates");
|
||||
|
||||
public double SoftwareLatencyCurrent
|
||||
{
|
||||
get { return MarshalEx.ReadDouble(handle, software_latency_current_offset); }
|
||||
set { MarshalEx.WriteDouble(handle, software_latency_current_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int software_latency_current_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_current");
|
||||
|
||||
public double SoftwareLatencyMin
|
||||
{
|
||||
get { return MarshalEx.ReadDouble(handle, software_latency_min_offset); }
|
||||
set { MarshalEx.WriteDouble(handle, software_latency_min_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int software_latency_min_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_min");
|
||||
|
||||
public double SoftwareLatencyMax
|
||||
{
|
||||
get { return MarshalEx.ReadDouble(handle, software_latency_max_offset); }
|
||||
set { MarshalEx.WriteDouble(handle, software_latency_max_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int software_latency_max_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_max");
|
||||
|
||||
public SoundIO SoundIO
|
||||
{
|
||||
get { return new SoundIO(Marshal.ReadIntPtr(handle, soundio_offset)); }
|
||||
}
|
||||
|
||||
static readonly int soundio_offset = (int)Marshal.OffsetOf<SoundIoDevice>("soundio");
|
||||
|
||||
// functions
|
||||
|
||||
public void AddReference()
|
||||
{
|
||||
Natives.soundio_device_ref(handle);
|
||||
}
|
||||
|
||||
public void RemoveReference()
|
||||
{
|
||||
Natives.soundio_device_unref(handle);
|
||||
}
|
||||
|
||||
public void SortDeviceChannelLayouts()
|
||||
{
|
||||
Natives.soundio_device_sort_channel_layouts(handle);
|
||||
}
|
||||
|
||||
public static readonly SoundIOFormat S16NE = BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
||||
public static readonly SoundIOFormat U16NE = BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
||||
public static readonly SoundIOFormat S24NE = BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
||||
public static readonly SoundIOFormat U24NE = BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
||||
public static readonly SoundIOFormat S32NE = BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
||||
public static readonly SoundIOFormat U32NE = BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
||||
public static readonly SoundIOFormat Float32NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
||||
public static readonly SoundIOFormat Float64NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
||||
public static readonly SoundIOFormat S16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
||||
public static readonly SoundIOFormat U16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
||||
public static readonly SoundIOFormat S24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
||||
public static readonly SoundIOFormat U24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
||||
public static readonly SoundIOFormat S32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
||||
public static readonly SoundIOFormat U32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
||||
public static readonly SoundIOFormat Float32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
||||
public static readonly SoundIOFormat Float64FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
||||
|
||||
public bool SupportsFormat(SoundIOFormat format)
|
||||
{
|
||||
return Natives.soundio_device_supports_format(handle, (SoundIoFormat)format);
|
||||
}
|
||||
|
||||
public bool SupportsSampleRate(int sampleRate)
|
||||
{
|
||||
return Natives.soundio_device_supports_sample_rate(handle, sampleRate);
|
||||
}
|
||||
|
||||
public bool SupportsChannelCount(int channelCount)
|
||||
{
|
||||
return Natives.soundio_device_supports_layout(handle, SoundIOChannelLayout.GetDefault(channelCount).Handle);
|
||||
}
|
||||
|
||||
public int GetNearestSampleRate(int sampleRate)
|
||||
{
|
||||
return Natives.soundio_device_nearest_sample_rate(handle, sampleRate);
|
||||
}
|
||||
|
||||
public SoundIOInStream CreateInStream()
|
||||
{
|
||||
return new SoundIOInStream(Natives.soundio_instream_create(handle));
|
||||
}
|
||||
|
||||
public SoundIOOutStream CreateOutStream()
|
||||
{
|
||||
return new SoundIOOutStream(Natives.soundio_outstream_create(handle));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public enum SoundIODeviceAim // soundio.h (228, 6)
|
||||
{
|
||||
Input,
|
||||
Output
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public class SoundIOException : Exception
|
||||
{
|
||||
internal SoundIOException(SoundIoError errorCode) : base (Marshal.PtrToStringAnsi(Natives.soundio_strerror((int) errorCode))) { }
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public enum SoundIOFormat
|
||||
{
|
||||
Invalid,
|
||||
S8,
|
||||
U8,
|
||||
S16LE,
|
||||
S16BE,
|
||||
U16LE,
|
||||
U16BE,
|
||||
S24LE,
|
||||
S24BE,
|
||||
U24LE,
|
||||
U24BE,
|
||||
S32LE,
|
||||
S32BE,
|
||||
U32LE,
|
||||
U32BE,
|
||||
Float32LE,
|
||||
Float32BE,
|
||||
Float64LE,
|
||||
Float64BE
|
||||
}
|
||||
}
|
@ -1,293 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public class SoundIOInStream : IDisposable
|
||||
{
|
||||
internal SoundIOInStream(Pointer<SoundIoInStream> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
Pointer<SoundIoInStream> handle;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Natives.soundio_instream_destroy(handle);
|
||||
}
|
||||
|
||||
// Equality (based on handle)
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
var d = other as SoundIOInStream;
|
||||
|
||||
return d != null && (this.handle == d.handle);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)(IntPtr)handle;
|
||||
}
|
||||
|
||||
public static bool operator == (SoundIOInStream obj1, SoundIOInStream obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator != (SoundIOInStream obj1, SoundIOInStream obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
// fields
|
||||
|
||||
public SoundIODevice Device
|
||||
{
|
||||
get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
|
||||
}
|
||||
|
||||
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoInStream>("device");
|
||||
|
||||
public SoundIOFormat Format
|
||||
{
|
||||
get { return (SoundIOFormat)Marshal.ReadInt32(handle, format_offset); }
|
||||
set { Marshal.WriteInt32(handle, format_offset, (int) value); }
|
||||
}
|
||||
|
||||
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoInStream>("format");
|
||||
|
||||
public int SampleRate
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, sample_rate_offset); }
|
||||
set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoInStream>("sample_rate");
|
||||
|
||||
public SoundIOChannelLayout Layout
|
||||
{
|
||||
get { return new SoundIOChannelLayout ((IntPtr) handle + layout_offset); }
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Buffer.MemoryCopy((void*)((IntPtr)handle + layout_offset), (void*)value.Handle, Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout");
|
||||
|
||||
public double SoftwareLatency
|
||||
{
|
||||
get { return MarshalEx.ReadDouble(handle, software_latency_offset); }
|
||||
set { MarshalEx.WriteDouble(handle, software_latency_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoInStream>("software_latency");
|
||||
|
||||
// error_callback
|
||||
public Action ErrorCallback
|
||||
{
|
||||
get { return error_callback; }
|
||||
set
|
||||
{
|
||||
error_callback = value;
|
||||
error_callback_native = _ => error_callback();
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
|
||||
|
||||
Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("error_callback");
|
||||
|
||||
Action error_callback;
|
||||
delegate void error_callback_delegate(IntPtr handle);
|
||||
error_callback_delegate error_callback_native;
|
||||
|
||||
// read_callback
|
||||
public Action<int,int> ReadCallback
|
||||
{
|
||||
get { return read_callback; }
|
||||
set
|
||||
{
|
||||
read_callback = value;
|
||||
read_callback_native = (_, minFrameCount, maxFrameCount) => read_callback(minFrameCount, maxFrameCount);
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(read_callback_native);
|
||||
|
||||
Marshal.WriteIntPtr(handle, read_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int read_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("read_callback");
|
||||
|
||||
Action<int, int> read_callback;
|
||||
delegate void read_callback_delegate(IntPtr handle, int min, int max);
|
||||
read_callback_delegate read_callback_native;
|
||||
|
||||
// overflow_callback
|
||||
public Action OverflowCallback
|
||||
{
|
||||
get { return overflow_callback; }
|
||||
set
|
||||
{
|
||||
overflow_callback = value;
|
||||
overflow_callback_native = _ => overflow_callback();
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(overflow_callback_native);
|
||||
|
||||
Marshal.WriteIntPtr(handle, overflow_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
static readonly int overflow_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("overflow_callback");
|
||||
|
||||
Action overflow_callback;
|
||||
delegate void overflow_callback_delegate(IntPtr handle);
|
||||
overflow_callback_delegate overflow_callback_native;
|
||||
|
||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
||||
// this kind of code anywhere we need string marshaling.
|
||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var existing = Marshal.ReadIntPtr(handle, name_offset);
|
||||
if (allocated_hglobals.Contains(existing))
|
||||
{
|
||||
allocated_hglobals.Remove(existing);
|
||||
Marshal.FreeHGlobal(existing);
|
||||
}
|
||||
|
||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
||||
Marshal.WriteIntPtr(handle, name_offset, ptr);
|
||||
allocated_hglobals.Add(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoInStream>("name");
|
||||
|
||||
public bool NonTerminalHint
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
|
||||
}
|
||||
|
||||
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoInStream>("non_terminal_hint");
|
||||
|
||||
public int BytesPerFrame
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, bytes_per_frame_offset); }
|
||||
}
|
||||
|
||||
static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoInStream>("bytes_per_frame");
|
||||
|
||||
public int BytesPerSample
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, bytes_per_sample_offset); }
|
||||
}
|
||||
|
||||
static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoInStream>("bytes_per_sample");
|
||||
|
||||
public string LayoutErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
var code = (SoundIoError)Marshal.ReadInt32(handle, layout_error_offset);
|
||||
|
||||
return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi(Natives.soundio_strerror((int)code));
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int layout_error_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout_error");
|
||||
|
||||
// functions
|
||||
|
||||
public void Open()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_instream_open(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_instream_start(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public SoundIOChannelAreas BeginRead(ref int frameCount)
|
||||
{
|
||||
IntPtr ptrs = default;
|
||||
int nativeFrameCount = frameCount;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var frameCountPtr = &nativeFrameCount;
|
||||
var ptrptr = &ptrs;
|
||||
var ret = (SoundIoError)Natives.soundio_instream_begin_read(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
|
||||
|
||||
frameCount = *frameCountPtr;
|
||||
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
|
||||
return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndRead()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_instream_end_read(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public void Pause(bool pause)
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_instream_pause(handle, pause);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public double GetLatency()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
double* dptr = null;
|
||||
IntPtr p = new IntPtr(dptr);
|
||||
|
||||
var ret = (SoundIoError)Natives.soundio_instream_get_latency(handle, p);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
|
||||
dptr = (double*)p;
|
||||
|
||||
return *dptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,331 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public class SoundIOOutStream : IDisposable
|
||||
{
|
||||
internal SoundIOOutStream (Pointer<SoundIoOutStream> handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
Pointer<SoundIoOutStream> handle;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Natives.soundio_outstream_destroy (handle);
|
||||
}
|
||||
// Equality (based on handle)
|
||||
|
||||
public override bool Equals (object other)
|
||||
{
|
||||
var d = other as SoundIOOutStream;
|
||||
|
||||
return d != null && (this.handle == d.handle);
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
return (int)(IntPtr)handle;
|
||||
}
|
||||
|
||||
public static bool operator == (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
public static bool operator != (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
||||
{
|
||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
||||
}
|
||||
|
||||
// fields
|
||||
|
||||
public SoundIODevice Device
|
||||
{
|
||||
get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
|
||||
}
|
||||
|
||||
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("device");
|
||||
|
||||
public SoundIOFormat Format
|
||||
{
|
||||
get { return (SoundIOFormat) Marshal.ReadInt32(handle, format_offset); }
|
||||
set { Marshal.WriteInt32(handle, format_offset, (int) value); }
|
||||
}
|
||||
|
||||
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("format");
|
||||
|
||||
public int SampleRate
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, sample_rate_offset); }
|
||||
set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("sample_rate");
|
||||
|
||||
public SoundIOChannelLayout Layout
|
||||
{
|
||||
get { unsafe { return new SoundIOChannelLayout((IntPtr) (void*)((IntPtr)handle + layout_offset)); } }
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Buffer.MemoryCopy((void*)value.Handle, (void*)((IntPtr)handle + layout_offset), Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
|
||||
}
|
||||
}
|
||||
}
|
||||
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("layout");
|
||||
|
||||
public double SoftwareLatency
|
||||
{
|
||||
get { return MarshalEx.ReadDouble (handle, software_latency_offset); }
|
||||
set { MarshalEx.WriteDouble (handle, software_latency_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("software_latency");
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get { return MarshalEx.ReadFloat(handle, volume_offset); }
|
||||
set { MarshalEx.WriteFloat(handle, volume_offset, value); }
|
||||
}
|
||||
|
||||
static readonly int volume_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("volume");
|
||||
|
||||
// error_callback
|
||||
public Action ErrorCallback
|
||||
{
|
||||
get { return error_callback; }
|
||||
set
|
||||
{
|
||||
error_callback = value;
|
||||
if (value == null)
|
||||
{
|
||||
error_callback_native = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
error_callback_native = stream => error_callback();
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
|
||||
Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("error_callback");
|
||||
|
||||
Action error_callback;
|
||||
delegate void error_callback_delegate (IntPtr handle);
|
||||
error_callback_delegate error_callback_native;
|
||||
|
||||
// write_callback
|
||||
public Action<int, int> WriteCallback
|
||||
{
|
||||
get { return write_callback; }
|
||||
set
|
||||
{
|
||||
write_callback = value;
|
||||
if (value == null)
|
||||
{
|
||||
write_callback_native = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
write_callback_native = (h, frame_count_min, frame_count_max) => write_callback(frame_count_min, frame_count_max);
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate (write_callback_native);
|
||||
Marshal.WriteIntPtr (handle, write_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int write_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("write_callback");
|
||||
|
||||
Action<int, int> write_callback;
|
||||
delegate void write_callback_delegate(IntPtr handle, int min, int max);
|
||||
write_callback_delegate write_callback_native;
|
||||
|
||||
// underflow_callback
|
||||
public Action UnderflowCallback
|
||||
{
|
||||
get { return underflow_callback; }
|
||||
set
|
||||
{
|
||||
underflow_callback = value;
|
||||
if (value == null)
|
||||
{
|
||||
underflow_callback_native = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
underflow_callback_native = h => underflow_callback();
|
||||
}
|
||||
|
||||
var ptr = Marshal.GetFunctionPointerForDelegate (underflow_callback_native);
|
||||
Marshal.WriteIntPtr (handle, underflow_callback_offset, ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int underflow_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("underflow_callback");
|
||||
|
||||
Action underflow_callback;
|
||||
delegate void underflow_callback_delegate(IntPtr handle);
|
||||
underflow_callback_delegate underflow_callback_native;
|
||||
|
||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
||||
// this kind of code anywhere we need string marshaling.
|
||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
||||
|
||||
public string Name {
|
||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
||||
set
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var existing = Marshal.ReadIntPtr(handle, name_offset);
|
||||
if (allocated_hglobals.Contains(existing))
|
||||
{
|
||||
allocated_hglobals.Remove(existing);
|
||||
Marshal.FreeHGlobal(existing);
|
||||
}
|
||||
|
||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
||||
Marshal.WriteIntPtr(handle, name_offset, ptr);
|
||||
allocated_hglobals.Add(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("name");
|
||||
|
||||
public bool NonTerminalHint
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
|
||||
}
|
||||
|
||||
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("non_terminal_hint");
|
||||
|
||||
public int BytesPerFrame
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, bytes_per_frame_offset); }
|
||||
}
|
||||
|
||||
static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("bytes_per_frame");
|
||||
|
||||
public int BytesPerSample
|
||||
{
|
||||
get { return Marshal.ReadInt32(handle, bytes_per_sample_offset); }
|
||||
}
|
||||
|
||||
static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("bytes_per_sample");
|
||||
|
||||
public string LayoutErrorMessage
|
||||
{
|
||||
get
|
||||
{
|
||||
var code = (SoundIoError)Marshal.ReadInt32(handle, layout_error_offset);
|
||||
|
||||
return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi(Natives.soundio_strerror((int)code));
|
||||
}
|
||||
}
|
||||
|
||||
static readonly int layout_error_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("layout_error");
|
||||
|
||||
// functions
|
||||
|
||||
public void Open ()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_open(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start ()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_start(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public SoundIOChannelAreas BeginWrite(ref int frameCount)
|
||||
{
|
||||
IntPtr ptrs = default;
|
||||
int nativeFrameCount = frameCount;
|
||||
|
||||
unsafe
|
||||
{
|
||||
var frameCountPtr = &nativeFrameCount;
|
||||
var ptrptr = &ptrs;
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_begin_write(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
|
||||
|
||||
frameCount = *frameCountPtr;
|
||||
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
|
||||
return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void EndWrite ()
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_end_write(handle);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearBuffer ()
|
||||
{
|
||||
_ = Natives.soundio_outstream_clear_buffer(handle);
|
||||
}
|
||||
|
||||
public void Pause (bool pause)
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_pause(handle, pause);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
|
||||
public double GetLatency ()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
double* dptr = null;
|
||||
IntPtr p = new IntPtr(dptr);
|
||||
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_get_latency(handle, p);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
|
||||
dptr = (double*)p;
|
||||
|
||||
return *dptr;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetVolume (double volume)
|
||||
{
|
||||
var ret = (SoundIoError)Natives.soundio_outstream_set_volume(handle, volume);
|
||||
if (ret != SoundIoError.SoundIoErrorNone)
|
||||
{
|
||||
throw new SoundIOException(ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
using System;
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public class SoundIORingBuffer : IDisposable
|
||||
{
|
||||
internal SoundIORingBuffer(IntPtr handle)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
IntPtr handle;
|
||||
|
||||
public int Capacity
|
||||
{
|
||||
get { return Natives.soundio_ring_buffer_capacity(handle); }
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Natives.soundio_ring_buffer_clear(handle);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Natives.soundio_ring_buffer_destroy(handle);
|
||||
}
|
||||
|
||||
public int FillCount
|
||||
{
|
||||
get { return Natives.soundio_ring_buffer_fill_count(handle); }
|
||||
}
|
||||
|
||||
public int FreeCount
|
||||
{
|
||||
get { return Natives.soundio_ring_buffer_free_count(handle); }
|
||||
}
|
||||
|
||||
public IntPtr ReadPointer
|
||||
{
|
||||
get { return Natives.soundio_ring_buffer_read_ptr(handle); }
|
||||
}
|
||||
|
||||
public IntPtr WritePointer
|
||||
{
|
||||
get { return Natives.soundio_ring_buffer_write_ptr(handle); }
|
||||
}
|
||||
|
||||
public void AdvanceReadPointer(int count)
|
||||
{
|
||||
Natives.soundio_ring_buffer_advance_read_ptr(handle, count);
|
||||
}
|
||||
|
||||
public void AdvanceWritePointer(int count)
|
||||
{
|
||||
Natives.soundio_ring_buffer_advance_write_ptr(handle, count);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
public struct SoundIOSampleRateRange
|
||||
{
|
||||
internal SoundIOSampleRateRange(int min, int max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
public readonly int Min;
|
||||
public readonly int Max;
|
||||
}
|
||||
}
|
@ -1,643 +0,0 @@
|
||||
// This source file is generated by nclang PInvokeGenerator.
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using delegate0 = SoundIOSharp.Delegates.delegate0;
|
||||
using delegate1 = SoundIOSharp.Delegates.delegate1;
|
||||
using delegate2 = SoundIOSharp.Delegates.delegate2;
|
||||
using delegate3 = SoundIOSharp.Delegates.delegate3;
|
||||
using delegate4 = SoundIOSharp.Delegates.delegate4;
|
||||
using delegate5 = SoundIOSharp.Delegates.delegate5;
|
||||
using delegate6 = SoundIOSharp.Delegates.delegate6;
|
||||
using delegate7 = SoundIOSharp.Delegates.delegate7;
|
||||
using delegate8 = SoundIOSharp.Delegates.delegate8;
|
||||
using delegate9 = SoundIOSharp.Delegates.delegate9;
|
||||
|
||||
namespace SoundIOSharp
|
||||
{
|
||||
enum SoundIoError // soundio.h (72, 6)
|
||||
{
|
||||
SoundIoErrorNone = 0,
|
||||
SoundIoErrorNoMem = 1,
|
||||
SoundIoErrorInitAudioBackend = 2,
|
||||
SoundIoErrorSystemResources = 3,
|
||||
SoundIoErrorOpeningDevice = 4,
|
||||
SoundIoErrorNoSuchDevice = 5,
|
||||
SoundIoErrorInvalid = 6,
|
||||
SoundIoErrorBackendUnavailable = 7,
|
||||
SoundIoErrorStreaming = 8,
|
||||
SoundIoErrorIncompatibleDevice = 9,
|
||||
SoundIoErrorNoSuchClient = 10,
|
||||
SoundIoErrorIncompatibleBackend = 11,
|
||||
SoundIoErrorBackendDisconnected = 12,
|
||||
SoundIoErrorInterrupted = 13,
|
||||
SoundIoErrorUnderflow = 14,
|
||||
SoundIoErrorEncodingString = 15,
|
||||
}
|
||||
|
||||
enum SoundIoChannelId // soundio.h (106, 6)
|
||||
{
|
||||
SoundIoChannelIdInvalid = 0,
|
||||
SoundIoChannelIdFrontLeft = 1,
|
||||
SoundIoChannelIdFrontRight = 2,
|
||||
SoundIoChannelIdFrontCenter = 3,
|
||||
SoundIoChannelIdLfe = 4,
|
||||
SoundIoChannelIdBackLeft = 5,
|
||||
SoundIoChannelIdBackRight = 6,
|
||||
SoundIoChannelIdFrontLeftCenter = 7,
|
||||
SoundIoChannelIdFrontRightCenter = 8,
|
||||
SoundIoChannelIdBackCenter = 9,
|
||||
SoundIoChannelIdSideLeft = 10,
|
||||
SoundIoChannelIdSideRight = 11,
|
||||
SoundIoChannelIdTopCenter = 12,
|
||||
SoundIoChannelIdTopFrontLeft = 13,
|
||||
SoundIoChannelIdTopFrontCenter = 14,
|
||||
SoundIoChannelIdTopFrontRight = 15,
|
||||
SoundIoChannelIdTopBackLeft = 16,
|
||||
SoundIoChannelIdTopBackCenter = 17,
|
||||
SoundIoChannelIdTopBackRight = 18,
|
||||
SoundIoChannelIdBackLeftCenter = 19,
|
||||
SoundIoChannelIdBackRightCenter = 20,
|
||||
SoundIoChannelIdFrontLeftWide = 21,
|
||||
SoundIoChannelIdFrontRightWide = 22,
|
||||
SoundIoChannelIdFrontLeftHigh = 23,
|
||||
SoundIoChannelIdFrontCenterHigh = 24,
|
||||
SoundIoChannelIdFrontRightHigh = 25,
|
||||
SoundIoChannelIdTopFrontLeftCenter = 26,
|
||||
SoundIoChannelIdTopFrontRightCenter = 27,
|
||||
SoundIoChannelIdTopSideLeft = 28,
|
||||
SoundIoChannelIdTopSideRight = 29,
|
||||
SoundIoChannelIdLeftLfe = 30,
|
||||
SoundIoChannelIdRightLfe = 31,
|
||||
SoundIoChannelIdLfe2 = 32,
|
||||
SoundIoChannelIdBottomCenter = 33,
|
||||
SoundIoChannelIdBottomLeftCenter = 34,
|
||||
SoundIoChannelIdBottomRightCenter = 35,
|
||||
SoundIoChannelIdMsMid = 36,
|
||||
SoundIoChannelIdMsSide = 37,
|
||||
SoundIoChannelIdAmbisonicW = 38,
|
||||
SoundIoChannelIdAmbisonicX = 39,
|
||||
SoundIoChannelIdAmbisonicY = 40,
|
||||
SoundIoChannelIdAmbisonicZ = 41,
|
||||
SoundIoChannelIdXyX = 42,
|
||||
SoundIoChannelIdXyY = 43,
|
||||
SoundIoChannelIdHeadphonesLeft = 44,
|
||||
SoundIoChannelIdHeadphonesRight = 45,
|
||||
SoundIoChannelIdClickTrack = 46,
|
||||
SoundIoChannelIdForeignLanguage = 47,
|
||||
SoundIoChannelIdHearingImpaired = 48,
|
||||
SoundIoChannelIdNarration = 49,
|
||||
SoundIoChannelIdHaptic = 50,
|
||||
SoundIoChannelIdDialogCentricMix = 51,
|
||||
SoundIoChannelIdAux = 52,
|
||||
SoundIoChannelIdAux0 = 53,
|
||||
SoundIoChannelIdAux1 = 54,
|
||||
SoundIoChannelIdAux2 = 55,
|
||||
SoundIoChannelIdAux3 = 56,
|
||||
SoundIoChannelIdAux4 = 57,
|
||||
SoundIoChannelIdAux5 = 58,
|
||||
SoundIoChannelIdAux6 = 59,
|
||||
SoundIoChannelIdAux7 = 60,
|
||||
SoundIoChannelIdAux8 = 61,
|
||||
SoundIoChannelIdAux9 = 62,
|
||||
SoundIoChannelIdAux10 = 63,
|
||||
SoundIoChannelIdAux11 = 64,
|
||||
SoundIoChannelIdAux12 = 65,
|
||||
SoundIoChannelIdAux13 = 66,
|
||||
SoundIoChannelIdAux14 = 67,
|
||||
SoundIoChannelIdAux15 = 68,
|
||||
}
|
||||
|
||||
enum SoundIoChannelLayoutId // soundio.h (189, 6)
|
||||
{
|
||||
SoundIoChannelLayoutIdMono = 0,
|
||||
SoundIoChannelLayoutIdStereo = 1,
|
||||
SoundIoChannelLayoutId2Point1 = 2,
|
||||
SoundIoChannelLayoutId3Point0 = 3,
|
||||
SoundIoChannelLayoutId3Point0Back = 4,
|
||||
SoundIoChannelLayoutId3Point1 = 5,
|
||||
SoundIoChannelLayoutId4Point0 = 6,
|
||||
SoundIoChannelLayoutIdQuad = 7,
|
||||
SoundIoChannelLayoutIdQuadSide = 8,
|
||||
SoundIoChannelLayoutId4Point1 = 9,
|
||||
SoundIoChannelLayoutId5Point0Back = 10,
|
||||
SoundIoChannelLayoutId5Point0Side = 11,
|
||||
SoundIoChannelLayoutId5Point1 = 12,
|
||||
SoundIoChannelLayoutId5Point1Back = 13,
|
||||
SoundIoChannelLayoutId6Point0Side = 14,
|
||||
SoundIoChannelLayoutId6Point0Front = 15,
|
||||
SoundIoChannelLayoutIdHexagonal = 16,
|
||||
SoundIoChannelLayoutId6Point1 = 17,
|
||||
SoundIoChannelLayoutId6Point1Back = 18,
|
||||
SoundIoChannelLayoutId6Point1Front = 19,
|
||||
SoundIoChannelLayoutId7Point0 = 20,
|
||||
SoundIoChannelLayoutId7Point0Front = 21,
|
||||
SoundIoChannelLayoutId7Point1 = 22,
|
||||
SoundIoChannelLayoutId7Point1Wide = 23,
|
||||
SoundIoChannelLayoutId7Point1WideBack = 24,
|
||||
SoundIoChannelLayoutIdOctagonal = 25,
|
||||
}
|
||||
|
||||
enum SoundIoBackend // soundio.h (218, 6)
|
||||
{
|
||||
SoundIoBackendNone = 0,
|
||||
SoundIoBackendJack = 1,
|
||||
SoundIoBackendPulseAudio = 2,
|
||||
SoundIoBackendAlsa = 3,
|
||||
SoundIoBackendCoreAudio = 4,
|
||||
SoundIoBackendWasapi = 5,
|
||||
SoundIoBackendDummy = 6,
|
||||
}
|
||||
|
||||
enum SoundIoDeviceAim // soundio.h (228, 6)
|
||||
{
|
||||
SoundIoDeviceAimInput = 0,
|
||||
SoundIoDeviceAimOutput = 1,
|
||||
}
|
||||
|
||||
enum SoundIoFormat // soundio.h (235, 6)
|
||||
{
|
||||
SoundIoFormatInvalid = 0,
|
||||
SoundIoFormatS8 = 1,
|
||||
SoundIoFormatU8 = 2,
|
||||
SoundIoFormatS16LE = 3,
|
||||
SoundIoFormatS16BE = 4,
|
||||
SoundIoFormatU16LE = 5,
|
||||
SoundIoFormatU16BE = 6,
|
||||
SoundIoFormatS24LE = 7,
|
||||
SoundIoFormatS24BE = 8,
|
||||
SoundIoFormatU24LE = 9,
|
||||
SoundIoFormatU24BE = 10,
|
||||
SoundIoFormatS32LE = 11,
|
||||
SoundIoFormatS32BE = 12,
|
||||
SoundIoFormatU32LE = 13,
|
||||
SoundIoFormatU32BE = 14,
|
||||
SoundIoFormatFloat32LE = 15,
|
||||
SoundIoFormatFloat32BE = 16,
|
||||
SoundIoFormatFloat64LE = 17,
|
||||
SoundIoFormatFloat64BE = 18,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoChannelLayout // soundio.h (306, 8)
|
||||
{
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
||||
public int @channel_count;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
|
||||
[CTypeDetails("ConstArrayOf<SoundIoChannelId>")] public SoundIoChannelId[] @channels;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoSampleRateRange // soundio.h (313, 8)
|
||||
{
|
||||
public int @min;
|
||||
public int @max;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoChannelArea // soundio.h (319, 8)
|
||||
{
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @ptr;
|
||||
public int @step;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIo // soundio.h (328, 8)
|
||||
{
|
||||
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
|
||||
[CTypeDetails("Pointer<void (SoundIo *)>")] public delegate0 @on_devices_change;
|
||||
[CTypeDetails("Pointer<void (SoundIo *, int)>")] public delegate1 @on_backend_disconnect;
|
||||
[CTypeDetails("Pointer<void (SoundIo *)>")] public Delegates.delegate0 @on_events_signal;
|
||||
public SoundIoBackend @current_backend;
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @app_name;
|
||||
[CTypeDetails("Pointer<void ()>")] public delegate2 @emit_rtprio_warning;
|
||||
[CTypeDetails("Pointer<void (const char *)>")] public delegate3 @jack_info_callback;
|
||||
[CTypeDetails("Pointer<void (const char *)>")] public Delegates.delegate3 @jack_error_callback;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoDevice // soundio.h (387, 8)
|
||||
{
|
||||
[CTypeDetails("Pointer<SoundIo>")] public System.IntPtr @soundio;
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @id;
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
||||
public SoundIoDeviceAim @aim;
|
||||
[CTypeDetails("Pointer<SoundIoChannelLayout>")] public System.IntPtr @layouts;
|
||||
public int @layout_count;
|
||||
public SoundIoChannelLayout @current_layout;
|
||||
[CTypeDetails("Pointer<SoundIoFormat>")] public System.IntPtr @formats;
|
||||
public int @format_count;
|
||||
public SoundIoFormat @current_format;
|
||||
[CTypeDetails("Pointer<SoundIoSampleRateRange>")] public System.IntPtr @sample_rates;
|
||||
public int @sample_rate_count;
|
||||
public int @sample_rate_current;
|
||||
public double @software_latency_min;
|
||||
public double @software_latency_max;
|
||||
public double @software_latency_current;
|
||||
public bool @is_raw;
|
||||
public int @ref_count;
|
||||
public int @probe_error;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoOutStream // soundio.h (497, 8)
|
||||
{
|
||||
[CTypeDetails("Pointer<SoundIoDevice>")] public System.IntPtr @device;
|
||||
public SoundIoFormat @format;
|
||||
public int @sample_rate;
|
||||
public SoundIoChannelLayout @layout;
|
||||
public double @software_latency;
|
||||
public float @volume;
|
||||
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
|
||||
[CTypeDetails("Pointer<void (SoundIoOutStream *, int, int)>")] public delegate4 @write_callback;
|
||||
[CTypeDetails("Pointer<void (SoundIoOutStream *)>")] public delegate5 @underflow_callback;
|
||||
[CTypeDetails("Pointer<void (SoundIoOutStream *, int)>")] public delegate6 @error_callback;
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
||||
public bool @non_terminal_hint;
|
||||
public int @bytes_per_frame;
|
||||
public int @bytes_per_sample;
|
||||
public int @layout_error;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoInStream // soundio.h (600, 8)
|
||||
{
|
||||
[CTypeDetails("Pointer<SoundIoDevice>")] public System.IntPtr @device;
|
||||
public SoundIoFormat @format;
|
||||
public int @sample_rate;
|
||||
public SoundIoChannelLayout @layout;
|
||||
public double @software_latency;
|
||||
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
|
||||
[CTypeDetails("Pointer<void (SoundIoInStream *, int, int)>")] public delegate7 @read_callback;
|
||||
[CTypeDetails("Pointer<void (SoundIoInStream *)>")] public delegate8 @overflow_callback;
|
||||
[CTypeDetails("Pointer<void (SoundIoInStream *, int)>")] public delegate9 @error_callback;
|
||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
||||
public bool @non_terminal_hint;
|
||||
public int @bytes_per_frame;
|
||||
public int @bytes_per_sample;
|
||||
public int @layout_error;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct SoundIoRingBuffer // soundio.h (1170, 8)
|
||||
{
|
||||
}
|
||||
|
||||
partial class Natives
|
||||
{
|
||||
const string LibraryName = "libsoundio";
|
||||
// function soundio_version_string - soundio.h (682, 28)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_version_string();
|
||||
|
||||
// function soundio_version_major - soundio.h (684, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_version_major();
|
||||
|
||||
// function soundio_version_minor - soundio.h (686, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_version_minor();
|
||||
|
||||
// function soundio_version_patch - soundio.h (688, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_version_patch();
|
||||
|
||||
// function soundio_create - soundio.h (694, 32)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_create();
|
||||
|
||||
// function soundio_destroy - soundio.h (695, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_destroy([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_connect - soundio.h (705, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_connect([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_connect_backend - soundio.h (717, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_connect_backend([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, SoundIoBackend @backend);
|
||||
|
||||
// function soundio_disconnect - soundio.h (718, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_disconnect([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_strerror - soundio.h (721, 28)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_strerror(int @error);
|
||||
|
||||
// function soundio_backend_name - soundio.h (723, 28)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_backend_name(SoundIoBackend @backend);
|
||||
|
||||
// function soundio_backend_count - soundio.h (726, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_backend_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_get_backend - soundio.h (729, 36)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern SoundIoBackend soundio_get_backend([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
|
||||
|
||||
// function soundio_have_backend - soundio.h (732, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_have_backend(SoundIoBackend @backend);
|
||||
|
||||
// function soundio_flush_events - soundio.h (756, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_flush_events([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_wait_events - soundio.h (760, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_wait_events([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_wakeup - soundio.h (763, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_wakeup([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_force_device_scan - soundio.h (780, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_force_device_scan([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_channel_layout_equal - soundio.h (787, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_channel_layout_equal([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @a, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @b);
|
||||
|
||||
// function soundio_get_channel_name - soundio.h (791, 28)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_get_channel_name(SoundIoChannelId @id);
|
||||
|
||||
// function soundio_parse_channel_id - soundio.h (795, 38)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern SoundIoChannelId soundio_parse_channel_id([CTypeDetails("Pointer<byte>")]System.IntPtr @str, int @str_len);
|
||||
|
||||
// function soundio_channel_layout_builtin_count - soundio.h (798, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_channel_layout_builtin_count();
|
||||
|
||||
// function soundio_channel_layout_get_builtin - soundio.h (803, 51)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_channel_layout_get_builtin(int @index);
|
||||
|
||||
// function soundio_channel_layout_get_default - soundio.h (806, 51)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_channel_layout_get_default(int @channel_count);
|
||||
|
||||
// function soundio_channel_layout_find_channel - soundio.h (809, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_channel_layout_find_channel([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout, SoundIoChannelId @channel);
|
||||
|
||||
// function soundio_channel_layout_detect_builtin - soundio.h (814, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_channel_layout_detect_builtin([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout);
|
||||
|
||||
// function soundio_best_matching_channel_layout - soundio.h (819, 51)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_best_matching_channel_layout([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @preferred_layouts, int @preferred_layout_count, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @available_layouts, int @available_layout_count);
|
||||
|
||||
// function soundio_sort_channel_layouts - soundio.h (824, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_sort_channel_layouts([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layouts, int @layout_count);
|
||||
|
||||
// function soundio_get_bytes_per_sample - soundio.h (830, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_get_bytes_per_sample(SoundIoFormat @format);
|
||||
|
||||
// function soundio_get_bytes_per_frame - soundio.h (833, 19)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_get_bytes_per_frame(SoundIoFormat @format, int @channel_count);
|
||||
|
||||
// function soundio_get_bytes_per_second - soundio.h (838, 19)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_get_bytes_per_second(SoundIoFormat @format, int @channel_count, int @sample_rate);
|
||||
|
||||
// function soundio_format_string - soundio.h (845, 29)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_format_string(SoundIoFormat @format);
|
||||
|
||||
// function soundio_input_device_count - soundio.h (861, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_input_device_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_output_device_count - soundio.h (864, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_output_device_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_get_input_device - soundio.h (870, 38)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_get_input_device([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
|
||||
|
||||
// function soundio_get_output_device - soundio.h (875, 38)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_get_output_device([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
|
||||
|
||||
// function soundio_default_input_device_index - soundio.h (880, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_default_input_device_index([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_default_output_device_index - soundio.h (885, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_default_output_device_index([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
||||
|
||||
// function soundio_device_ref - soundio.h (888, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_device_ref([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
||||
|
||||
// function soundio_device_unref - soundio.h (891, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_device_unref([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
||||
|
||||
// function soundio_device_equal - soundio.h (895, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_device_equal([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @a, [CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @b);
|
||||
|
||||
// function soundio_device_sort_channel_layouts - soundio.h (900, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_device_sort_channel_layouts([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
||||
|
||||
// function soundio_device_supports_format - soundio.h (904, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_device_supports_format([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, SoundIoFormat @format);
|
||||
|
||||
// function soundio_device_supports_layout - soundio.h (909, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_device_supports_layout([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout);
|
||||
|
||||
// function soundio_device_supports_sample_rate - soundio.h (914, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern bool soundio_device_supports_sample_rate([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, int @sample_rate);
|
||||
|
||||
// function soundio_device_nearest_sample_rate - soundio.h (919, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_device_nearest_sample_rate([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, int @sample_rate);
|
||||
|
||||
// function soundio_outstream_create - soundio.h (929, 41)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_outstream_create([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
||||
|
||||
// function soundio_outstream_destroy - soundio.h (931, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_outstream_destroy([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
||||
|
||||
// function soundio_outstream_open - soundio.h (954, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_open([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
||||
|
||||
// function soundio_outstream_start - soundio.h (965, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_start([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
||||
|
||||
// function soundio_outstream_begin_write - soundio.h (997, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_begin_write([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, [CTypeDetails("Pointer<System.IntPtr>")]System.IntPtr @areas, [CTypeDetails("Pointer<int>")]System.IntPtr @frame_count);
|
||||
|
||||
// function soundio_outstream_end_write - soundio.h (1009, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_end_write([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
||||
|
||||
// function soundio_outstream_clear_buffer - soundio.h (1024, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_clear_buffer([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
||||
|
||||
// function soundio_outstream_pause - soundio.h (1045, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_pause([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, bool @pause);
|
||||
|
||||
// function soundio_outstream_get_latency - soundio.h (1058, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_get_latency([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, [CTypeDetails("Pointer<double>")]System.IntPtr @out_latency);
|
||||
|
||||
// function soundio_outstream_set_volume - soundio.h (1061, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_outstream_set_volume([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, double @volume);
|
||||
|
||||
// function soundio_instream_create - soundio.h (1071, 40)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_instream_create([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
||||
|
||||
// function soundio_instream_destroy - soundio.h (1073, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_instream_destroy([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
||||
|
||||
// function soundio_instream_open - soundio.h (1093, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_instream_open([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
||||
|
||||
// function soundio_instream_start - soundio.h (1102, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_instream_start([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
||||
|
||||
// function soundio_instream_begin_read - soundio.h (1133, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_instream_begin_read([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, [CTypeDetails("Pointer<System.IntPtr>")]System.IntPtr @areas, [CTypeDetails("Pointer<int>")]System.IntPtr @frame_count);
|
||||
|
||||
// function soundio_instream_end_read - soundio.h (1143, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_instream_end_read([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
||||
|
||||
// function soundio_instream_pause - soundio.h (1156, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_instream_pause([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, bool @pause);
|
||||
|
||||
// function soundio_instream_get_latency - soundio.h (1166, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_instream_get_latency([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, [CTypeDetails("Pointer<double>")]System.IntPtr @out_latency);
|
||||
|
||||
// function soundio_ring_buffer_create - soundio.h (1181, 42)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_ring_buffer_create([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @requested_capacity);
|
||||
|
||||
// function soundio_ring_buffer_destroy - soundio.h (1182, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_ring_buffer_destroy([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
// function soundio_ring_buffer_capacity - soundio.h (1186, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_ring_buffer_capacity([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
// function soundio_ring_buffer_write_ptr - soundio.h (1189, 22)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_ring_buffer_write_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
// function soundio_ring_buffer_advance_write_ptr - soundio.h (1191, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_ring_buffer_advance_write_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer, int @count);
|
||||
|
||||
// function soundio_ring_buffer_read_ptr - soundio.h (1194, 22)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern System.IntPtr soundio_ring_buffer_read_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
// function soundio_ring_buffer_advance_read_ptr - soundio.h (1196, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_ring_buffer_advance_read_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer, int @count);
|
||||
|
||||
// function soundio_ring_buffer_fill_count - soundio.h (1199, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_ring_buffer_fill_count([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
// function soundio_ring_buffer_free_count - soundio.h (1202, 20)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern int soundio_ring_buffer_free_count([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
// function soundio_ring_buffer_clear - soundio.h (1205, 21)
|
||||
[DllImport(LibraryName)]
|
||||
internal static extern void soundio_ring_buffer_clear([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
||||
|
||||
}
|
||||
|
||||
class Delegates
|
||||
{
|
||||
public delegate void delegate0(System.IntPtr p0);
|
||||
public delegate void delegate1(System.IntPtr p0, int p1);
|
||||
public delegate void delegate2();
|
||||
public delegate void delegate3(System.IntPtr p0);
|
||||
public delegate void delegate4(System.IntPtr p0, int p1, int p2);
|
||||
public delegate void delegate5(System.IntPtr p0);
|
||||
public delegate void delegate6(System.IntPtr p0, int p1);
|
||||
public delegate void delegate7(System.IntPtr p0, int p1, int p2);
|
||||
public delegate void delegate8(System.IntPtr p0);
|
||||
public delegate void delegate9(System.IntPtr p0, int p1);
|
||||
}
|
||||
|
||||
public struct Pointer<T>
|
||||
{
|
||||
public IntPtr Handle;
|
||||
public static implicit operator IntPtr(Pointer<T> value) { return value.Handle; }
|
||||
public static implicit operator Pointer<T>(IntPtr value) { return new Pointer<T>(value); }
|
||||
|
||||
public Pointer(IntPtr handle)
|
||||
{
|
||||
Handle = handle;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is Pointer<T> && this == (Pointer<T>)obj;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (int)Handle;
|
||||
}
|
||||
|
||||
public static bool operator ==(Pointer<T> p1, Pointer<T> p2)
|
||||
{
|
||||
return p1.Handle == p2.Handle;
|
||||
}
|
||||
|
||||
public static bool operator !=(Pointer<T> p1, Pointer<T> p2)
|
||||
{
|
||||
return p1.Handle != p2.Handle;
|
||||
}
|
||||
}
|
||||
public struct ArrayOf<T> { }
|
||||
public struct ConstArrayOf<T> { }
|
||||
public class CTypeDetailsAttribute : Attribute
|
||||
{
|
||||
public CTypeDetailsAttribute(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Value { get; set; }
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<RuntimeIdentifiers>win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
@ -1,19 +1,20 @@
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Backends.SoundIo.Native;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Integration;
|
||||
using Ryujinx.Memory;
|
||||
using SoundIOSharp;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo
|
||||
{
|
||||
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
||||
{
|
||||
private readonly SoundIO _audioContext;
|
||||
private readonly SoundIODevice _audioDevice;
|
||||
private readonly SoundIoContext _audioContext;
|
||||
private readonly SoundIoDeviceContext _audioDevice;
|
||||
private readonly ManualResetEvent _updateRequiredEvent;
|
||||
private readonly ManualResetEvent _pauseEvent;
|
||||
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
||||
@ -21,7 +22,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
|
||||
public SoundIoHardwareDeviceDriver()
|
||||
{
|
||||
_audioContext = new SoundIO();
|
||||
_audioContext = SoundIoContext.Create();
|
||||
_updateRequiredEvent = new ManualResetEvent(false);
|
||||
_pauseEvent = new ManualResetEvent(true);
|
||||
_sessions = new ConcurrentDictionary<SoundIoHardwareDeviceSession, byte>();
|
||||
@ -29,24 +30,23 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
_audioContext.Connect();
|
||||
_audioContext.FlushEvents();
|
||||
|
||||
_audioDevice = FindNonRawDefaultAudioDevice(_audioContext, true);
|
||||
_audioDevice = FindValidAudioDevice(_audioContext, true);
|
||||
}
|
||||
|
||||
public static bool IsSupported => IsSupportedInternal();
|
||||
|
||||
private static bool IsSupportedInternal()
|
||||
{
|
||||
SoundIO context = null;
|
||||
SoundIODevice device = null;
|
||||
SoundIOOutStream stream = null;
|
||||
SoundIoContext context = null;
|
||||
SoundIoDeviceContext device = null;
|
||||
SoundIoOutStreamContext stream = null;
|
||||
|
||||
bool backendDisconnected = false;
|
||||
|
||||
try
|
||||
{
|
||||
context = new SoundIO();
|
||||
|
||||
context.OnBackendDisconnect = (i) =>
|
||||
context = SoundIoContext.Create();
|
||||
context.OnBackendDisconnect = err =>
|
||||
{
|
||||
backendDisconnected = true;
|
||||
};
|
||||
@ -64,7 +64,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
return false;
|
||||
}
|
||||
|
||||
device = FindNonRawDefaultAudioDevice(context);
|
||||
device = FindValidAudioDevice(context);
|
||||
|
||||
if (device == null || backendDisconnected)
|
||||
{
|
||||
@ -86,30 +86,23 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (stream != null)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
context.Dispose();
|
||||
}
|
||||
stream?.Dispose();
|
||||
context?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static SoundIODevice FindNonRawDefaultAudioDevice(SoundIO audioContext, bool fallback = false)
|
||||
private static SoundIoDeviceContext FindValidAudioDevice(SoundIoContext audioContext, bool fallback = false)
|
||||
{
|
||||
SoundIODevice defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
|
||||
SoundIoDeviceContext defaultAudioDevice = audioContext.GetOutputDevice(audioContext.DefaultOutputDeviceIndex);
|
||||
|
||||
if (!defaultAudioDevice.IsRaw)
|
||||
{
|
||||
return defaultAudioDevice;
|
||||
}
|
||||
|
||||
for (int i = 0; i < audioContext.BackendCount; i++)
|
||||
for (int i = 0; i < audioContext.OutputDeviceCount; i++)
|
||||
{
|
||||
SoundIODevice audioDevice = audioContext.GetOutputDevice(i);
|
||||
SoundIoDeviceContext audioDevice = audioContext.GetOutputDevice(i);
|
||||
|
||||
if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
|
||||
{
|
||||
@ -161,22 +154,22 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
return _sessions.TryRemove(session, out _);
|
||||
}
|
||||
|
||||
public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
|
||||
public static SoundIoFormat GetSoundIoFormat(SampleFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
SampleFormat.PcmInt8 => SoundIOFormat.S8,
|
||||
SampleFormat.PcmInt16 => SoundIOFormat.S16LE,
|
||||
SampleFormat.PcmInt24 => SoundIOFormat.S24LE,
|
||||
SampleFormat.PcmInt32 => SoundIOFormat.S32LE,
|
||||
SampleFormat.PcmFloat => SoundIOFormat.Float32LE,
|
||||
SampleFormat.PcmInt8 => SoundIoFormat.S8,
|
||||
SampleFormat.PcmInt16 => SoundIoFormat.S16LE,
|
||||
SampleFormat.PcmInt24 => SoundIoFormat.S24LE,
|
||||
SampleFormat.PcmInt32 => SoundIoFormat.S32LE,
|
||||
SampleFormat.PcmFloat => SoundIoFormat.Float32LE,
|
||||
_ => throw new ArgumentException ($"Unsupported sample format {format}"),
|
||||
};
|
||||
}
|
||||
|
||||
internal SoundIOOutStream OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
|
||||
internal SoundIoOutStreamContext OpenStream(SampleFormat requestedSampleFormat, uint requestedSampleRate, uint requestedChannelCount)
|
||||
{
|
||||
SoundIOFormat driverSampleFormat = GetSoundIoFormat(requestedSampleFormat);
|
||||
SoundIoFormat driverSampleFormat = GetSoundIoFormat(requestedSampleFormat);
|
||||
|
||||
if (!_audioDevice.SupportsSampleRate((int)requestedSampleRate))
|
||||
{
|
||||
@ -193,10 +186,10 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
throw new ArgumentException($"This sound device does not support channel count {requestedChannelCount}");
|
||||
}
|
||||
|
||||
SoundIOOutStream result = _audioDevice.CreateOutStream();
|
||||
SoundIoOutStreamContext result = _audioDevice.CreateOutStream();
|
||||
|
||||
result.Name = "Ryujinx";
|
||||
result.Layout = SoundIOChannelLayout.GetDefault((int)requestedChannelCount);
|
||||
result.Layout = SoundIoChannelLayout.GetDefaultValue((int)requestedChannelCount);
|
||||
result.Format = driverSampleFormat;
|
||||
result.SampleRate = (int)requestedSampleRate;
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Backends.SoundIo.Native;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Memory;
|
||||
using SoundIOSharp;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.SoundIo
|
||||
{
|
||||
@ -13,7 +14,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
{
|
||||
private SoundIoHardwareDeviceDriver _driver;
|
||||
private ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers;
|
||||
private SoundIOOutStream _outputStream;
|
||||
private SoundIoOutStreamContext _outputStream;
|
||||
private DynamicRingBuffer _ringBuffer;
|
||||
private ulong _playedSampleCount;
|
||||
private ManualResetEvent _updateRequiredEvent;
|
||||
@ -106,9 +107,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
return;
|
||||
}
|
||||
|
||||
SoundIOChannelAreas areas = _outputStream.BeginWrite(ref frameCount);
|
||||
Span<SoundIoChannelArea> areas = _outputStream.BeginWrite(ref frameCount);
|
||||
|
||||
int channelCount = areas.ChannelCount;
|
||||
int channelCount = areas.Length;
|
||||
|
||||
byte[] samples = new byte[frameCount * bytesPerFrame];
|
||||
|
||||
@ -117,12 +118,12 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
// This is a huge ugly block of code, but we save
|
||||
// a significant amount of time over the generic
|
||||
// loop that handles other channel counts.
|
||||
// TODO: Is this still right in 2021?
|
||||
// TODO: Is this still right in 2022?
|
||||
|
||||
// Mono
|
||||
if (channelCount == 1)
|
||||
{
|
||||
SoundIOChannelArea area = areas.GetArea(0);
|
||||
ref SoundIoChannelArea area = ref areas[0];
|
||||
|
||||
fixed (byte* srcptr = samples)
|
||||
{
|
||||
@ -167,8 +168,8 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
// Stereo
|
||||
else if (channelCount == 2)
|
||||
{
|
||||
SoundIOChannelArea area1 = areas.GetArea(0);
|
||||
SoundIOChannelArea area2 = areas.GetArea(1);
|
||||
ref SoundIoChannelArea area1 = ref areas[0];
|
||||
ref SoundIoChannelArea area2 = ref areas[1];
|
||||
|
||||
fixed (byte* srcptr = samples)
|
||||
{
|
||||
@ -233,12 +234,12 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
// Surround
|
||||
else if (channelCount == 6)
|
||||
{
|
||||
SoundIOChannelArea area1 = areas.GetArea(0);
|
||||
SoundIOChannelArea area2 = areas.GetArea(1);
|
||||
SoundIOChannelArea area3 = areas.GetArea(2);
|
||||
SoundIOChannelArea area4 = areas.GetArea(3);
|
||||
SoundIOChannelArea area5 = areas.GetArea(4);
|
||||
SoundIOChannelArea area6 = areas.GetArea(5);
|
||||
ref SoundIoChannelArea area1 = ref areas[0];
|
||||
ref SoundIoChannelArea area2 = ref areas[1];
|
||||
ref SoundIoChannelArea area3 = ref areas[2];
|
||||
ref SoundIoChannelArea area4 = ref areas[3];
|
||||
ref SoundIoChannelArea area5 = ref areas[4];
|
||||
ref SoundIoChannelArea area6 = ref areas[5];
|
||||
|
||||
fixed (byte* srcptr = samples)
|
||||
{
|
||||
@ -367,24 +368,18 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
||||
// Every other channel count
|
||||
else
|
||||
{
|
||||
SoundIOChannelArea[] channels = new SoundIOChannelArea[channelCount];
|
||||
|
||||
// Obtain the channel area for each channel
|
||||
for (int i = 0; i < channelCount; i++)
|
||||
{
|
||||
channels[i] = areas.GetArea(i);
|
||||
}
|
||||
|
||||
fixed (byte* srcptr = samples)
|
||||
{
|
||||
for (int frame = 0; frame < frameCount; frame++)
|
||||
for (int channel = 0; channel < areas.ChannelCount; channel++)
|
||||
{
|
||||
for (int channel = 0; channel < areas.Length; channel++)
|
||||
{
|
||||
// Copy channel by channel, frame by frame. This is slow!
|
||||
Unsafe.CopyBlockUnaligned((byte*)channels[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample);
|
||||
Unsafe.CopyBlockUnaligned((byte*)areas[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample);
|
||||
|
||||
channels[channel].Pointer += channels[channel].Step;
|
||||
areas[channel].Pointer += areas[channel].Step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,40 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
};
|
||||
}
|
||||
|
||||
private SampleFormat SelectHardwareSampleFormat(SampleFormat targetSampleFormat)
|
||||
{
|
||||
if (_realDriver.SupportsSampleFormat(targetSampleFormat))
|
||||
{
|
||||
return targetSampleFormat;
|
||||
}
|
||||
|
||||
// Attempt conversion from PCM16.
|
||||
if (targetSampleFormat == SampleFormat.PcmInt16)
|
||||
{
|
||||
// Prefer PCM32 if we need to convert.
|
||||
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt32))
|
||||
{
|
||||
return SampleFormat.PcmInt32;
|
||||
}
|
||||
|
||||
// If not supported, PCM float provides the best quality with a cost lower than PCM24.
|
||||
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmFloat))
|
||||
{
|
||||
return SampleFormat.PcmFloat;
|
||||
}
|
||||
|
||||
// TODO: Implement PCM24 conversion.
|
||||
|
||||
// If nothing is truly supported, attempt PCM8 at the cost of loosing quality.
|
||||
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt8))
|
||||
{
|
||||
return SampleFormat.PcmInt8;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException("No valid sample format configuration found!");
|
||||
}
|
||||
|
||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||
{
|
||||
if (channelCount == 0)
|
||||
@ -77,15 +111,26 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
|
||||
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
||||
|
||||
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, sampleFormat, sampleRate, hardwareChannelCount, volume);
|
||||
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount, volume);
|
||||
|
||||
if (hardwareChannelCount == channelCount)
|
||||
if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat)
|
||||
{
|
||||
return realSession;
|
||||
}
|
||||
|
||||
if (hardwareSampleFormat != sampleFormat)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Audio, $"{sampleFormat} isn't supported by the audio device, conversion to {hardwareSampleFormat} will happen.");
|
||||
|
||||
if (hardwareSampleFormat < sampleFormat)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Audio, $"{hardwareSampleFormat} has lower quality than {sampleFormat}, expect some loss in audio fidelity.");
|
||||
}
|
||||
}
|
||||
|
||||
if (direction == Direction.Input)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Audio, $"The selected audio backend doesn't support the requested audio input configuration, fallback to dummy...");
|
||||
@ -103,7 +148,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
}
|
||||
|
||||
// If we need to do post processing before sending to the hardware device, wrap around it.
|
||||
return new CompatLayerHardwareDeviceSession(realSessionOutputBase, channelCount);
|
||||
return new CompatLayerHardwareDeviceSession(realSessionOutputBase, sampleFormat, channelCount);
|
||||
}
|
||||
|
||||
public bool SupportsChannelCount(uint channelCount)
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Audio.Renderer.Dsp;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@ -8,11 +9,13 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||
{
|
||||
private HardwareDeviceSessionOutputBase _realSession;
|
||||
private SampleFormat _userSampleFormat;
|
||||
private uint _userChannelCount;
|
||||
|
||||
public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
|
||||
public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, SampleFormat userSampleFormat, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
|
||||
{
|
||||
_realSession = realSession;
|
||||
_userSampleFormat = userSampleFormat;
|
||||
_userChannelCount = userChannelCount;
|
||||
}
|
||||
|
||||
@ -38,53 +41,86 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
||||
|
||||
public override void QueueBuffer(AudioBuffer buffer)
|
||||
{
|
||||
SampleFormat realSampleFormat = _realSession.RequestedSampleFormat;
|
||||
|
||||
if (_userSampleFormat != realSampleFormat)
|
||||
{
|
||||
if (_userSampleFormat != SampleFormat.PcmInt16)
|
||||
{
|
||||
throw new NotImplementedException("Converting formats other than PCM16 is not supported.");
|
||||
}
|
||||
|
||||
int userSampleCount = buffer.Data.Length / BackendHelper.GetSampleSize(_userSampleFormat);
|
||||
|
||||
ReadOnlySpan<short> samples = MemoryMarshal.Cast<byte, short>(buffer.Data);
|
||||
byte[] convertedSamples = new byte[BackendHelper.GetSampleSize(realSampleFormat) * userSampleCount];
|
||||
|
||||
switch (realSampleFormat)
|
||||
{
|
||||
case SampleFormat.PcmInt8:
|
||||
PcmHelper.Convert(MemoryMarshal.Cast<byte, sbyte>(convertedSamples), samples);
|
||||
break;
|
||||
case SampleFormat.PcmInt32:
|
||||
PcmHelper.Convert(MemoryMarshal.Cast<byte, int>(convertedSamples), samples);
|
||||
break;
|
||||
case SampleFormat.PcmFloat:
|
||||
PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast<byte, float>(convertedSamples), samples);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Sample format conversion from {_userSampleFormat} to {realSampleFormat} not implemented.");
|
||||
}
|
||||
|
||||
buffer.Data = convertedSamples;
|
||||
}
|
||||
|
||||
_realSession.QueueBuffer(buffer);
|
||||
}
|
||||
|
||||
public override bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
|
||||
{
|
||||
if (RequestedSampleFormat != SampleFormat.PcmInt16)
|
||||
{
|
||||
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
|
||||
}
|
||||
|
||||
if (samples == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
short[] downmixedBufferPCM16;
|
||||
|
||||
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
|
||||
|
||||
if (_userChannelCount == 6)
|
||||
if (_userChannelCount != _realSession.RequestedChannelCount)
|
||||
{
|
||||
downmixedBufferPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
|
||||
|
||||
if (_realSession.RequestedChannelCount == 1)
|
||||
if (_userSampleFormat != SampleFormat.PcmInt16)
|
||||
{
|
||||
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(downmixedBufferPCM16);
|
||||
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
|
||||
}
|
||||
}
|
||||
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
|
||||
{
|
||||
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
|
||||
}
|
||||
|
||||
byte[] downmixedBuffer = MemoryMarshal.Cast<short, byte>(downmixedBufferPCM16).ToArray();
|
||||
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
|
||||
|
||||
if (_userChannelCount == 6)
|
||||
{
|
||||
samplesPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
|
||||
|
||||
if (_realSession.RequestedChannelCount == 1)
|
||||
{
|
||||
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
|
||||
}
|
||||
}
|
||||
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
|
||||
{
|
||||
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
|
||||
}
|
||||
|
||||
samples = MemoryMarshal.Cast<short, byte>(samplesPCM16).ToArray();
|
||||
}
|
||||
|
||||
AudioBuffer fakeBuffer = new AudioBuffer
|
||||
{
|
||||
BufferTag = buffer.BufferTag,
|
||||
DataPointer = buffer.DataPointer,
|
||||
DataSize = (ulong)downmixedBuffer.Length
|
||||
DataSize = (ulong)samples.Length
|
||||
};
|
||||
|
||||
bool result = _realSession.RegisterBuffer(fakeBuffer, downmixedBuffer);
|
||||
bool result = _realSession.RegisterBuffer(fakeBuffer, samples);
|
||||
|
||||
if (result)
|
||||
{
|
||||
|
@ -48,6 +48,11 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||
/// <summary>
|
||||
/// Effect to capture mixes (via auxiliary buffers).
|
||||
/// </summary>
|
||||
CaptureBuffer
|
||||
CaptureBuffer,
|
||||
|
||||
/// <summary>
|
||||
/// Effect applying a compressor filter (DRC).
|
||||
/// </summary>
|
||||
Compressor,
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||
Reverb3d,
|
||||
PcmFloat,
|
||||
Limiter,
|
||||
CaptureBuffer
|
||||
CaptureBuffer,
|
||||
Compressor
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
ulong alignedOffset = BitUtils.AlignUp(Offset, align);
|
||||
ulong alignedOffset = BitUtils.AlignUp<ulong>(Offset, (ulong)align);
|
||||
|
||||
if (alignedOffset + size <= (ulong)BackingMemory.Length)
|
||||
{
|
||||
@ -55,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
||||
|
||||
public static ulong GetTargetSize<T>(ulong currentSize, ulong count, int align) where T : unmanaged
|
||||
{
|
||||
return BitUtils.AlignUp(currentSize, align) + (ulong)Unsafe.SizeOf<T>() * count;
|
||||
return BitUtils.AlignUp<ulong>(currentSize, (ulong)align) + (ulong)Unsafe.SizeOf<T>() * count;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -71,6 +72,19 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
return (short)value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
||||
{
|
||||
if ((uint)index > (uint)coefficients.Length)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return coefficients[index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
|
||||
{
|
||||
@ -84,8 +98,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
|
||||
short history0 = loopContext.History0;
|
||||
short history1 = loopContext.History1;
|
||||
short coefficient0 = coefficients[coefficientIndex * 2 + 0];
|
||||
short coefficient1 = coefficients[coefficientIndex * 2 + 1];
|
||||
short coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 0);
|
||||
short coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
|
||||
|
||||
int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
|
||||
int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
|
||||
@ -109,8 +123,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
|
||||
coefficientIndex = (byte)((predScale >> 4) & 0xF);
|
||||
|
||||
coefficient0 = coefficients[coefficientIndex * 2 + 0];
|
||||
coefficient1 = coefficients[coefficientIndex * 2 + 1];
|
||||
coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2);
|
||||
coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
|
||||
|
||||
nibbles += 2;
|
||||
|
||||
|
@ -116,6 +116,11 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
||||
};
|
||||
}
|
||||
|
||||
public bool HasRemainingCommands(int sessionId)
|
||||
{
|
||||
return _sessionCommandList[sessionId] != null;
|
||||
}
|
||||
|
||||
public void Signal()
|
||||
{
|
||||
_mailbox.SendMessage(MailboxMessage.RenderStart);
|
||||
|
@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.AdpcmDataSourceVersion1;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ushort OutputBufferIndex { get; }
|
||||
public uint SampleRate { get; }
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.AuxiliaryBuffer;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public uint InputBufferIndex { get; }
|
||||
public uint OutputBufferIndex { get; }
|
||||
|
@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.BiquadFilter;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public Memory<BiquadFilterState> BiquadFilterState { get; }
|
||||
public int InputBufferIndex { get; }
|
||||
|
@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.CaptureBuffer;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public uint InputBufferIndex { get; }
|
||||
|
||||
|
@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.CircularBufferSink;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ushort[] Input { get; }
|
||||
public uint InputCount { get; }
|
||||
|
@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.ClearMixBuffer;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ClearMixBufferCommand(int nodeId)
|
||||
{
|
||||
|
@ -31,6 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
LimiterVersion1,
|
||||
LimiterVersion2,
|
||||
GroupedBiquadFilter,
|
||||
CaptureBuffer
|
||||
CaptureBuffer,
|
||||
Compressor
|
||||
}
|
||||
}
|
173
Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
Normal file
173
Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
Normal file
@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
public class CompressorCommand : ICommand
|
||||
{
|
||||
private const int FixedPointPrecision = 15;
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
public int NodeId { get; }
|
||||
|
||||
public CommandType CommandType => CommandType.Compressor;
|
||||
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public CompressorParameter Parameter => _parameter;
|
||||
public Memory<CompressorState> State { get; }
|
||||
public ushort[] OutputBufferIndices { get; }
|
||||
public ushort[] InputBufferIndices { get; }
|
||||
public bool IsEffectEnabled { get; }
|
||||
|
||||
private CompressorParameter _parameter;
|
||||
|
||||
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
_parameter = parameter;
|
||||
State = state;
|
||||
|
||||
IsEffectEnabled = isEnabled;
|
||||
|
||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Process(CommandList context)
|
||||
{
|
||||
ref CompressorState state = ref State.Span[0];
|
||||
|
||||
if (IsEffectEnabled)
|
||||
{
|
||||
if (_parameter.Status == Server.Effect.UsageState.Invalid)
|
||||
{
|
||||
state = new CompressorState(ref _parameter);
|
||||
}
|
||||
else if (_parameter.Status == Server.Effect.UsageState.New)
|
||||
{
|
||||
state.UpdateParameter(ref _parameter);
|
||||
}
|
||||
}
|
||||
|
||||
ProcessCompressor(context, ref state);
|
||||
}
|
||||
|
||||
private unsafe void ProcessCompressor(CommandList context, ref CompressorState state)
|
||||
{
|
||||
Debug.Assert(_parameter.IsChannelCountValid());
|
||||
|
||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||
{
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
|
||||
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
||||
float unknown4 = state.Unknown4;
|
||||
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
||||
float previousCompressionEmaAlpha = state.PreviousCompressionEmaAlpha;
|
||||
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
||||
}
|
||||
|
||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||
{
|
||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||
}
|
||||
|
||||
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
||||
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
||||
float z = 0.0f;
|
||||
|
||||
bool unknown10OutOfRange = false;
|
||||
|
||||
if (newMean < 1.0e-10f)
|
||||
{
|
||||
z = 1.0f;
|
||||
|
||||
unknown10OutOfRange = state.Unknown10 < -100.0f;
|
||||
}
|
||||
|
||||
if (y >= state.Unknown10 || unknown10OutOfRange)
|
||||
{
|
||||
float tmpGain;
|
||||
|
||||
if (y >= state.Unknown14)
|
||||
{
|
||||
tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
|
||||
}
|
||||
|
||||
z = FloatingPointHelper.DecibelToLinearExtended(tmpGain);
|
||||
}
|
||||
|
||||
float unknown4New = z;
|
||||
float compressionEmaAlpha;
|
||||
|
||||
if ((unknown4 - z) <= 0.08f)
|
||||
{
|
||||
compressionEmaAlpha = Parameter.ReleaseCoefficient;
|
||||
|
||||
if ((unknown4 - z) >= -0.08f)
|
||||
{
|
||||
if (MathF.Abs(compressionGainAverage.Read() - z) >= 0.001f)
|
||||
{
|
||||
unknown4New = unknown4;
|
||||
}
|
||||
|
||||
compressionEmaAlpha = previousCompressionEmaAlpha;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
compressionEmaAlpha = Parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
|
||||
|
||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
|
||||
}
|
||||
|
||||
unknown4 = unknown4New;
|
||||
previousCompressionEmaAlpha = compressionEmaAlpha;
|
||||
}
|
||||
|
||||
state.InputMovingAverage = inputMovingAverage;
|
||||
state.Unknown4 = unknown4;
|
||||
state.CompressionGainAverage = compressionGainAverage;
|
||||
state.PreviousCompressionEmaAlpha = previousCompressionEmaAlpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
{
|
||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||
{
|
||||
context.CopyBuffer(OutputBufferIndices[i], InputBufferIndices[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.CopyMixBuffer;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ushort InputBufferIndex { get; }
|
||||
public ushort OutputBufferIndex { get; }
|
||||
|
@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType { get; }
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public ushort OutputBufferIndex { get; }
|
||||
public uint SampleRate { get; }
|
||||
|
@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.Delay;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public DelayParameter Parameter => _parameter;
|
||||
public Memory<DelayState> State { get; }
|
||||
@ -49,15 +49,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||
}
|
||||
|
||||
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
|
||||
// TODO: Update delay processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
|
||||
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
|
||||
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices);
|
||||
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices);
|
||||
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount)
|
||||
{
|
||||
const ushort channelCount = 1;
|
||||
|
||||
float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
|
||||
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
||||
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
||||
@ -70,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
float temp = input * inGain + delayLineValue * feedbackGain;
|
||||
|
||||
state.UpdateLowPassFilter(ref temp, 1);
|
||||
state.UpdateLowPassFilter(ref temp, channelCount);
|
||||
|
||||
outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
|
||||
}
|
||||
@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
Y = state.DelayLines[1].Read(),
|
||||
};
|
||||
|
||||
Vector2 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
||||
Vector2 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
|
||||
|
||||
state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount);
|
||||
|
||||
@ -148,7 +148,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
W = state.DelayLines[3].Read()
|
||||
};
|
||||
|
||||
Vector4 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
||||
Vector4 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
|
||||
|
||||
state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount);
|
||||
|
||||
@ -171,12 +171,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||
|
||||
Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain,
|
||||
0.0f, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
|
||||
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f,
|
||||
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f,
|
||||
delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain);
|
||||
Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
|
||||
0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
|
||||
delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
|
||||
delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
|
||||
0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
@ -200,7 +200,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
U = state.DelayLines[5].Read()
|
||||
};
|
||||
|
||||
Vector6 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
||||
Vector6 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
|
||||
|
||||
state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount);
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CommandType CommandType => CommandType.DepopForMixBuffers;
|
||||
|
||||
public ulong EstimatedProcessingTime { get; set; }
|
||||
public uint EstimatedProcessingTime { get; set; }
|
||||
|
||||
public uint MixBufferOffset { get; }
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user