Compare commits

...

43 Commits

Author SHA1 Message Date
09c9686498 misc: Use official names for NVDEC registers (#4192)
* misc: Uses official names for NVDEC registers

* Address gdkchan's comment

* Address comments
2023-01-02 14:48:46 +00:00
b6614c6ad5 chore: Update tests dependencies (#3978)
* chore: Update tests dependencies

* Apply TSR Berry suggestion to add a GC.SuppressFinalize in MemoryBlock.cs

* Ensure we wait for the test thread to be dead on PartialUnmap

* Use platform attribute for os specific tests

* Make P/Invoke methods private

* Downgrade NUnit3TestAdapter to 4.1.0

* test: Disable warning about platform compat for ThreadLocalMap()

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
2023-01-01 17:35:29 +01:00
b1d4b174a6 fix typo in left joycon sl binding (#4195) 2023-01-01 15:46:02 +01:00
2b23463daa Filter hidden game files from the Game List (#4051)
* Filter “._” files from the game list

* Filter all hidden files from the game list

* Fix style

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* merge OR expression into a pattern

* migrate from GetFiles/Directories to Enumerate

* Remove GetFilesInDirectory()

* Update Ryujinx.Ui.Common/App/ApplicationLibrary.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* add error handeling

* code cleanup

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 16:52:30 +01:00
9dfe81770a Use vector outputs for texture operations (#3939)
* Change AggregateType to include vector type counts

* Replace VariableType uses with AggregateType and delete VariableType

* Support new local vector types on SPIR-V and GLSL

* Start using vector outputs for texture operations

* Use vectors on more texture operations

* Use vector output for ImageLoad operations

* Replace all uses of single destination texture constructors with multi destination ones

* Update textureGatherOffsets replacement to split vector operations

* Shader cache version bump

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 16:09:34 +01:00
52c115a1f8 HLE: Add basic stubs to get Labo VR booting to title screen. (#4007)
* HLE: Add basic stubs to get Labo VR booting to title screen.

* Address code review

* Apply suggestions from code review (pt. 2)

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Apply suggestions from code review (pt. 3)

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Formatting: final batch?

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Address last? bit of formatting

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-29 15:57:35 +01:00
e20abbf9cc Vulkan: Don't flush commands when creating most sync (#4087)
* Vulkan: Don't flush commands when creating most sync

When the WaitForIdle method is called, we create sync as some internal GPU method may read back written buffer data. Some games randomly intersperse compute dispatch into their render passes, which result in this happening an unbounded number of times depending on how many times they run compute.

Creating sync in Vulkan is expensive, as we need to flush the current command buffer so that it can be waited on. We have a limited number of active command buffers due to how we track resource usage, so submitting too many command buffers will force us to wait for them to return to the pool.

This PR allows less "important" sync (things which are less likely to be waited on) to wait on a command buffer's result without submitting it, instead relying on AutoFlush or another, more important sync to flush it later on.

Because of the possibility of us waiting for a command buffer that hasn't submitted yet, any thread needs to be able to force the active command buffer to submit. The ability to do this has been added to the backend multithreading via an "Interrupt", though it is not supported without multithreading.

OpenGL drivers should already be doing something similar so they don't blow up when creating lots of sync, which is why this hasn't been a problem for these games over there.

Improves Vulkan performance on Xenoblade DE, Pokemon Scarlet/Violet, and Zelda BOTW (still another large issue here)

* Add strict argument

This is technically a separate concern from whether the sync is a host syncpoint.

* Remove _interrupted variable

* Actually wait for the invoke

This is required by AMD GPUs, and also may have caused some issues on other GPUs.

* Remove unused using.

* I don't know why it added these ones.

* Address Feedback

* Fix typo
2022-12-29 15:39:04 +01:00
76671d63d4 Ava GUI: Restructure Ryujinx.Ava (#4165)
* Restructure `Ryujinx.Ava`

* Stylistic consistency

* Update Ryujinx.Ava/UI/Controls/UserEditor.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/UserEditor.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/UserSelector.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Controls/SaveManager.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/ViewModels/UserProfileViewModel.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Helpers/EmbeddedWindow.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Fix redundancies

* Remove redunancies

* Add back elses

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2022-12-29 15:24:05 +01:00
3d1a0bf374 [Headless] Fix for not receiving any SDL events on Linux (#4182)
* headless: Fix SDL events not working

* Add nuget.config
2022-12-29 15:09:18 +01:00
c20f3fbebd haydn: Add support for PCMFloat, PCM32 and PCM8 conversions (#4100)
* haydn: Add support for PCMFloat, PCM32 and PCM8 conversions

This adds support in the compatibility layer for other sample format
than PCM16.

This should help extends compatibility with soundio on devices that
doesn't expose PCM16.

I ommited PCM24 conversion for now as it's not simplest of all.

* Address TSRBerry's comment

* Address comments

* Fix conversion issue and clean up saturation usage

* Revert saturation changes

* Address gdkchan's comment
2022-12-28 19:34:30 -03:00
0d3b82477e Use new ArgumentNullException and ObjectDisposedException throw-helper API (#4163) 2022-12-27 20:27:11 +01:00
470be03c2f GPU: Add fallback when 16-bit formats are not supported (#4108)
* Add conversion for 16 bit RGBA formats (not supported in Rosetta)

* Rebase fix

Rebase fix

* Forgot to remove this

* Fix RGBA16 format conversion

* Add RGBA4 -> RGBA8 conversion

* Handle host stride alignment

* Address Feedback Part 1

* Can't count

* Don't zero out rgb when alpha is 0

* Separate RGBA4 and 5-bit component formats

Not sure of a better way to name them...

* Add A1B5G5R5 conversion

* Put this in the right place.

* Make format naming consistent for capabilities

* Change method names
2022-12-26 15:50:27 -03:00
c963b3c804 Added Generic Math to BitUtils (#3929)
* Generic Math Update

Updated Several functions in Ryujinx.Common/Utilities/BitUtils to use generic math

* Updated BitUtil calls

* Removed Whitespace

* Switched decrement

* Fixed changed method calls.

The method calls were originally changed on accident due to me relying too much on intellisense doing stuff for me

* Update Ryujinx.Common/Utilities/BitUtils.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-26 14:11:05 +00:00
a4fdfb5f94 bsd::RecvFrom: verify output buffer size before writing socket address (#4135)
* bsd::RecvFrom: Ryujinx does not verify output buffer size before writing socket address

* Calculate the size of BsdSockAddr

* use bsdSockAddr variable

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* set errno to ENOMEM in case we can't write the address to memory

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-26 13:49:26 +00:00
37d27c4c99 Some minor cleanups and optimizations (#4174)
* Replace Array.Clear(x, 0, x.Length) with Array.Clear(x)

* Use DateTime.UnixEpoch field

* Replace SHA256.ComputeHash calls with static SHA256.HashData call

More performant and avoids the need to initialize a SHA256 instance.
2022-12-24 14:30:39 -03:00
f906eb06c2 Implement a software ETC2 texture decoder (#4121)
* Implement a software ETC2 texture decoder

* Fix output size calculation for non-2D textures

* Address PR feedback
2022-12-21 20:39:58 -03:00
219f63ff4e Fix CPU FCVTN instruction implementation (slow path) (#4159)
* Fix CPU FCVTN instruction implementation (slow path)

* PPTC version bump
2022-12-21 23:05:58 +00:00
1cca3e99ab GPU: Force rebind when pool changes (#4129) 2022-12-21 17:35:28 -03:00
Ccl
55a23e5ec8 make UI display correct content in Chinese (#4155) 2022-12-21 17:20:37 -03:00
479d1fd8b0 hle: Handle GPU profiler and debugger device path correctly (#4138)
This fix a warning on "慟哭そして…" by handling correctly the debug mode
flag.

When debug mode isn't enabled, opening /dev/nvhost-dbg-gpu or /dev/nvhost-prof-gpu should fail with a not implemented error code.

This implement this behaviour and also define stubbed interfaces for
completness.
2022-12-21 18:23:11 +00:00
cb70e7bb30 Fix DrawArrays vertex buffer size (#4141) 2022-12-21 19:08:12 +01:00
c200a7b7c6 ARMeilleure: Hash _data pointer instead of value for Operand (#4156)
I noticed a weirdly high cost for dictionary accesses from MarkLabel etc. Turns out that the hash code was always the same for labels, so the whole point of having a dictionary was missed and it was putting everything in the same bucket. I made it always hash the _data pointer as that's a good source of identifiable and "random" data.
2022-12-21 02:28:18 +01:00
6268170a10 fix sw kbd row collision (#4144) 2022-12-19 15:09:36 -03:00
ee0f9b03a4 Eliminate zero-extension moves in more cases on 32-bit games (#4140)
* Eliminate zero-extension moves in more cases on 32-bit games

* PPTC version bump

* Revert X86Optimizer changes
2022-12-19 14:45:58 -03:00
f93c5f006a Revert "ARMeilleure: Add initial support for AVX512(EVEX encoding) (#3663)" (#4145)
This reverts commit 295fbd0542.
2022-12-18 20:21:10 -03:00
295fbd0542 ARMeilleure: Add initial support for AVX512(EVEX encoding) (#3663)
* ARMeilleure: Add AVX512{F,VL,DQ,BW} detection

Add `UseAvx512Ortho` and `UseAvx512OrthoFloat` optimization flags as
short-hands for `F+VL` and `F+VL+DQ`.

* ARMeilleure: Add initial support for EVEX instruction encoding

Does not implement rounding, or exception controls.

* ARMeilleure: Add `X86Vpternlogd`

Accelerates the vector-`Not` instruction.

* ARMeilleure: Add check for `OSXSAVE` for AVX{2,512}

* ARMeilleure: Add check for `XCR0` flags

Add XCR0 register checks for AVX and AVX512F, following the guidelines
from section 14.3 and 15.2 from the Intel Architecture Software
Developer's Manual.

* ARMeilleure: Increment InternalVersion

* ARMeilleure: Remove redundant `ReProtect` and `Dispose`, formatting

* ARMeilleure: Move XCR0 procedure to GetXcr0Eax

* ARMeilleure: Add `XCR0` to `FeatureInfo` structure

* ARMeilleure: Utilize `ReadOnlySpan` for Xcr0 assembly

Avoids an additional allocation

* ARMeilleure: Formatting fixes
2022-12-18 16:46:13 -03:00
d7310d7a1c hle: Fix wrong conversion in UserPresence.ToString (#4142)
This fixes an error from #3805 that caused a wrong conversion of ``AppKeyValueStorage`` to string.
As that information isn't really relevant without appropriate parsing, it was removed from ``ToString``.

This should get ride of "bell warning" in Mario Kart 8 when entering Time Trials.
2022-12-18 14:23:19 +00:00
8c50943a2e nuget: bump Microsoft.NET.Test.Sdk from 17.4.0 to 17.4.1 (#4137)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0 to 17.4.1.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0...v17.4.1)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-17 01:19:04 +01:00
ec4cd57ccf Implement another non-indexed draw method on GPU (#4123) 2022-12-16 12:06:38 -03:00
5a085cba0f GPU: Fix layered attachment write (#4131)
Fixes a regression caused by #4003 where the code that writes `_vtgWritesRtLayer` was removed, breaking the crowd in mario strikers.
2022-12-16 09:40:01 -03:00
1a1d33a018 ava: Fix invisible swkbd applet on Linux (#4130) 2022-12-16 02:40:55 +01:00
0fbcd630bc Replace DllImport usage with LibraryImport (#4084)
* Replace usage of `DllImport` with `LibraryImport`

* Mark methods as `partial`

* Marshalling

* More `partial` & marshalling

* More `partial` and marshalling

* More partial and marshalling

* Update GdiPlusHelper to LibraryImport

* Unicorn

* More Partial

* Marshal

* Specify EntryPoint

* Specify EntryPoint

* Change GlobalMemoryStatusEx to LibraryImport

* Change RegisterClassEx to LibraryImport

* Define EntryPoints

* Update Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Move return mashal

* Remove calling convention specification

* Remove calling conventions

* Update Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx/Modules/Updater/Updater.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Ava/Modules/Updater/Updater.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Mary-nyan <thog@protonmail.com>
2022-12-15 18:07:31 +01:00
f4d731ae20 Fix NRE when loading Vulkan shader cache with Vertex A shaders (#4124) 2022-12-15 17:52:12 +01:00
8ac53c66b4 Remove Half Conversion (#4106)
* Remove HalfConversion

* Update `CodeGenVersion`
2022-12-14 21:13:23 -03:00
0f50de72be Vulkan: enable VK_EXT_custom_border_color features (#4116)
* Vulkan: enable VK_EXT_custom_border_color features

radv only create the border color bo if this feature is enabled, so it crashed when creating samplers with custom border colors
Fixes #4072
Fixes #3993

* Address gdkchan's comment

Co-authored-by: Mary <mary@mary.zone>
2022-12-14 20:53:33 -03:00
df758eddd1 Bsd: Add support for dns_mitm (#4102)
* bsd: Add dns_mitm from Atmosphère

related AMS files:
- https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp
- https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp

* Remove debug logging and adjust redirect message

* Improve formatting

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Replace Initialize with instance property

* bsd: dns_mitm - Ignore empty lines

* bsd: Mark _mitmHostEntries as readonly

* bsd: Initialize Aliases when returning IpHostEntry

Fixes NullReferenceException

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-12 18:04:08 +01:00
5f32a8ed94 misc: Update to Ryujinx.Graphics.Nvdec.Dependencies 5.0.1-build13 (#4097)
Fix packaging issues on macOS related to an unsatisfied dependency on libX11
2022-12-12 16:19:46 +01:00
535fbec675 Use NuGet Central Package Management to manage package versions solution-wise (#4095) 2022-12-12 16:03:10 +01:00
6fe88115a3 misc: Some fixes to the updaters (#4092)
This was meant to be only an upgrade of how we set unix permission in
the updater to use .NET 7 new APIs, but I end up finding bugs along the
way.

Changelog:
- Remove direct usage of chmod to use File.SetUnixFileMode.
- Fix command line being broken when updating (#3744) but on
  Ryujinx.Ava.
- Makes Ryujinx.Ava updater fallback to Ryujinx executable if current
  name isn't found.
- Make permission setter function more generic.
2022-12-12 15:17:22 +01:00
475fa4d390 Fix "UI" abbreviation being miscapitalized (#4093) 2022-12-12 15:11:55 +01:00
edf7e628ca Use method overloads that support trimming. Mark some types to be trimming friendly (#4083)
* Use method overloads that support trimming. Mark some types to be trimming friendly

* Use generic version of marshalling method
2022-12-12 15:10:05 +01:00
ba5c0cf5d8 Bsd: Implement Select (#4017)
* bsd: Add gdkchan's Select implementation

Co-authored-by: TSRBerry <20988865+tsrberry@users.noreply.github.com>

* bsd: Fix Select() causing a crash with an ArgumentException

.NET Sockets have to be used for the Select() call

* bsd: Make Select more generic

* bsd: Adjust namespaces and remove unused imports

* bsd: Fix NullReferenceException in Select

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-12 14:59:31 +01:00
403e67d983 audio: Rewrite SoundIo bindings (#4088)
* audio: Rewrite SoundIo bindings

This rewrite SoundIo bindings to be safer and not a pedantic autogenerated mess.

* Address comments

* Switch DllImport to LibraryImport

* Address gdkchan's comment
2022-12-11 00:57:01 +01:00
352 changed files with 5298 additions and 4909 deletions

3
.gitignore vendored
View File

@ -125,6 +125,9 @@ ClientBin/
packages/*
*.config
# Include nuget.config
!nuget.config
# RIA/Silverlight projects
Generated_Code/

View File

@ -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);
}

View File

@ -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))
{

View File

@ -49,10 +49,7 @@ namespace ARMeilleure.Common
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return ref _countTable.GetValue(_index);
}

View File

@ -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)
{

View File

@ -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) */

View File

@ -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.");

View File

@ -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)

View File

@ -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)
{

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
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";

View File

@ -140,7 +140,7 @@ namespace ARMeilleure.Translation
}
}
Array.Clear(localDefs, 0, localDefs.Length);
Array.Clear(localDefs);
}
}

View File

@ -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
View 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>

View File

@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
<PackageReference Include="OpenTK.OpenAL" />
</ItemGroup>
<ItemGroup>

View 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);
}
}

View 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
}
}

View 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,
}
}

View 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);
}
}
}

View File

@ -0,0 +1,8 @@
namespace Ryujinx.Audio.Backends.SoundIo.Native
{
public enum SoundIoDeviceAim
{
SoundIoDeviceAimInput = 0,
SoundIoDeviceAimOutput = 1
}
}

View File

@ -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);
}
}
}

View 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,
}
}

View 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))) { }
}
}

View 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,
}
}

View 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);
}
}
}

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -1,13 +0,0 @@
namespace SoundIOSharp
{
public enum SoundIOBackend
{
None,
Jack,
PulseAudio,
Alsa,
CoreAudio,
Wasapi,
Dummy
}
}

View File

@ -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");
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -1,116 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace SoundIOSharp
{
public readonly 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);
}
}
}

View File

@ -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));
}
}
}

View File

@ -1,8 +0,0 @@
namespace SoundIOSharp
{
public enum SoundIODeviceAim // soundio.h (228, 6)
{
Input,
Output
}
}

View File

@ -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))) { }
}
}

View File

@ -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
}
}

View File

@ -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;
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -1,14 +0,0 @@
namespace SoundIOSharp
{
public readonly struct SoundIOSampleRateRange
{
internal SoundIOSampleRateRange(int min, int max)
{
Min = min;
Max = max;
}
public readonly int Min;
public readonly int Max;
}
}

View File

@ -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; }
}
}

View File

@ -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;

View File

@ -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;
}
}
}
}

View File

@ -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)

View File

@ -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)
{

View File

@ -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;
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp
@ -23,6 +24,44 @@ namespace Ryujinx.Audio.Renderer.Dsp
return GetCountToDecode(startSampleOffset, endSampleOffset, offset, count) * Unsafe.SizeOf<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ConvertSampleToPcmFloat(short sample)
{
return (float)sample / short.MaxValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ConvertSampleToPcmInt16(float sample)
{
return Saturate(sample * short.MaxValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TOutput ConvertSample<TInput, TOutput>(TInput value) where TInput: INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
{
TInput conversionRate = TInput.CreateSaturating(TOutput.MaxValue / TOutput.CreateSaturating(TInput.MaxValue));
return TOutput.CreateSaturating(value * conversionRate);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert<TInput, TOutput>(Span<TOutput> output, ReadOnlySpan<TInput> input) where TInput : INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
{
for (int i = 0; i < input.Length; i++)
{
output[i] = ConvertSample<TInput, TOutput>(input[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ConvertSampleToPcmFloat(Span<float> output, ReadOnlySpan<short> input)
{
for (int i = 0; i < input.Length; i++)
{
output[i] = ConvertSampleToPcmFloat(input[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Decode(Span<short> output, ReadOnlySpan<short> input, int startSampleOffset, int endSampleOffset, int channelIndex, int channelCount)
{
@ -53,7 +92,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
for (int i = 0; i < decodedCount; i++)
{
output[i] = (short)(input[i * channelCount + channelIndex] * short.MaxValue);
output[i] = ConvertSampleToPcmInt16(input[i * channelCount + channelIndex]);
}
return decodedCount;

View File

@ -167,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall;
}
_depopBuffer = workBufferAllocator.Allocate<float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
_depopBuffer = workBufferAllocator.Allocate<float>(BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
if (_depopBuffer.IsEmpty)
{
@ -772,7 +772,7 @@ namespace Ryujinx.Audio.Renderer.Server
size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
// Depop buffer
size = WorkBufferAllocator.GetTargetSize<float>(size, (ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
size = WorkBufferAllocator.GetTargetSize<float>(size, BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
// Voice
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment);
@ -804,10 +804,10 @@ namespace Ryujinx.Audio.Renderer.Server
{
ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
size += BitUtils.AlignUp(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
size += BitUtils.AlignUp<ulong>(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
}
return BitUtils.AlignUp(size, Constants.WorkBufferAlignment);
return BitUtils.AlignUp<ulong>(size, Constants.WorkBufferAlignment);
}
public ResultCode QuerySystemEvent(out IWritableEvent systemEvent)

View File

@ -4,6 +4,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sty="using:FluentAvalonia.Styling">
<Application.Styles>
<sty:FluentAvaloniaTheme UseSystemThemeOnWindows="False" />
<sty:FluentAvaloniaTheme PreferSystemTheme="False" />
</Application.Styles>
</Application>

View File

@ -5,8 +5,9 @@ using Avalonia.Styling;
using Avalonia.Threading;
using FluentAvalonia.Styling;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Configuration;

View File

@ -11,13 +11,14 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;

View File

@ -280,7 +280,7 @@
"ControllerSettingsRemoveProfileToolTip": "Remove Profile",
"ControllerSettingsSaveProfileToolTip": "Save Profile",
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
"MenuBarFileToolsHideUi": "Hide Ui",
"MenuBarFileToolsHideUi": "Hide UI",
"GameListContextMenuToggleFavorite": "Toggle Favorite",
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
"SettingsTabGeneralTheme": "Theme",

View File

@ -12,8 +12,9 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;

View File

@ -1,4 +1,4 @@
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Configuration;

View File

@ -3,12 +3,12 @@ using System.Runtime.Versioning;
using System.Runtime.InteropServices;
using Avalonia;
namespace Ryujinx.Ava.Ui.Helper
namespace Ryujinx.Ava.UI.Helper
{
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
[SupportedOSPlatform("macos")]
static class MetalHelper
static partial class MetalHelper
{
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
@ -100,28 +100,28 @@ namespace Ryujinx.Ava.Ui.Helper
// TODO
}
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr sel_registerName(byte* data);
[LibraryImport(LibObjCImport)]
private static unsafe partial IntPtr sel_registerName(byte* data);
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr objc_getClass(byte* data);
[LibraryImport(LibObjCImport)]
private static unsafe partial IntPtr objc_getClass(byte* data);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, double value);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
[DllImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
[LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
}
}

View File

@ -7,8 +7,7 @@ namespace Ryujinx.Ava.Input
{
internal static class AvaloniaKeyboardMappingHelper
{
private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count]
{
private static readonly AvaKey[] _keyMapping = {
// NOTE: Invalid
AvaKey.None,
@ -151,16 +150,16 @@ namespace Ryujinx.Ava.Input
static AvaloniaKeyboardMappingHelper()
{
var inputKeys = Enum.GetValues(typeof(Key));
var inputKeys = Enum.GetValues<Key>();
// NOTE: Avalonia.Input.Key is not contiguous and quite large, so use a dictionary instead of an array.
_avaKeyMapping = new Dictionary<AvaKey, Key>();
foreach (var key in inputKeys)
{
if (TryGetAvaKey((Key)key, out var index))
if (TryGetAvaKey(key, out var index))
{
_avaKeyMapping[index] = (Key)key;
_avaKeyMapping[index] = key;
}
}
}

View File

@ -7,10 +7,12 @@ using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json.Linq;
using Ryujinx.Ava;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -278,14 +280,15 @@ namespace Ryujinx.Modules
{
string ryuName = Path.GetFileName(Environment.ProcessPath);
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
if (!OperatingSystem.IsWindows())
if (!Path.Exists(ryuExe))
{
chmod(ryuExe, Convert.ToUInt32("0777", 8));
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
}
Process.Start(ryuExe, ryuArg);
SetFileExecutable(ryuExe);
Process.Start(ryuExe, CommandLineState.Arguments);
Environment.Exit(0);
}
@ -456,16 +459,19 @@ namespace Ryujinx.Modules
worker.Start();
}
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string path, uint mode);
private static void SetUnixPermissions()
private static void SetFileExecutable(string path)
{
string ryuBin = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
UnixFileMode.UserWrite |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows())
if (!OperatingSystem.IsWindows() && File.Exists(path))
{
chmod(ryuBin, 493);
File.SetUnixFileMode(path, ExecutableFileMode);
}
}
@ -586,7 +592,7 @@ namespace Ryujinx.Modules
Directory.Delete(UpdateDir, true);
SetUnixPermissions();
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
UpdateSuccessful = true;

View File

@ -1,12 +1,12 @@
using ARMeilleure.Translation.PTC;
using Avalonia;
using Avalonia.Threading;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.SystemInfo;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
@ -20,7 +20,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava
{
internal class Program
internal partial class Program
{
public static double WindowScaleFactor { get; set; }
public static double DesktopScaleFactor { get; set; } = 1.0;
@ -28,8 +28,8 @@ namespace Ryujinx.Ava
public static string ConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; }
[DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
private const uint MB_ICONWARNING = 0x30;

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
@ -18,31 +18,31 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.18" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
<PackageReference Include="Avalonia.Svg" Version="0.10.18" />
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageReference Include="DynamicData" Version="7.12.11" />
<PackageReference Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
<PackageReference Include="Avalonia" />
<PackageReference Include="Avalonia.Desktop" />
<PackageReference Include="Avalonia.Diagnostics" />
<PackageReference Include="Avalonia.Controls.DataGrid" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" />
<PackageReference Include="Avalonia.Svg" />
<PackageReference Include="Avalonia.Svg.Skia" />
<PackageReference Include="jp2masa.Avalonia.Flexbox" />
<PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" />
<PackageReference Include="XamlNameReferenceGenerator" />
<PackageReference Include="OpenTK.Core" Version="4.7.5" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build12" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageReference Include="SPB" Version="0.0.4-build28" />
<PackageReference Include="SharpZipLib" Version="1.4.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Silk.NET.Vulkan" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
<PackageReference Include="SixLabors.ImageSharp" />
<!--NOTE: DO NOT REMOVE, THIS IS REQUIRED AS A RESULT OF A TRIMMING ISSUE IN AVALONIA -->
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="System.Drawing.Common" />
</ItemGroup>
<ItemGroup>

View File

@ -2,8 +2,9 @@ using Avalonia.Controls;
using Avalonia.Threading;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
@ -11,7 +12,7 @@ using Ryujinx.HLE.Ui;
using System;
using System.Threading;
namespace Ryujinx.Ava.Ui.Applet
namespace Ryujinx.Ava.UI.Applet
{
internal class AvaHostUiHandler : IHostUiHandler
{

View File

@ -3,15 +3,16 @@ using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Threading;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui;
using System;
using System.Threading;
using HidKey = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.Ui.Applet
namespace Ryujinx.Ava.UI.Applet
{
class AvaloniaDynamicTextInputHandler : IDynamicTextInputHandler
{

View File

@ -1,9 +1,9 @@
using Avalonia.Media;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui;
using System;
namespace Ryujinx.Ava.Ui.Applet
namespace Ryujinx.Ava.UI.Applet
{
class AvaloniaHostUiTheme : IHostUiTheme
{

View File

@ -1,11 +1,10 @@
<Window
x:Class="Ryujinx.Ava.Ui.Applet.ErrorAppletWindow"
x:Class="Ryujinx.Ava.UI.Applet.ErrorAppletWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="{locale:Locale ErrorWindowTitle}"
Width="450"
Height="340"

View File

@ -3,10 +3,10 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Windows;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Applet
namespace Ryujinx.Ava.UI.Applet
{
internal partial class ErrorAppletWindow : StyleableWindow
{

View File

@ -1,10 +1,9 @@
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.SwkbdAppletDialog"
x:Class="Ryujinx.Ava.UI.Controls.SwkbdAppletDialog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Width="400"
mc:Ignorable="d"
Focusable="True">
@ -45,7 +44,7 @@
TextWrapping="Wrap" />
<TextBox
Name="Input"
Grid.Row="2"
Grid.Row="3"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"

View File

@ -6,12 +6,13 @@ using Avalonia.Media;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.HOS.Applets;
using System;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
internal partial class SwkbdAppletDialog : UserControl
{
@ -111,7 +112,7 @@ namespace Ryujinx.Ava.Ui.Controls
overlay.Position = window.PointToScreen(new Point());
await contentDialog.ShowAsync();
await contentDialog.ShowAsync(overlay);
contentDialog.Closed -= handler;
overlay.Close();
};

View File

@ -1,19 +1,19 @@
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.GameGridView"
x:Class="Ryujinx.Ava.UI.Controls.GameGridView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
Focusable="True">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
<MenuItem
Command="{Binding ToggleFavorite}"

View File

@ -4,11 +4,12 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using LibHac.Common;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common;
using System;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class GameGridView : UserControl
{

View File

@ -1,19 +1,18 @@
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.GameListView"
x:Class="Ryujinx.Ava.UI.Controls.GameListView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
d:DesignHeight="450"
d:DesignWidth="800"
mc:Ignorable="d"
Focusable="True">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
<MenuFlyout x:Key="GameContextMenu" Opened="MenuBase_OnMenuOpened">
<MenuItem
Command="{Binding ToggleFavorite}"

View File

@ -4,11 +4,12 @@ using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using LibHac.Common;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common;
using System;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class GameListView : UserControl
{

View File

@ -1,5 +1,5 @@
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.InputDialog"
x:Class="Ryujinx.Ava.UI.Controls.InputDialog"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

View File

@ -1,10 +1,11 @@
using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class InputDialog : UserControl
{

View File

@ -0,0 +1,16 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Controls.NavigationDialogHost"
Focusable="True">
<ui:Frame
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
x:Name="ContentFrame" />
</UserControl>

View File

@ -3,13 +3,13 @@ using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using LibHac;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class NavigationDialogHost : UserControl
{

View File

@ -0,0 +1,57 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.UI.Controls.ProfileImageSelectionDialog"
Focusable="True">
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Margin="5,10,5, 5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="70" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
FontWeight="Bold"
FontSize="18"
HorizontalAlignment="Center"
Grid.Row="1"
Text="{locale:Locale ProfileImageSelectionHeader}" />
<TextBlock
FontWeight="Bold"
Grid.Row="2"
Margin="10"
MaxWidth="400"
TextWrapping="Wrap"
HorizontalAlignment="Center"
TextAlignment="Center"
Text="{locale:Locale ProfileImageSelectionNote}" />
<StackPanel
Margin="5,0"
Spacing="10"
Grid.Row="4"
HorizontalAlignment="Center"
Orientation="Horizontal">
<Button
Name="Import"
Click="Import_OnClick"
Width="200">
<TextBlock Text="{locale:Locale ProfileImageSelectionImportImage}" />
</Button>
<Button
Name="SelectFirmwareImage"
IsEnabled="{Binding FirmwareFound}"
Click="SelectFirmwareImage_OnClick"
Width="200">
<TextBlock Text="{locale:Locale ProfileImageSelectionSelectAvatar}" />
</Button>
</StackPanel>
</Grid>
</UserControl>

View File

@ -4,15 +4,15 @@ using Avalonia.VisualTree;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Processing;
using System.IO;
using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class ProfileImageSelectionDialog : UserControl
{

View File

@ -0,0 +1,11 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Controls.RendererHost"
Focusable="True">
</UserControl>

View File

@ -1,13 +1,14 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common.Configuration;
using Silk.NET.Vulkan;
using SPB.Graphics.OpenGL;
using SPB.Windowing;
using System;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class RendererHost : UserControl, IDisposable
{

View File

@ -0,0 +1,175 @@
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:Ryujinx.Ava.UI.Models"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
Height="400"
Width="550"
x:Class="Ryujinx.Ava.UI.Controls.SaveManager"
Focusable="True">
<UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid
Grid.Row="0"
HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel
Spacing="10"
Orientation="Horizontal"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<Label
Content="{locale:Locale CommonSort}"
VerticalAlignment="Center" />
<ComboBox SelectedIndex="{Binding SortIndex}" Width="100">
<ComboBoxItem>
<Label
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
Content="{locale:Locale Name}" />
</ComboBoxItem>
<ComboBoxItem>
<Label
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
Content="{locale:Locale Size}" />
</ComboBoxItem>
</ComboBox>
<ComboBox SelectedIndex="{Binding OrderIndex}" Width="150">
<ComboBoxItem>
<Label
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
Content="{locale:Locale OrderAscending}" />
</ComboBoxItem>
<ComboBoxItem>
<Label
VerticalAlignment="Center"
HorizontalContentAlignment="Left"
Content="{locale:Locale Descending}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<Grid
Grid.Column="1"
HorizontalAlignment="Stretch"
Margin="10,0, 0, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label
Content="{locale:Locale Search}"
VerticalAlignment="Center"/>
<TextBox
Margin="5,0,0,0"
Grid.Column="1"
HorizontalAlignment="Stretch"
Text="{Binding Search}"/>
</Grid>
</Grid>
<Border
Grid.Row="1"
Margin="0,5"
BorderThickness="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ListBox
Name="SaveList"
Items="{Binding View}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="models:SaveModel">
<Grid HorizontalAlignment="Stretch" Margin="0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<Border
Height="42"
Margin="2"
Width="42"
Padding="10"
IsVisible="{Binding !InGameList}">
<ui:SymbolIcon
Symbol="Help"
FontSize="30"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<Image
IsVisible="{Binding InGameList}"
Margin="2"
Width="42"
Height="42"
Source="{Binding Icon,
Converter={StaticResource ByteImage}}" />
<TextBlock
MaxLines="3"
Width="320"
Margin="5"
TextWrapping="Wrap"
Text="{Binding Title}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel
Grid.Column="1"
Spacing="10"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Label
Content="{Binding SizeString}"
IsVisible="{Binding SizeAvailable}"
VerticalAlignment="Center"
HorizontalAlignment="Right" />
<Button
VerticalAlignment="Center"
HorizontalAlignment="Right"
Padding="10"
MinWidth="0"
MinHeight="0"
Name="OpenLocation"
Command="{Binding OpenLocation}">
<ui:SymbolIcon
Symbol="OpenFolder"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Button>
<Button
VerticalAlignment="Center"
HorizontalAlignment="Right"
Padding="10"
MinWidth="0"
MinHeight="0"
Name="Delete"
Command="{Binding Delete}">
<ui:SymbolIcon
Symbol="Delete"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Button>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Border>
</Grid>
</UserControl>

View File

@ -7,16 +7,16 @@ using LibHac.Fs;
using LibHac.Fs.Shim;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class SaveManager : UserControl
{

View File

@ -1,10 +1,9 @@
<Window
x:Class="Ryujinx.Ava.Ui.Controls.UpdateWaitWindow"
x:Class="Ryujinx.Ava.UI.Controls.UpdateWaitWindow"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
Title="Ryujinx - Waiting"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterOwner"

View File

@ -1,7 +1,7 @@
using Avalonia.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Windows;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class UpdateWaitWindow : StyleableWindow
{

View File

@ -1,21 +1,18 @@
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.UserEditor"
x:Class="Ryujinx.Ava.UI.Controls.UserEditor"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:Ryujinx.Ava.Ui.Models"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
Margin="0"
MinWidth="500"
Padding="0"
mc:Ignorable="d"
Focusable="True">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Grid Margin="0">
<Grid.ColumnDefinitions>
@ -43,13 +40,13 @@
Margin="5"
HorizontalAlignment="Stretch"
Click="ChangePictureButton_Click"
Content="{Locale:Locale UserProfilesChangeProfileImage}" />
Content="{locale:Locale UserProfilesChangeProfileImage}" />
<Button
Name="AddPictureButton"
Margin="5"
HorizontalAlignment="Stretch"
Click="ChangePictureButton_Click"
Content="{Locale:Locale UserProfilesSetProfileImage}" />
Content="{locale:Locale UserProfilesSetProfileImage}" />
</StackPanel>
<StackPanel
Grid.Row="0"
@ -58,14 +55,14 @@
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Text="{Locale:Locale UserProfilesName}" />
<TextBlock Text="{locale:Locale UserProfilesName}" />
<TextBox
Name="NameBox"
Width="300"
HorizontalAlignment="Stretch"
MaxLength="{Binding MaxProfileNameLength}"
Text="{Binding Name}" />
<TextBlock Name="IdText" Text="{Locale:Locale UserProfilesUserId}" />
<TextBlock Name="IdText" Text="{locale:Locale UserProfilesUserId}" />
<TextBlock Name="IdLabel" Text="{Binding UserId}" />
</StackPanel>
<StackPanel
@ -78,12 +75,12 @@
<Button
Name="SaveButton"
Click="SaveButton_Click"
Content="{Locale:Locale Save}" />
Content="{locale:Locale Save}" />
<Button
Name="CloseButton"
HorizontalAlignment="Right"
Click="CloseButton_Click"
Content="{Locale:Locale Discard}" />
Content="{locale:Locale Discard}" />
</StackPanel>
</Grid>
</UserControl>

View File

@ -4,10 +4,11 @@ using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class UserEditor : UserControl
{

View File

@ -1,17 +1,18 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
MinWidth="500"
MinHeight="400"
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
x:Class="Ryujinx.Ava.Ui.Controls.UserRecoverer"
Focusable="True">
<UserControl
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
MinWidth="500"
MinHeight="400"
x:Class="Ryujinx.Ava.UI.Controls.UserRecoverer"
Focusable="True">
<Design.DataContext>
<viewModels:UserProfileViewModel />
</Design.DataContext>
@ -32,7 +33,7 @@
<ui:SymbolIcon Symbol="Back"/>
</Button>
<TextBlock Grid.Row="1"
Text="{Locale:Locale UserProfilesRecoverHeading}"/>
Text="{locale:Locale UserProfilesRecoverHeading}"/>
<ListBox
Margin="5"
Grid.Row="2"
@ -61,7 +62,7 @@
HorizontalAlignment="Right"
Command="{Binding Recover}"
CommandParameter="{Binding}"
Content="{Locale:Locale Recover}"/>
Content="{locale:Locale Recover}"/>
</Grid>
</Border>
</DataTemplate>

View File

@ -4,10 +4,10 @@ using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.ViewModels;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class UserRecoverer : UserControl
{

View File

@ -1,21 +1,20 @@
<UserControl
x:Class="Ryujinx.Ava.Ui.Controls.UserSelector"
x:Class="Ryujinx.Ava.UI.Controls.UserSelector"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:controls="clr-namespace:Ryujinx.Ava.Ui.Controls"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.Ui.ViewModels"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
d:DesignHeight="450"
MinWidth="500"
d:DesignWidth="800"
mc:Ignorable="d"
Focusable="True">
<UserControl.Resources>
<controls:BitmapArrayValueConverter x:Key="ByteImage" />
<helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources>
<Design.DataContext>
<viewModels:UserProfileViewModel />
@ -109,21 +108,21 @@
Grid.Column="0"
Margin="2"
Command="{Binding AddUser}"
Content="{Locale:Locale UserProfilesAddNewProfile}" />
Content="{locale:Locale UserProfilesAddNewProfile}" />
<Button
HorizontalAlignment="Stretch"
Grid.Row="0"
Margin="2"
Grid.Column="1"
Command="{Binding EditUser}"
Content="{Locale:Locale UserProfilesEditProfile}"
Content="{locale:Locale UserProfilesEditProfile}"
IsEnabled="{Binding IsSelectedProfiledEditable}" />
<Button
HorizontalAlignment="Stretch"
Grid.Row="1"
Grid.Column="0"
Margin="2"
Content="{Locale:Locale UserProfilesManageSaves}"
Content="{locale:Locale UserProfilesManageSaves}"
Command="{Binding ManageSaves}" />
<Button
HorizontalAlignment="Stretch"
@ -131,7 +130,7 @@
Grid.Column="1"
Margin="2"
Command="{Binding DeleteUser}"
Content="{Locale:Locale UserProfilesDeleteSelectedProfile}"
Content="{locale:Locale UserProfilesDeleteSelectedProfile}"
IsEnabled="{Binding IsSelectedProfileDeletable}" />
<Button
HorizontalAlignment="Stretch"
@ -140,7 +139,7 @@
Grid.Column="0"
Margin="2"
Command="{Binding RecoverLostAccounts}"
Content="{Locale:Locale UserProfilesRecoverLostAccounts}" />
Content="{locale:Locale UserProfilesRecoverLostAccounts}" />
</Grid>
</Grid>
</UserControl>

View File

@ -2,10 +2,10 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Ui.ViewModels;
using UserProfile = Ryujinx.Ava.Ui.Models.UserProfile;
using Ryujinx.Ava.UI.ViewModels;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Controls
{
public partial class UserSelector : UserControl
{

View File

@ -1,7 +1,7 @@
using Avalonia.Interactivity;
using Ryujinx.Ui.App.Common;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
public class ApplicationOpenedEventArgs : RoutedEventArgs
{

View File

@ -2,7 +2,7 @@
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
[SupportedOSPlatform("linux")]
internal class AvaloniaGlxContext : SPB.Platform.GLX.GLXOpenGLContext

View File

@ -2,7 +2,7 @@
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
[SupportedOSPlatform("windows")]
internal class AvaloniaWglContext : SPB.Platform.WGL.WGLOpenGLContext

View File

@ -5,7 +5,7 @@ using System;
using System.Globalization;
using System.IO;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
internal class BitmapArrayValueConverter : IValueConverter
{

View File

@ -8,7 +8,7 @@ using System;
using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
internal class ButtonKeyAssigner
{

View File

@ -1,20 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Primitives;
using Avalonia.Media;
using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Ui.Models;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
public static class ContentDialogHelper
{

View File

@ -2,7 +2,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Platform;
using Ryujinx.Ava.Ui.Helper;
using Ryujinx.Ava.UI.Helper;
using SPB.Graphics;
using SPB.Platform;
using SPB.Platform.GLX;
@ -10,9 +10,9 @@ using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using static Ryujinx.Ava.Ui.Controls.Win32NativeInterop;
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
namespace Ryujinx.Ava.Ui.Controls
namespace Ryujinx.Ava.UI.Helpers
{
public class EmbeddedWindow : NativeControlHost
{
@ -125,9 +125,9 @@ namespace Ryujinx.Ava.Ui.Controls
{
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = _wndProcDelegate,
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CS_OWNDC,
lpszClassName = _className,
lpszClassName = Marshal.StringToHGlobalUni(_className),
hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW)
};
@ -148,7 +148,9 @@ namespace Ryujinx.Ava.Ui.Controls
IntPtr.Zero);
WindowHandle = handle;
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
return new PlatformHandle(WindowHandle, "HWND");
}
@ -197,7 +199,7 @@ namespace Ryujinx.Ava.Ui.Controls
KeyModifiers.None));
break;
}
return DefWindowProc(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
[SupportedOSPlatform("macos")]

View File

@ -0,0 +1,9 @@
namespace Ryujinx.Ava.UI.Helpers
{
public enum Glyph
{
List,
Grid,
Chip
}
}

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