Compare commits
90 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7f27aabbd0 | ||
|
e876c43ce9 | ||
|
8639245533 | ||
|
d6b86a6629 | ||
|
8f2b7b5b8e | ||
|
fc4b7cba2c | ||
|
08831eecf7 | ||
|
c6a139a6e7 | ||
|
02714a1291 | ||
|
09c9686498 | ||
|
b6614c6ad5 | ||
|
b1d4b174a6 | ||
|
2b23463daa | ||
|
9dfe81770a | ||
|
52c115a1f8 | ||
|
e20abbf9cc | ||
|
76671d63d4 | ||
|
3d1a0bf374 | ||
|
c20f3fbebd | ||
|
0d3b82477e | ||
|
470be03c2f | ||
|
c963b3c804 | ||
|
a4fdfb5f94 | ||
|
37d27c4c99 | ||
|
f906eb06c2 | ||
|
219f63ff4e | ||
|
1cca3e99ab | ||
|
55a23e5ec8 | ||
|
479d1fd8b0 | ||
|
cb70e7bb30 | ||
|
c200a7b7c6 | ||
|
6268170a10 | ||
|
ee0f9b03a4 | ||
|
f93c5f006a | ||
|
295fbd0542 | ||
|
d7310d7a1c | ||
|
8c50943a2e | ||
|
ec4cd57ccf | ||
|
5a085cba0f | ||
|
1a1d33a018 | ||
|
0fbcd630bc | ||
|
f4d731ae20 | ||
|
8ac53c66b4 | ||
|
0f50de72be | ||
|
df758eddd1 | ||
|
5f32a8ed94 | ||
|
535fbec675 | ||
|
6fe88115a3 | ||
|
475fa4d390 | ||
|
edf7e628ca | ||
|
ba5c0cf5d8 | ||
|
403e67d983 | ||
|
c6f1908e0f | ||
|
851d81d24a | ||
|
459c4caeba | ||
|
539b22ef7b | ||
|
872f036d64 | ||
|
dca96122bf | ||
|
e752959109 | ||
|
cf01664698 | ||
|
b283a4adcd | ||
|
8428bb6541 | ||
|
9a0330f7f8 | ||
|
57fc996337 | ||
|
1f3b860f06 | ||
|
abe3c02ab4 | ||
|
45b417b2b4 | ||
|
d076339e3e | ||
|
837836431d | ||
|
9f555db5cd | ||
|
bf7fa60dfc | ||
|
752b93d3b7 | ||
|
f23b2878cc | ||
|
e211c3f00a | ||
|
d3709a753f | ||
|
ab676d58ea | ||
|
2372c194f1 | ||
|
40311310d1 | ||
|
dde9bb5c69 | ||
|
266338a7c9 | ||
|
90156eea4c | ||
|
071c01c235 | ||
|
de06ffb0f7 | ||
|
8a7de35e3f | ||
|
121296834a | ||
|
bbb24d8c7e | ||
|
4da44e09cb | ||
|
ae13f0ab4d | ||
|
a2a35f1be6 | ||
|
aedfadaaf7 |
@@ -89,6 +89,7 @@ csharp_style_conditional_delegate_call = true:suggestion
|
|||||||
# Modifier preferences
|
# Modifier preferences
|
||||||
csharp_prefer_static_local_function = true:suggestion
|
csharp_prefer_static_local_function = true:suggestion
|
||||||
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
|
||||||
|
csharp_style_prefer_readonly_struct = true
|
||||||
|
|
||||||
# Code-block preferences
|
# Code-block preferences
|
||||||
csharp_prefer_braces = true:silent
|
csharp_prefer_braces = true:silent
|
||||||
|
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -11,6 +11,7 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
|
concurrency: release
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -125,6 +125,9 @@ ClientBin/
|
|||||||
packages/*
|
packages/*
|
||||||
*.config
|
*.config
|
||||||
|
|
||||||
|
# Include nuget.config
|
||||||
|
!nuget.config
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
# RIA/Silverlight projects
|
||||||
Generated_Code/
|
Generated_Code/
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
{
|
{
|
||||||
struct AllocationResult
|
readonly struct AllocationResult
|
||||||
{
|
{
|
||||||
public int IntUsedRegisters { get; }
|
public int IntUsedRegisters { get; }
|
||||||
public int VecUsedRegisters { get; }
|
public int VecUsedRegisters { get; }
|
||||||
|
@@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
private class ParallelCopy
|
private class ParallelCopy
|
||||||
{
|
{
|
||||||
private struct Copy
|
private readonly struct Copy
|
||||||
{
|
{
|
||||||
public Register Dest { get; }
|
public Register Dest { get; }
|
||||||
public Register Source { get; }
|
public Register Source { get; }
|
||||||
|
@@ -11,7 +11,7 @@ namespace ARMeilleure.CodeGen.RegisterAllocators
|
|||||||
{
|
{
|
||||||
class HybridAllocator : IRegisterAllocator
|
class HybridAllocator : IRegisterAllocator
|
||||||
{
|
{
|
||||||
private struct BlockInfo
|
private readonly struct BlockInfo
|
||||||
{
|
{
|
||||||
public bool HasCall { get; }
|
public bool HasCall { get; }
|
||||||
|
|
||||||
|
@@ -3,7 +3,7 @@ using System;
|
|||||||
|
|
||||||
namespace ARMeilleure.CodeGen.RegisterAllocators
|
namespace ARMeilleure.CodeGen.RegisterAllocators
|
||||||
{
|
{
|
||||||
struct RegisterMasks
|
readonly struct RegisterMasks
|
||||||
{
|
{
|
||||||
public int IntAvailableRegisters { get; }
|
public int IntAvailableRegisters { get; }
|
||||||
public int VecAvailableRegisters { get; }
|
public int VecAvailableRegisters { get; }
|
||||||
|
@@ -1587,6 +1587,12 @@ namespace ARMeilleure.CodeGen.X86
|
|||||||
|
|
||||||
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
|
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);
|
context.Assembler.Mov(dest, source, OperandType.I32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
namespace ARMeilleure.CodeGen.X86
|
namespace ARMeilleure.CodeGen.X86
|
||||||
{
|
{
|
||||||
struct IntrinsicInfo
|
readonly struct IntrinsicInfo
|
||||||
{
|
{
|
||||||
public X86Instruction Inst { get; }
|
public X86Instruction Inst { get; }
|
||||||
public IntrinsicType Type { get; }
|
public IntrinsicType Type { get; }
|
||||||
|
@@ -80,10 +80,7 @@ namespace ARMeilleure.Common
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_pages)
|
lock (_pages)
|
||||||
{
|
{
|
||||||
@@ -100,10 +97,7 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||||
public AddressTable(Level[] levels)
|
public AddressTable(Level[] levels)
|
||||||
{
|
{
|
||||||
if (levels == null)
|
ArgumentNullException.ThrowIfNull(levels);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(levels));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (levels.Length < 2)
|
if (levels.Length < 2)
|
||||||
{
|
{
|
||||||
@@ -141,10 +135,7 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||||
public ref TEntry GetValue(ulong address)
|
public ref TEntry GetValue(ulong address)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsValid(address))
|
if (!IsValid(address))
|
||||||
{
|
{
|
||||||
|
@@ -49,10 +49,7 @@ namespace ARMeilleure.Common
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ref _countTable.GetValue(_index);
|
return ref _countTable.GetValue(_index);
|
||||||
}
|
}
|
||||||
|
@@ -53,10 +53,7 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
public int Allocate()
|
public int Allocate()
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_allocated)
|
lock (_allocated)
|
||||||
{
|
{
|
||||||
@@ -83,10 +80,7 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
public void Free(int index)
|
public void Free(int index)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_allocated)
|
lock (_allocated)
|
||||||
{
|
{
|
||||||
@@ -108,10 +102,7 @@ namespace ARMeilleure.Common
|
|||||||
/// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
|
/// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
|
||||||
public ref TEntry GetValue(int index)
|
public ref TEntry GetValue(int index)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_allocated)
|
lock (_allocated)
|
||||||
{
|
{
|
||||||
|
@@ -2,7 +2,7 @@ using ARMeilleure.Instructions;
|
|||||||
|
|
||||||
namespace ARMeilleure.Decoders
|
namespace ARMeilleure.Decoders
|
||||||
{
|
{
|
||||||
struct InstDescriptor
|
readonly struct InstDescriptor
|
||||||
{
|
{
|
||||||
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);
|
public static InstDescriptor Undefined => new InstDescriptor(InstName.Und, InstEmit.Und);
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ namespace ARMeilleure.Decoders
|
|||||||
|
|
||||||
private const int FastLookupSize = 0x1000;
|
private const int FastLookupSize = 0x1000;
|
||||||
|
|
||||||
private struct InstInfo
|
private readonly struct InstInfo
|
||||||
{
|
{
|
||||||
public int Mask { get; }
|
public int Mask { get; }
|
||||||
public int Value { get; }
|
public int Value { get; }
|
||||||
@@ -1341,7 +1341,7 @@ namespace ARMeilleure.Decoders
|
|||||||
{
|
{
|
||||||
string reversedEncoding = encoding.Substring(16) + encoding.Substring(0, 16);
|
string reversedEncoding = encoding.Substring(16) + encoding.Substring(0, 16);
|
||||||
MakeOp reversedMakeOp =
|
MakeOp reversedMakeOp =
|
||||||
(InstDescriptor inst, ulong address, int opCode)
|
(inst, address, opCode)
|
||||||
=> makeOp(inst, address, (int)BitOperations.RotateRight((uint)opCode, 16));
|
=> makeOp(inst, address, (int)BitOperations.RotateRight((uint)opCode, 16));
|
||||||
Set(reversedEncoding, AllInstT32, new InstDescriptor(name, emitter), reversedMakeOp);
|
Set(reversedEncoding, AllInstT32, new InstDescriptor(name, emitter), reversedMakeOp);
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@ namespace ARMeilleure.Diagnostics
|
|||||||
{
|
{
|
||||||
static class Symbols
|
static class Symbols
|
||||||
{
|
{
|
||||||
private struct RangedSymbol
|
private readonly struct RangedSymbol
|
||||||
{
|
{
|
||||||
public readonly ulong Start;
|
public readonly ulong Start;
|
||||||
public readonly ulong End;
|
public readonly ulong End;
|
||||||
|
@@ -381,7 +381,7 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
for (int index = 0; index < elems; index++)
|
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)
|
if (sizeF == 0)
|
||||||
{
|
{
|
||||||
@@ -389,8 +389,6 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
|
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
|
||||||
context.LoadFromContext();
|
context.LoadFromContext();
|
||||||
|
|
||||||
e = context.ZeroExtend16(OperandType.I64, e);
|
|
||||||
|
|
||||||
res = EmitVectorInsert(context, res, e, part + index, 1);
|
res = EmitVectorInsert(context, res, e, part + index, 1);
|
||||||
}
|
}
|
||||||
else /* if (sizeF == 1) */
|
else /* if (sizeF == 1) */
|
||||||
|
@@ -48,10 +48,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
|
|
||||||
public void AddSuccessor(BasicBlock block)
|
public void AddSuccessor(BasicBlock block)
|
||||||
{
|
{
|
||||||
if (block == null)
|
ArgumentNullException.ThrowIfNull(block);
|
||||||
{
|
|
||||||
ThrowNull(nameof(block));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)_succCount + 1 > MaxSuccessors)
|
if ((uint)_succCount + 1 > MaxSuccessors)
|
||||||
{
|
{
|
||||||
@@ -100,10 +97,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
|
|
||||||
public void SetSuccessor(int index, BasicBlock block)
|
public void SetSuccessor(int index, BasicBlock block)
|
||||||
{
|
{
|
||||||
if (block == null)
|
ArgumentNullException.ThrowIfNull(block);
|
||||||
{
|
|
||||||
ThrowNull(nameof(block));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((uint)index >= (uint)_succCount)
|
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 ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
|
||||||
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
|
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");
|
||||||
|
|
||||||
|
@@ -378,14 +378,7 @@ namespace ARMeilleure.IntermediateRepresentation
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
if (Kind == OperandKind.LocalVariable)
|
return ((ulong)_data).GetHashCode();
|
||||||
{
|
|
||||||
return base.GetHashCode();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (int)Value ^ ((int)Kind << 16) ^ ((int)Type << 20);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(Operand operand)
|
public bool Equals(Operand operand)
|
||||||
|
@@ -3,7 +3,7 @@ using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
|||||||
|
|
||||||
namespace ARMeilleure.IntermediateRepresentation
|
namespace ARMeilleure.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
struct PhiOperation
|
readonly struct PhiOperation
|
||||||
{
|
{
|
||||||
private readonly Operation _operation;
|
private readonly Operation _operation;
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace ARMeilleure.IntermediateRepresentation
|
namespace ARMeilleure.IntermediateRepresentation
|
||||||
{
|
{
|
||||||
struct Register : IEquatable<Register>
|
readonly struct Register : IEquatable<Register>
|
||||||
{
|
{
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
|
|
||||||
|
@@ -18,17 +18,17 @@ namespace ARMeilleure.Signal
|
|||||||
public IntPtr sa_restorer;
|
public IntPtr sa_restorer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static class UnixSignalHandlerRegistration
|
static partial class UnixSignalHandlerRegistration
|
||||||
{
|
{
|
||||||
private const int SIGSEGV = 11;
|
private const int SIGSEGV = 11;
|
||||||
private const int SIGBUS = 10;
|
private const int SIGBUS = 10;
|
||||||
private const int SA_SIGINFO = 0x00000004;
|
private const int SA_SIGINFO = 0x00000004;
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static extern int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static extern int sigemptyset(ref SigSet set);
|
private static partial int sigemptyset(ref SigSet set);
|
||||||
|
|
||||||
public static SigAction RegisterExceptionHandler(IntPtr action)
|
public static SigAction RegisterExceptionHandler(IntPtr action)
|
||||||
{
|
{
|
||||||
|
@@ -3,19 +3,19 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace ARMeilleure.Signal
|
||||||
{
|
{
|
||||||
unsafe class WindowsSignalHandlerRegistration
|
unsafe partial class WindowsSignalHandlerRegistration
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll")]
|
[LibraryImport("kernel32.dll")]
|
||||||
private static extern IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
[LibraryImport("kernel32.dll")]
|
||||||
private static extern ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
|
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||||
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
|
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||||
|
|
||||||
private static IntPtr _getCurrentThreadIdPtr;
|
private static IntPtr _getCurrentThreadIdPtr;
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ using ARMeilleure.Instructions;
|
|||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -44,14 +43,13 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public IMemoryManager Memory { get; }
|
public IMemoryManager Memory { get; }
|
||||||
|
|
||||||
public bool HasPtc { get; }
|
|
||||||
|
|
||||||
public EntryTable<uint> CountTable { get; }
|
public EntryTable<uint> CountTable { get; }
|
||||||
public AddressTable<ulong> FunctionTable { get; }
|
public AddressTable<ulong> FunctionTable { get; }
|
||||||
public TranslatorStubs Stubs { get; }
|
public TranslatorStubs Stubs { get; }
|
||||||
|
|
||||||
public ulong EntryAddress { get; }
|
public ulong EntryAddress { get; }
|
||||||
public bool HighCq { get; }
|
public bool HighCq { get; }
|
||||||
|
public bool HasPtc { get; }
|
||||||
public Aarch32Mode Mode { get; }
|
public Aarch32Mode Mode { get; }
|
||||||
|
|
||||||
private int _ifThenBlockStateIndex = 0;
|
private int _ifThenBlockStateIndex = 0;
|
||||||
@@ -66,15 +64,16 @@ namespace ARMeilleure.Translation
|
|||||||
TranslatorStubs stubs,
|
TranslatorStubs stubs,
|
||||||
ulong entryAddress,
|
ulong entryAddress,
|
||||||
bool highCq,
|
bool highCq,
|
||||||
|
bool hasPtc,
|
||||||
Aarch32Mode mode)
|
Aarch32Mode mode)
|
||||||
{
|
{
|
||||||
HasPtc = Ptc.State != PtcState.Disabled;
|
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
CountTable = countTable;
|
CountTable = countTable;
|
||||||
FunctionTable = funcTable;
|
FunctionTable = funcTable;
|
||||||
Stubs = stubs;
|
Stubs = stubs;
|
||||||
EntryAddress = entryAddress;
|
EntryAddress = entryAddress;
|
||||||
HighCq = highCq;
|
HighCq = highCq;
|
||||||
|
HasPtc = hasPtc;
|
||||||
Mode = mode;
|
Mode = mode;
|
||||||
|
|
||||||
_labels = new Dictionary<ulong, Operand>();
|
_labels = new Dictionary<ulong, Operand>();
|
||||||
|
@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
struct CacheEntry : IComparable<CacheEntry>
|
readonly struct CacheEntry : IComparable<CacheEntry>
|
||||||
{
|
{
|
||||||
public int Offset { get; }
|
public int Offset { get; }
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
|
@@ -6,7 +6,7 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
{
|
{
|
||||||
class CacheMemoryAllocator
|
class CacheMemoryAllocator
|
||||||
{
|
{
|
||||||
private struct MemoryBlock : IComparable<MemoryBlock>
|
private readonly struct MemoryBlock : IComparable<MemoryBlock>
|
||||||
{
|
{
|
||||||
public int Offset { get; }
|
public int Offset { get; }
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
|
@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
static class JitUnwindWindows
|
static partial class JitUnwindWindows
|
||||||
{
|
{
|
||||||
private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
|
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);
|
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
|
||||||
|
|
||||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
|
[LibraryImport("kernel32.dll")]
|
||||||
private static unsafe extern bool RtlInstallFunctionTableCallback(
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static unsafe partial bool RtlInstallFunctionTableCallback(
|
||||||
ulong tableIdentifier,
|
ulong tableIdentifier,
|
||||||
ulong baseAddress,
|
ulong baseAddress,
|
||||||
uint length,
|
uint length,
|
||||||
GetRuntimeFunctionCallback callback,
|
GetRuntimeFunctionCallback callback,
|
||||||
IntPtr context,
|
IntPtr context,
|
||||||
string outOfProcessCallbackDll);
|
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
|
||||||
|
|
||||||
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation
|
namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
struct CompilerContext
|
readonly struct CompilerContext
|
||||||
{
|
{
|
||||||
public ControlFlowGraph Cfg { get; }
|
public ControlFlowGraph Cfg { get; }
|
||||||
|
|
||||||
|
@@ -25,10 +25,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public static Delegate GetDelegate(MethodInfo info)
|
public static Delegate GetDelegate(MethodInfo info)
|
||||||
{
|
{
|
||||||
if (info == null)
|
ArgumentNullException.ThrowIfNull(info);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
|
||||||
Type returnType = info.ReturnType;
|
Type returnType = info.ReturnType;
|
||||||
|
@@ -35,10 +35,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
|
||||||
{
|
{
|
||||||
if (info == null)
|
ArgumentNullException.ThrowIfNull(info);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
string key = GetKey(info);
|
string key = GetKey(info);
|
||||||
|
|
||||||
@@ -52,10 +49,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
public static int GetDelegateIndex(MethodInfo info)
|
public static int GetDelegateIndex(MethodInfo info)
|
||||||
{
|
{
|
||||||
if (info == null)
|
ArgumentNullException.ThrowIfNull(info);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
string key = GetKey(info);
|
string key = GetKey(info);
|
||||||
|
|
||||||
|
@@ -67,10 +67,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
|
/// <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)
|
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
|
||||||
{
|
{
|
||||||
if (value == null)
|
ArgumentNullException.ThrowIfNull(value);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
|
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>
|
/// <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)
|
public V GetOrAdd(K start, K end, V value)
|
||||||
{
|
{
|
||||||
if (value == null)
|
ArgumentNullException.ThrowIfNull(value);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
|
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
|
||||||
return node.Value;
|
return node.Value;
|
||||||
@@ -152,10 +146,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// <returns>Node reference in the tree</returns>
|
/// <returns>Node reference in the tree</returns>
|
||||||
private IntervalTreeNode<K, V> GetNode(K key)
|
private IntervalTreeNode<K, V> GetNode(K key)
|
||||||
{
|
{
|
||||||
if (key == null)
|
ArgumentNullException.ThrowIfNull(key);
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalTreeNode<K, V> node = _root;
|
IntervalTreeNode<K, V> node = _root;
|
||||||
while (node != null)
|
while (node != null)
|
||||||
|
10
ARMeilleure/Translation/PTC/IPtcLoadState.cs
Normal file
10
ARMeilleure/Translation/PTC/IPtcLoadState.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Translation.PTC
|
||||||
|
{
|
||||||
|
public interface IPtcLoadState
|
||||||
|
{
|
||||||
|
event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||||
|
void Continue();
|
||||||
|
}
|
||||||
|
}
|
@@ -22,12 +22,12 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
public static class Ptc
|
class Ptc : IPtcLoadState
|
||||||
{
|
{
|
||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\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 ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
@@ -35,45 +35,49 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string TitleIdTextDefault = "0000000000000000";
|
private const string TitleIdTextDefault = "0000000000000000";
|
||||||
private const string DisplayVersionDefault = "0";
|
private const string DisplayVersionDefault = "0";
|
||||||
|
|
||||||
internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||||
internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||||
internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||||
|
|
||||||
private const byte FillingByte = 0x00;
|
private const byte FillingByte = 0x00;
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
|
|
||||||
|
public PtcProfiler Profiler { get; }
|
||||||
|
|
||||||
// Carriers.
|
// Carriers.
|
||||||
private static MemoryStream _infosStream;
|
private MemoryStream _infosStream;
|
||||||
private static List<byte[]> _codesList;
|
private List<byte[]> _codesList;
|
||||||
private static MemoryStream _relocsStream;
|
private MemoryStream _relocsStream;
|
||||||
private static MemoryStream _unwindInfosStream;
|
private MemoryStream _unwindInfosStream;
|
||||||
|
|
||||||
private static readonly ulong _outerHeaderMagic;
|
private readonly ulong _outerHeaderMagic;
|
||||||
private static readonly ulong _innerHeaderMagic;
|
private readonly ulong _innerHeaderMagic;
|
||||||
|
|
||||||
private static readonly ManualResetEvent _waitEvent;
|
private readonly ManualResetEvent _waitEvent;
|
||||||
|
|
||||||
private static readonly object _lock;
|
private readonly object _lock;
|
||||||
|
|
||||||
private static bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
internal static string TitleIdText { get; private set; }
|
public string TitleIdText { get; private set; }
|
||||||
internal static string DisplayVersion { get; private set; }
|
public string DisplayVersion { get; private set; }
|
||||||
|
|
||||||
private static MemoryManagerMode _memoryMode;
|
private MemoryManagerType _memoryMode;
|
||||||
|
|
||||||
internal static string CachePathActual { get; private set; }
|
public string CachePathActual { get; private set; }
|
||||||
internal static string CachePathBackup { get; private set; }
|
public string CachePathBackup { get; private set; }
|
||||||
|
|
||||||
internal static PtcState State { get; private set; }
|
public PtcState State { get; private set; }
|
||||||
|
|
||||||
// Progress reporting helpers.
|
// Progress reporting helpers.
|
||||||
private static volatile int _translateCount;
|
private volatile int _translateCount;
|
||||||
private static volatile int _translateTotalCount;
|
private volatile int _translateTotalCount;
|
||||||
public static event Action<PtcLoadingState, int, int> PtcStateChanged;
|
public event Action<PtcLoadingState, int, int> PtcStateChanged;
|
||||||
|
|
||||||
static Ptc()
|
public Ptc()
|
||||||
{
|
{
|
||||||
|
Profiler = new PtcProfiler(this);
|
||||||
|
|
||||||
InitializeCarriers();
|
InitializeCarriers();
|
||||||
|
|
||||||
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
|
||||||
@@ -94,12 +98,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Disable();
|
Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode)
|
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
|
||||||
{
|
{
|
||||||
Wait();
|
Wait();
|
||||||
|
|
||||||
PtcProfiler.Wait();
|
Profiler.Wait();
|
||||||
PtcProfiler.ClearEntries();
|
Profiler.ClearEntries();
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
|
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
|
||||||
|
|
||||||
@@ -137,12 +141,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
||||||
|
|
||||||
PreLoad();
|
PreLoad();
|
||||||
PtcProfiler.PreLoad();
|
Profiler.PreLoad();
|
||||||
|
|
||||||
Enable();
|
Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InitializeCarriers()
|
private void InitializeCarriers()
|
||||||
{
|
{
|
||||||
_infosStream = new MemoryStream();
|
_infosStream = new MemoryStream();
|
||||||
_codesList = new List<byte[]>();
|
_codesList = new List<byte[]>();
|
||||||
@@ -150,7 +154,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
_unwindInfosStream = new MemoryStream();
|
_unwindInfosStream = new MemoryStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DisposeCarriers()
|
private void DisposeCarriers()
|
||||||
{
|
{
|
||||||
_infosStream.Dispose();
|
_infosStream.Dispose();
|
||||||
_codesList.Clear();
|
_codesList.Clear();
|
||||||
@@ -158,12 +162,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
_unwindInfosStream.Dispose();
|
_unwindInfosStream.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool AreCarriersEmpty()
|
private bool AreCarriersEmpty()
|
||||||
{
|
{
|
||||||
return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
|
return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ResetCarriersIfNeeded()
|
private void ResetCarriersIfNeeded()
|
||||||
{
|
{
|
||||||
if (AreCarriersEmpty())
|
if (AreCarriersEmpty())
|
||||||
{
|
{
|
||||||
@@ -175,7 +179,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
InitializeCarriers();
|
InitializeCarriers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PreLoad()
|
private void PreLoad()
|
||||||
{
|
{
|
||||||
string fileNameActual = string.Concat(CachePathActual, ".cache");
|
string fileNameActual = string.Concat(CachePathActual, ".cache");
|
||||||
string fileNameBackup = string.Concat(CachePathBackup, ".cache");
|
string fileNameBackup = string.Concat(CachePathBackup, ".cache");
|
||||||
@@ -199,7 +203,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe bool Load(string fileName, bool isBackup)
|
private unsafe bool Load(string fileName, bool isBackup)
|
||||||
{
|
{
|
||||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||||
@@ -376,12 +380,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||||
{
|
{
|
||||||
compressedStream.SetLength(0L);
|
compressedStream.SetLength(0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PreSave()
|
private void PreSave()
|
||||||
{
|
{
|
||||||
_waitEvent.Reset();
|
_waitEvent.Reset();
|
||||||
|
|
||||||
@@ -409,7 +413,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
_waitEvent.Set();
|
_waitEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe void Save(string fileName)
|
private unsafe void Save(string fileName)
|
||||||
{
|
{
|
||||||
int translatedFuncsCount;
|
int translatedFuncsCount;
|
||||||
|
|
||||||
@@ -517,7 +521,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void LoadTranslations(Translator translator)
|
public void LoadTranslations(Translator translator)
|
||||||
{
|
{
|
||||||
if (AreCarriersEmpty())
|
if (AreCarriersEmpty())
|
||||||
{
|
{
|
||||||
@@ -550,7 +554,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
|
||||||
|
|
||||||
if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
|
||||||
{
|
{
|
||||||
infoEntry.Stubbed = true;
|
infoEntry.Stubbed = true;
|
||||||
infoEntry.CodeLength = 0;
|
infoEntry.CodeLength = 0;
|
||||||
@@ -601,38 +605,38 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
|
Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetEntriesCount()
|
private int GetEntriesCount()
|
||||||
{
|
{
|
||||||
return _codesList.Count;
|
return _codesList.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG")]
|
||||||
private static void SkipCode(int index, int codeLength)
|
private void SkipCode(int index, int codeLength)
|
||||||
{
|
{
|
||||||
Debug.Assert(_codesList[index].Length == 0);
|
Debug.Assert(_codesList[index].Length == 0);
|
||||||
Debug.Assert(codeLength == 0);
|
Debug.Assert(codeLength == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SkipReloc(int relocEntriesCount)
|
private void SkipReloc(int relocEntriesCount)
|
||||||
{
|
{
|
||||||
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
|
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
private void SkipUnwindInfo(BinaryReader unwindInfosReader)
|
||||||
{
|
{
|
||||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||||
|
|
||||||
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
|
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] ReadCode(int index, int codeLength)
|
private byte[] ReadCode(int index, int codeLength)
|
||||||
{
|
{
|
||||||
Debug.Assert(_codesList[index].Length == codeLength);
|
Debug.Assert(_codesList[index].Length == codeLength);
|
||||||
|
|
||||||
return _codesList[index];
|
return _codesList[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
|
private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
|
||||||
{
|
{
|
||||||
RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
|
RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
|
||||||
|
|
||||||
@@ -648,7 +652,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return relocEntries;
|
return relocEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
private void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
|
||||||
{
|
{
|
||||||
callCounter = null;
|
callCounter = null;
|
||||||
|
|
||||||
@@ -702,7 +706,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
|
private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
|
||||||
{
|
{
|
||||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||||
|
|
||||||
@@ -723,7 +727,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return new UnwindInfo(pushEntries, prologueSize);
|
return new UnwindInfo(pushEntries, prologueSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TranslatedFunction FastTranslate(
|
private TranslatedFunction FastTranslate(
|
||||||
byte[] code,
|
byte[] code,
|
||||||
Counter<uint> callCounter,
|
Counter<uint> callCounter,
|
||||||
ulong guestSize,
|
ulong guestSize,
|
||||||
@@ -736,19 +740,19 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
|
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void UpdateInfo(InfoEntry infoEntry)
|
private void UpdateInfo(InfoEntry infoEntry)
|
||||||
{
|
{
|
||||||
_infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
|
_infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
|
||||||
|
|
||||||
SerializeStructure(_infosStream, infoEntry);
|
SerializeStructure(_infosStream, infoEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StubCode(int index)
|
private void StubCode(int index)
|
||||||
{
|
{
|
||||||
_codesList[index] = Array.Empty<byte>();
|
_codesList[index] = Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StubReloc(int relocEntriesCount)
|
private void StubReloc(int relocEntriesCount)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
|
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
|
||||||
{
|
{
|
||||||
@@ -756,7 +760,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void StubUnwindInfo(BinaryReader unwindInfosReader)
|
private void StubUnwindInfo(BinaryReader unwindInfosReader)
|
||||||
{
|
{
|
||||||
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
int pushEntriesLength = unwindInfosReader.ReadInt32();
|
||||||
|
|
||||||
@@ -766,9 +770,9 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void MakeAndSaveTranslations(Translator translator)
|
public void MakeAndSaveTranslations(Translator translator)
|
||||||
{
|
{
|
||||||
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions);
|
var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
|
||||||
|
|
||||||
_translateCount = 0;
|
_translateCount = 0;
|
||||||
_translateTotalCount = profiledFuncsToTranslate.Count;
|
_translateTotalCount = profiledFuncsToTranslate.Count;
|
||||||
@@ -811,7 +815,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
ulong address = item.address;
|
ulong address = item.address;
|
||||||
|
|
||||||
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
|
Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
|
||||||
|
|
||||||
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
|
||||||
|
|
||||||
@@ -861,7 +865,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
preSaveThread.Start();
|
preSaveThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ReportProgress(object state)
|
private void ReportProgress(object state)
|
||||||
{
|
{
|
||||||
const int refreshRate = 50; // ms.
|
const int refreshRate = 50; // ms.
|
||||||
|
|
||||||
@@ -882,12 +886,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
while (!endEvent.WaitOne(refreshRate));
|
while (!endEvent.WaitOne(refreshRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
|
public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
|
||||||
{
|
{
|
||||||
return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
|
return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
|
public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -936,12 +940,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void WriteCode(ReadOnlySpan<byte> code)
|
private void WriteCode(ReadOnlySpan<byte> code)
|
||||||
{
|
{
|
||||||
_codesList.Add(code.ToArray());
|
_codesList.Add(code.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool GetEndianness()
|
public static bool GetEndianness()
|
||||||
{
|
{
|
||||||
return BitConverter.IsLittleEndian;
|
return BitConverter.IsLittleEndian;
|
||||||
}
|
}
|
||||||
@@ -955,7 +959,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
(uint)HardwareCapabilities.FeatureInfo7Ecx);
|
(uint)HardwareCapabilities.FeatureInfo7Ecx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte GetMemoryManagerMode()
|
private byte GetMemoryManagerMode()
|
||||||
{
|
{
|
||||||
return (byte)_memoryMode;
|
return (byte)_memoryMode;
|
||||||
}
|
}
|
||||||
@@ -1050,12 +1054,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
public int RelocEntriesCount;
|
public int RelocEntriesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Enable()
|
private void Enable()
|
||||||
{
|
{
|
||||||
State = PtcState.Enabled;
|
State = PtcState.Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Continue()
|
public void Continue()
|
||||||
{
|
{
|
||||||
if (State == PtcState.Enabled)
|
if (State == PtcState.Enabled)
|
||||||
{
|
{
|
||||||
@@ -1063,7 +1067,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Close()
|
public void Close()
|
||||||
{
|
{
|
||||||
if (State == PtcState.Enabled ||
|
if (State == PtcState.Enabled ||
|
||||||
State == PtcState.Continuing)
|
State == PtcState.Continuing)
|
||||||
@@ -1072,17 +1076,17 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Disable()
|
public void Disable()
|
||||||
{
|
{
|
||||||
State = PtcState.Disabled;
|
State = PtcState.Disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Wait()
|
private void Wait()
|
||||||
{
|
{
|
||||||
_waitEvent.WaitOne();
|
_waitEvent.WaitOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
if (!_disposed)
|
||||||
{
|
{
|
||||||
|
@@ -16,7 +16,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
|
|||||||
|
|
||||||
namespace ARMeilleure.Translation.PTC
|
namespace ARMeilleure.Translation.PTC
|
||||||
{
|
{
|
||||||
public static class PtcProfiler
|
class PtcProfiler
|
||||||
{
|
{
|
||||||
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
|
||||||
|
|
||||||
@@ -26,27 +26,31 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
|
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
|
|
||||||
private static readonly System.Timers.Timer _timer;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
private static readonly ulong _outerHeaderMagic;
|
private readonly System.Timers.Timer _timer;
|
||||||
|
|
||||||
private static readonly ManualResetEvent _waitEvent;
|
private readonly ulong _outerHeaderMagic;
|
||||||
|
|
||||||
private static readonly object _lock;
|
private readonly ManualResetEvent _waitEvent;
|
||||||
|
|
||||||
private static bool _disposed;
|
private readonly object _lock;
|
||||||
|
|
||||||
private static Hash128 _lastHash;
|
private bool _disposed;
|
||||||
|
|
||||||
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
private Hash128 _lastHash;
|
||||||
|
|
||||||
internal static bool Enabled { get; private set; }
|
public Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
|
||||||
|
|
||||||
public static ulong StaticCodeStart { internal get; set; }
|
public bool Enabled { get; private set; }
|
||||||
public static ulong StaticCodeSize { internal get; set; }
|
|
||||||
|
|
||||||
static PtcProfiler()
|
public ulong StaticCodeStart { get; set; }
|
||||||
|
public ulong StaticCodeSize { get; set; }
|
||||||
|
|
||||||
|
public PtcProfiler(Ptc ptc)
|
||||||
{
|
{
|
||||||
|
_ptc = ptc;
|
||||||
|
|
||||||
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
|
||||||
_timer.Elapsed += PreSave;
|
_timer.Elapsed += PreSave;
|
||||||
|
|
||||||
@@ -63,7 +67,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Enabled = false;
|
Enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
{
|
{
|
||||||
@@ -76,7 +80,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
|
||||||
{
|
{
|
||||||
if (IsAddressInStaticCodeRange(address))
|
if (IsAddressInStaticCodeRange(address))
|
||||||
{
|
{
|
||||||
@@ -91,12 +95,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsAddressInStaticCodeRange(ulong address)
|
public bool IsAddressInStaticCodeRange(ulong address)
|
||||||
{
|
{
|
||||||
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
|
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
|
||||||
{
|
{
|
||||||
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
|
||||||
|
|
||||||
@@ -111,18 +115,18 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return profiledFuncsToTranslate;
|
return profiledFuncsToTranslate;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ClearEntries()
|
public void ClearEntries()
|
||||||
{
|
{
|
||||||
ProfiledFuncs.Clear();
|
ProfiledFuncs.Clear();
|
||||||
ProfiledFuncs.TrimExcess();
|
ProfiledFuncs.TrimExcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void PreLoad()
|
public void PreLoad()
|
||||||
{
|
{
|
||||||
_lastHash = default;
|
_lastHash = default;
|
||||||
|
|
||||||
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
|
string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
|
||||||
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
|
string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
|
||||||
|
|
||||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||||
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
|
FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
|
||||||
@@ -143,7 +147,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool Load(string fileName, bool isBackup)
|
private bool Load(string fileName, bool isBackup)
|
||||||
{
|
{
|
||||||
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
using (FileStream compressedStream = new(fileName, FileMode.Open))
|
||||||
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
|
||||||
@@ -228,22 +232,22 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
|
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
private ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
|
||||||
{
|
{
|
||||||
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void InvalidateCompressedStream(FileStream compressedStream)
|
private void InvalidateCompressedStream(FileStream compressedStream)
|
||||||
{
|
{
|
||||||
compressedStream.SetLength(0L);
|
compressedStream.SetLength(0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
|
||||||
{
|
{
|
||||||
_waitEvent.Reset();
|
_waitEvent.Reset();
|
||||||
|
|
||||||
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info");
|
string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
|
||||||
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info");
|
string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
|
||||||
|
|
||||||
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
FileInfo fileInfoActual = new FileInfo(fileNameActual);
|
||||||
|
|
||||||
@@ -257,7 +261,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
_waitEvent.Set();
|
_waitEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Save(string fileName)
|
private void Save(string fileName)
|
||||||
{
|
{
|
||||||
int profiledFuncsCount;
|
int profiledFuncsCount;
|
||||||
|
|
||||||
@@ -329,7 +333,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
private void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
|
||||||
{
|
{
|
||||||
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
|
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
|
||||||
}
|
}
|
||||||
@@ -361,7 +365,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
|
||||||
internal struct FuncProfile
|
public struct FuncProfile
|
||||||
{
|
{
|
||||||
public ExecutionMode Mode;
|
public ExecutionMode Mode;
|
||||||
public bool HighCq;
|
public bool HighCq;
|
||||||
@@ -373,10 +377,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
if (Ptc.State == PtcState.Enabled ||
|
if (_ptc.State == PtcState.Enabled ||
|
||||||
Ptc.State == PtcState.Continuing)
|
_ptc.State == PtcState.Continuing)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
|
|
||||||
@@ -384,7 +388,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
Enabled = false;
|
Enabled = false;
|
||||||
|
|
||||||
@@ -394,12 +398,12 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Wait()
|
public void Wait()
|
||||||
{
|
{
|
||||||
_waitEvent.WaitOne();
|
_waitEvent.WaitOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!_disposed)
|
if (!_disposed)
|
||||||
{
|
{
|
||||||
|
@@ -14,7 +14,7 @@ namespace ARMeilleure.Translation
|
|||||||
private const int RegsCount = 32;
|
private const int RegsCount = 32;
|
||||||
private const int RegsMask = RegsCount - 1;
|
private const int RegsMask = RegsCount - 1;
|
||||||
|
|
||||||
private struct RegisterMask : IEquatable<RegisterMask>
|
private readonly struct RegisterMask : IEquatable<RegisterMask>
|
||||||
{
|
{
|
||||||
public long IntMask => Mask.GetElement(0);
|
public long IntMask => Mask.GetElement(0);
|
||||||
public long VecMask => Mask.GetElement(1);
|
public long VecMask => Mask.GetElement(1);
|
||||||
|
@@ -140,7 +140,7 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.Clear(localDefs, 0, localDefs.Length);
|
Array.Clear(localDefs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,6 +44,8 @@ namespace ARMeilleure.Translation
|
|||||||
private readonly IJitMemoryAllocator _allocator;
|
private readonly IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
internal AddressTable<ulong> FunctionTable { get; }
|
internal AddressTable<ulong> FunctionTable { get; }
|
||||||
internal EntryTable<uint> CountTable { get; }
|
internal EntryTable<uint> CountTable { get; }
|
||||||
@@ -63,6 +65,8 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
|
||||||
|
|
||||||
|
_ptc = new Ptc();
|
||||||
|
|
||||||
Queue = new TranslatorQueue();
|
Queue = new TranslatorQueue();
|
||||||
|
|
||||||
JitCache.Initialize(allocator);
|
JitCache.Initialize(allocator);
|
||||||
@@ -80,22 +84,37 @@ namespace ARMeilleure.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||||
|
{
|
||||||
|
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
||||||
|
return _ptc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
if (_ptc.Profiler.StaticCodeSize == 0)
|
||||||
|
{
|
||||||
|
_ptc.Profiler.StaticCodeStart = address;
|
||||||
|
_ptc.Profiler.StaticCodeSize = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Execute(State.ExecutionContext context, ulong address)
|
public void Execute(State.ExecutionContext context, ulong address)
|
||||||
{
|
{
|
||||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||||
{
|
{
|
||||||
IsReadyForTranslation.WaitOne();
|
IsReadyForTranslation.WaitOne();
|
||||||
|
|
||||||
if (Ptc.State == PtcState.Enabled)
|
if (_ptc.State == PtcState.Enabled)
|
||||||
{
|
{
|
||||||
Debug.Assert(Functions.Count == 0);
|
Debug.Assert(Functions.Count == 0);
|
||||||
Ptc.LoadTranslations(this);
|
_ptc.LoadTranslations(this);
|
||||||
Ptc.MakeAndSaveTranslations(this);
|
_ptc.MakeAndSaveTranslations(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
PtcProfiler.Start();
|
_ptc.Profiler.Start();
|
||||||
|
|
||||||
Ptc.Disable();
|
_ptc.Disable();
|
||||||
|
|
||||||
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
|
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
|
||||||
// etc). All threads are normal priority except from the last, which just fills as much of the last core
|
// etc). All threads are normal priority except from the last, which just fills as much of the last core
|
||||||
@@ -148,6 +167,12 @@ namespace ARMeilleure.Translation
|
|||||||
Stubs.Dispose();
|
Stubs.Dispose();
|
||||||
FunctionTable.Dispose();
|
FunctionTable.Dispose();
|
||||||
CountTable.Dispose();
|
CountTable.Dispose();
|
||||||
|
|
||||||
|
_ptc.Close();
|
||||||
|
_ptc.Profiler.Stop();
|
||||||
|
|
||||||
|
_ptc.Dispose();
|
||||||
|
_ptc.Profiler.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,9 +214,9 @@ namespace ARMeilleure.Translation
|
|||||||
func = oldFunc;
|
func = oldFunc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PtcProfiler.Enabled)
|
if (_ptc.Profiler.Enabled)
|
||||||
{
|
{
|
||||||
PtcProfiler.AddEntry(address, mode, highCq: false);
|
_ptc.Profiler.AddEntry(address, mode, highCq: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterFunction(address, func);
|
RegisterFunction(address, func);
|
||||||
@@ -217,6 +242,7 @@ namespace ARMeilleure.Translation
|
|||||||
Stubs,
|
Stubs,
|
||||||
address,
|
address,
|
||||||
highCq,
|
highCq,
|
||||||
|
_ptc.State != PtcState.Disabled,
|
||||||
mode: Aarch32Mode.User);
|
mode: Aarch32Mode.User);
|
||||||
|
|
||||||
Logger.StartPass(PassName.Decoding);
|
Logger.StartPass(PassName.Decoding);
|
||||||
@@ -262,7 +288,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
|
||||||
|
|
||||||
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
GuestFunction func = compiledFunc.Map<GuestFunction>();
|
GuestFunction func = compiledFunc.Map<GuestFunction>();
|
||||||
@@ -284,16 +310,16 @@ namespace ARMeilleure.Translation
|
|||||||
return func;
|
return func;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (PtcProfiler.Enabled)
|
if (_ptc.Profiler.Enabled)
|
||||||
{
|
{
|
||||||
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
_ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterFunction(request.Address, func);
|
RegisterFunction(request.Address, func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct Range
|
private readonly struct Range
|
||||||
{
|
{
|
||||||
public ulong Start { get; }
|
public ulong Start { get; }
|
||||||
public ulong End { get; }
|
public ulong End { get; }
|
||||||
|
@@ -30,10 +30,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _dispatchStub.Value;
|
return _dispatchStub.Value;
|
||||||
}
|
}
|
||||||
@@ -47,10 +44,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _slowDispatchStub.Value;
|
return _slowDispatchStub.Value;
|
||||||
}
|
}
|
||||||
@@ -64,10 +58,7 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_disposed)
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _dispatchLoop.Value;
|
return _dispatchLoop.Value;
|
||||||
}
|
}
|
||||||
@@ -81,7 +72,9 @@ namespace ARMeilleure.Translation
|
|||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(Translator translator)
|
public TranslatorStubs(Translator translator)
|
||||||
{
|
{
|
||||||
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
|
ArgumentNullException.ThrowIfNull(translator);
|
||||||
|
|
||||||
|
_translator = translator;
|
||||||
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
|
||||||
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
|
||||||
}
|
}
|
||||||
|
54
Directory.Packages.props
Normal file
54
Directory.Packages.props
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageVersion Include="Avalonia" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
|
<PackageVersion Include="Crc32.NET" Version="1.2.0" />
|
||||||
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
|
<PackageVersion Include="DynamicData" Version="7.12.11" />
|
||||||
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
|
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||||
|
<PackageVersion Include="LibHac" Version="0.17.0" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
|
||||||
|
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||||
|
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||||
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
|
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
|
||||||
|
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
|
||||||
|
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
|
||||||
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
||||||
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" />
|
||||||
|
<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>
|
@@ -1,11 +1,11 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
|
<PackageReference Include="OpenTK.OpenAL" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
178
Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs
Normal file
178
Ryujinx.Audio.Backends.SoundIo/Native/SoundIo.cs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public static partial class SoundIo
|
||||||
|
{
|
||||||
|
private const string LibraryName = "libsoundio";
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void OnDeviceChangeNativeDelegate(IntPtr ctx);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void OnBackendDisconnectedDelegate(IntPtr ctx, SoundIoError err);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void OnEventsSignalDelegate(IntPtr ctx);
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void EmitRtPrioWarningDelegate();
|
||||||
|
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
public unsafe delegate void JackCallbackDelegate(IntPtr msg);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct SoundIoStruct
|
||||||
|
{
|
||||||
|
public IntPtr UserData;
|
||||||
|
public IntPtr OnDeviceChange;
|
||||||
|
public IntPtr OnBackendDisconnected;
|
||||||
|
public IntPtr OnEventsSignal;
|
||||||
|
public SoundIoBackend CurrentBackend;
|
||||||
|
public IntPtr ApplicationName;
|
||||||
|
public IntPtr EmitRtPrioWarning;
|
||||||
|
public IntPtr JackInfoCallback;
|
||||||
|
public IntPtr JackErrorCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SoundIoChannelLayout
|
||||||
|
{
|
||||||
|
public IntPtr Name;
|
||||||
|
public int ChannelCount;
|
||||||
|
public Array24<SoundIoChannelId> Channels;
|
||||||
|
|
||||||
|
public static IntPtr GetDefault(int channelCount)
|
||||||
|
{
|
||||||
|
return soundio_channel_layout_get_default(channelCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static unsafe SoundIoChannelLayout GetDefaultValue(int channelCount)
|
||||||
|
{
|
||||||
|
return Unsafe.AsRef<SoundIoChannelLayout>((SoundIoChannelLayout*)GetDefault(channelCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SoundIoSampleRateRange
|
||||||
|
{
|
||||||
|
public int Min;
|
||||||
|
public int Max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SoundIoDevice
|
||||||
|
{
|
||||||
|
public IntPtr SoundIo;
|
||||||
|
public IntPtr Id;
|
||||||
|
public IntPtr Name;
|
||||||
|
public SoundIoDeviceAim Aim;
|
||||||
|
public IntPtr Layouts;
|
||||||
|
public int LayoutCount;
|
||||||
|
public SoundIoChannelLayout CurrentLayout;
|
||||||
|
public IntPtr Formats;
|
||||||
|
public int FormatCount;
|
||||||
|
public SoundIoFormat CurrentFormat;
|
||||||
|
public IntPtr SampleRates;
|
||||||
|
public int SampleRateCount;
|
||||||
|
public int SampleRateCurrent;
|
||||||
|
public double SoftwareLatencyMin;
|
||||||
|
public double SoftwareLatencyMax;
|
||||||
|
public double SoftwareLatencyCurrent;
|
||||||
|
public bool IsRaw;
|
||||||
|
public int RefCount;
|
||||||
|
public SoundIoError ProbeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SoundIoOutStream
|
||||||
|
{
|
||||||
|
public IntPtr Device;
|
||||||
|
public SoundIoFormat Format;
|
||||||
|
public int SampleRate;
|
||||||
|
public SoundIoChannelLayout Layout;
|
||||||
|
public double SoftwareLatency;
|
||||||
|
public float Volume;
|
||||||
|
public IntPtr UserData;
|
||||||
|
public IntPtr WriteCallback;
|
||||||
|
public IntPtr UnderflowCallback;
|
||||||
|
public IntPtr ErrorCallback;
|
||||||
|
public IntPtr Name;
|
||||||
|
public bool NonTerminalHint;
|
||||||
|
public int BytesPerFrame;
|
||||||
|
public int BytesPerSample;
|
||||||
|
public SoundIoError LayoutError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct SoundIoChannelArea
|
||||||
|
{
|
||||||
|
public IntPtr Pointer;
|
||||||
|
public int Step;
|
||||||
|
}
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial IntPtr soundio_create();
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_connect(IntPtr ctx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial void soundio_disconnect(IntPtr ctx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial void soundio_flush_events(IntPtr ctx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial int soundio_output_device_count(IntPtr ctx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial int soundio_default_output_device_index(IntPtr ctx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial IntPtr soundio_get_output_device(IntPtr ctx, int index);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static partial bool soundio_device_supports_format(IntPtr devCtx, SoundIoFormat format);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static partial bool soundio_device_supports_layout(IntPtr devCtx, IntPtr layout);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static partial bool soundio_device_supports_sample_rate(IntPtr devCtx, int sampleRate);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial IntPtr soundio_outstream_create(IntPtr devCtx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_outstream_open(IntPtr outStreamCtx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_outstream_start(IntPtr outStreamCtx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_outstream_begin_write(IntPtr outStreamCtx, IntPtr areas, IntPtr frameCount);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_outstream_end_write(IntPtr outStreamCtx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_outstream_pause(IntPtr devCtx, [MarshalAs(UnmanagedType.Bool)] bool pause);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial SoundIoError soundio_outstream_set_volume(IntPtr devCtx, double volume);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial void soundio_outstream_destroy(IntPtr streamCtx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial void soundio_destroy(IntPtr ctx);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial IntPtr soundio_channel_layout_get_default(int channelCount);
|
||||||
|
|
||||||
|
[LibraryImport(LibraryName)]
|
||||||
|
public static partial IntPtr soundio_strerror(SoundIoError err);
|
||||||
|
}
|
||||||
|
}
|
13
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs
Normal file
13
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoBackend.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public enum SoundIoBackend : int
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Jack = 1,
|
||||||
|
PulseAudio = 2,
|
||||||
|
Alsa = 3,
|
||||||
|
CoreAudio = 4,
|
||||||
|
Wasapi = 5,
|
||||||
|
Dummy = 6
|
||||||
|
}
|
||||||
|
}
|
75
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs
Normal file
75
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoChannelId.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public enum SoundIoChannelId
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
FrontLeft = 1,
|
||||||
|
FrontRight = 2,
|
||||||
|
FrontCenter = 3,
|
||||||
|
Lfe = 4,
|
||||||
|
BackLeft = 5,
|
||||||
|
BackRight = 6,
|
||||||
|
FrontLeftCenter = 7,
|
||||||
|
FrontRightCenter = 8,
|
||||||
|
BackCenter = 9,
|
||||||
|
SideLeft = 10,
|
||||||
|
SideRight = 11,
|
||||||
|
TopCenter = 12,
|
||||||
|
TopFrontLeft = 13,
|
||||||
|
TopFrontCenter = 14,
|
||||||
|
TopFrontRight = 15,
|
||||||
|
TopBackLeft = 16,
|
||||||
|
TopBackCenter = 17,
|
||||||
|
TopBackRight = 18,
|
||||||
|
BackLeftCenter = 19,
|
||||||
|
BackRightCenter = 20,
|
||||||
|
FrontLeftWide = 21,
|
||||||
|
FrontRightWide = 22,
|
||||||
|
FrontLeftHigh = 23,
|
||||||
|
FrontCenterHigh = 24,
|
||||||
|
FrontRightHigh = 25,
|
||||||
|
TopFrontLeftCenter = 26,
|
||||||
|
TopFrontRightCenter = 27,
|
||||||
|
TopSideLeft = 28,
|
||||||
|
TopSideRight = 29,
|
||||||
|
LeftLfe = 30,
|
||||||
|
RightLfe = 31,
|
||||||
|
Lfe2 = 32,
|
||||||
|
BottomCenter = 33,
|
||||||
|
BottomLeftCenter = 34,
|
||||||
|
BottomRightCenter = 35,
|
||||||
|
MsMid = 36,
|
||||||
|
MsSide = 37,
|
||||||
|
AmbisonicW = 38,
|
||||||
|
AmbisonicX = 39,
|
||||||
|
AmbisonicY = 40,
|
||||||
|
AmbisonicZ = 41,
|
||||||
|
XyX = 42,
|
||||||
|
XyY = 43,
|
||||||
|
HeadphonesLeft = 44,
|
||||||
|
HeadphonesRight = 45,
|
||||||
|
ClickTrack = 46,
|
||||||
|
ForeignLanguage = 47,
|
||||||
|
HearingImpaired = 48,
|
||||||
|
Narration = 49,
|
||||||
|
Haptic = 50,
|
||||||
|
DialogCentricMix = 51,
|
||||||
|
Aux = 52,
|
||||||
|
Aux0 = 53,
|
||||||
|
Aux1 = 54,
|
||||||
|
Aux2 = 55,
|
||||||
|
Aux3 = 56,
|
||||||
|
Aux4 = 57,
|
||||||
|
Aux5 = 58,
|
||||||
|
Aux6 = 59,
|
||||||
|
Aux7 = 60,
|
||||||
|
Aux8 = 61,
|
||||||
|
Aux9 = 62,
|
||||||
|
Aux10 = 63,
|
||||||
|
Aux11 = 64,
|
||||||
|
Aux12 = 65,
|
||||||
|
Aux13 = 66,
|
||||||
|
Aux14 = 67,
|
||||||
|
Aux15 = 68,
|
||||||
|
}
|
||||||
|
}
|
107
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs
Normal file
107
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoContext.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public class SoundIoContext : IDisposable
|
||||||
|
{
|
||||||
|
private IntPtr _context;
|
||||||
|
private Action<SoundIoError> _onBackendDisconnect;
|
||||||
|
private OnBackendDisconnectedDelegate _onBackendDisconnectNative;
|
||||||
|
|
||||||
|
public IntPtr Context => _context;
|
||||||
|
|
||||||
|
internal SoundIoContext(IntPtr context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_onBackendDisconnect = null;
|
||||||
|
_onBackendDisconnectNative = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIoError Connect() => soundio_connect(_context);
|
||||||
|
public void Disconnect() => soundio_disconnect(_context);
|
||||||
|
|
||||||
|
public void FlushEvents() => soundio_flush_events(_context);
|
||||||
|
|
||||||
|
public int OutputDeviceCount => soundio_output_device_count(_context);
|
||||||
|
|
||||||
|
public int DefaultOutputDeviceIndex => soundio_default_output_device_index(_context);
|
||||||
|
|
||||||
|
public Action<SoundIoError> OnBackendDisconnect
|
||||||
|
{
|
||||||
|
get { return _onBackendDisconnect; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_onBackendDisconnect = value;
|
||||||
|
|
||||||
|
if (_onBackendDisconnect == null)
|
||||||
|
{
|
||||||
|
_onBackendDisconnectNative = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_onBackendDisconnectNative = (ctx, err) => _onBackendDisconnect(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetContext().OnBackendDisconnected = Marshal.GetFunctionPointerForDelegate(_onBackendDisconnectNative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ref SoundIoStruct GetContext()
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
return ref Unsafe.AsRef<SoundIoStruct>((SoundIoStruct*)_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIoDeviceContext GetOutputDevice(int index)
|
||||||
|
{
|
||||||
|
IntPtr deviceContext = soundio_get_output_device(_context, index);
|
||||||
|
|
||||||
|
if (deviceContext == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SoundIoDeviceContext(deviceContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SoundIoContext Create()
|
||||||
|
{
|
||||||
|
IntPtr context = soundio_create();
|
||||||
|
|
||||||
|
if (context == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SoundIoContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
IntPtr currentContext = Interlocked.Exchange(ref _context, IntPtr.Zero);
|
||||||
|
|
||||||
|
if (currentContext != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
soundio_destroy(currentContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SoundIoContext()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public enum SoundIoDeviceAim
|
||||||
|
{
|
||||||
|
SoundIoDeviceAimInput = 0,
|
||||||
|
SoundIoDeviceAimOutput = 1
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public class SoundIoDeviceContext
|
||||||
|
{
|
||||||
|
private readonly IntPtr _context;
|
||||||
|
|
||||||
|
public IntPtr Context => _context;
|
||||||
|
|
||||||
|
internal SoundIoDeviceContext(IntPtr context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ref SoundIoDevice GetDeviceContext()
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
return ref Unsafe.AsRef<SoundIoDevice>((SoundIoDevice*)_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRaw => GetDeviceContext().IsRaw;
|
||||||
|
|
||||||
|
public string Id => Marshal.PtrToStringAnsi(GetDeviceContext().Id);
|
||||||
|
|
||||||
|
public bool SupportsSampleRate(int sampleRate) => soundio_device_supports_sample_rate(_context, sampleRate);
|
||||||
|
|
||||||
|
public bool SupportsFormat(SoundIoFormat format) => soundio_device_supports_format(_context, format);
|
||||||
|
|
||||||
|
public bool SupportsChannelCount(int channelCount) => soundio_device_supports_layout(_context, SoundIoChannelLayout.GetDefault(channelCount));
|
||||||
|
|
||||||
|
public SoundIoOutStreamContext CreateOutStream()
|
||||||
|
{
|
||||||
|
IntPtr context = soundio_outstream_create(_context);
|
||||||
|
|
||||||
|
if (context == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SoundIoOutStreamContext(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs
Normal file
22
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoError.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public enum SoundIoError
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
NoMem = 1,
|
||||||
|
InitAudioBackend = 2,
|
||||||
|
SystemResources = 3,
|
||||||
|
OpeningDevice = 4,
|
||||||
|
NoSuchDevice = 5,
|
||||||
|
Invalid = 6,
|
||||||
|
BackendUnavailable = 7,
|
||||||
|
Streaming = 8,
|
||||||
|
IncompatibleDevice = 9,
|
||||||
|
NoSuchClient = 10,
|
||||||
|
IncompatibleBackend = 11,
|
||||||
|
BackendDisconnected = 12,
|
||||||
|
Interrupted = 13,
|
||||||
|
Underflow = 14,
|
||||||
|
EncodingString = 15,
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs
Normal file
11
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoException.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
internal class SoundIoException : Exception
|
||||||
|
{
|
||||||
|
internal SoundIoException(SoundIoError error) : base(Marshal.PtrToStringAnsi(soundio_strerror(error))) { }
|
||||||
|
}
|
||||||
|
}
|
25
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs
Normal file
25
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoFormat.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public enum SoundIoFormat
|
||||||
|
{
|
||||||
|
Invalid = 0,
|
||||||
|
S8 = 1,
|
||||||
|
U8 = 2,
|
||||||
|
S16LE = 3,
|
||||||
|
S16BE = 4,
|
||||||
|
U16LE = 5,
|
||||||
|
U16BE = 6,
|
||||||
|
S24LE = 7,
|
||||||
|
S24BE = 8,
|
||||||
|
U24LE = 9,
|
||||||
|
U24BE = 10,
|
||||||
|
S32LE = 11,
|
||||||
|
S32BE = 12,
|
||||||
|
U32LE = 13,
|
||||||
|
U32BE = 14,
|
||||||
|
Float32LE = 15,
|
||||||
|
Float32BE = 16,
|
||||||
|
Float64LE = 17,
|
||||||
|
Float64BE = 18,
|
||||||
|
}
|
||||||
|
}
|
164
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs
Normal file
164
Ryujinx.Audio.Backends.SoundIo/Native/SoundIoOutStreamContext.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||||
|
{
|
||||||
|
public class SoundIoOutStreamContext : IDisposable
|
||||||
|
{
|
||||||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
private unsafe delegate void WriteCallbackDelegate(IntPtr ctx, int frameCountMin, int frameCountMax);
|
||||||
|
|
||||||
|
private IntPtr _context;
|
||||||
|
private IntPtr _nameStored;
|
||||||
|
private Action<int, int> _writeCallback;
|
||||||
|
private WriteCallbackDelegate _writeCallbackNative;
|
||||||
|
|
||||||
|
public IntPtr Context => _context;
|
||||||
|
|
||||||
|
internal SoundIoOutStreamContext(IntPtr context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_nameStored = IntPtr.Zero;
|
||||||
|
_writeCallback = null;
|
||||||
|
_writeCallbackNative = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ref SoundIoOutStream GetOutContext()
|
||||||
|
{
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
return ref Unsafe.AsRef<SoundIoOutStream>((SoundIoOutStream*)_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => Marshal.PtrToStringAnsi(GetOutContext().Name);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var context = GetOutContext();
|
||||||
|
|
||||||
|
if (_nameStored != IntPtr.Zero && context.Name == _nameStored)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(_nameStored);
|
||||||
|
}
|
||||||
|
|
||||||
|
_nameStored = Marshal.StringToHGlobalAnsi(value);
|
||||||
|
GetOutContext().Name = _nameStored;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIoChannelLayout Layout
|
||||||
|
{
|
||||||
|
get => GetOutContext().Layout;
|
||||||
|
set => GetOutContext().Layout = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SoundIoFormat Format
|
||||||
|
{
|
||||||
|
get => GetOutContext().Format;
|
||||||
|
set => GetOutContext().Format = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int SampleRate
|
||||||
|
{
|
||||||
|
get => GetOutContext().SampleRate;
|
||||||
|
set => GetOutContext().SampleRate = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Volume
|
||||||
|
{
|
||||||
|
get => GetOutContext().Volume;
|
||||||
|
set => GetOutContext().Volume = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int BytesPerFrame
|
||||||
|
{
|
||||||
|
get => GetOutContext().BytesPerFrame;
|
||||||
|
set => GetOutContext().BytesPerFrame = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int BytesPerSample
|
||||||
|
{
|
||||||
|
get => GetOutContext().BytesPerSample;
|
||||||
|
set => GetOutContext().BytesPerSample = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action<int, int> WriteCallback
|
||||||
|
{
|
||||||
|
get { return _writeCallback; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_writeCallback = value;
|
||||||
|
|
||||||
|
if (_writeCallback == null)
|
||||||
|
{
|
||||||
|
_writeCallbackNative = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_writeCallbackNative = (ctx, frameCountMin, frameCountMax) => _writeCallback(frameCountMin, frameCountMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
GetOutContext().WriteCallback = Marshal.GetFunctionPointerForDelegate(_writeCallbackNative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CheckError(SoundIoError error)
|
||||||
|
{
|
||||||
|
if (error != SoundIoError.None)
|
||||||
|
{
|
||||||
|
throw new SoundIoException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Open() => CheckError(soundio_outstream_open(_context));
|
||||||
|
|
||||||
|
public void Start() => CheckError(soundio_outstream_start(_context));
|
||||||
|
|
||||||
|
public void Pause(bool pause) => CheckError(soundio_outstream_pause(_context, pause));
|
||||||
|
|
||||||
|
public void SetVolume(double volume) => CheckError(soundio_outstream_set_volume(_context, volume));
|
||||||
|
|
||||||
|
public Span<SoundIoChannelArea> BeginWrite(ref int frameCount)
|
||||||
|
{
|
||||||
|
IntPtr arenas = default;
|
||||||
|
int nativeFrameCount = frameCount;
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var frameCountPtr = &nativeFrameCount;
|
||||||
|
var arenasPtr = &arenas;
|
||||||
|
CheckError(soundio_outstream_begin_write(_context, (IntPtr)arenasPtr, (IntPtr)frameCountPtr));
|
||||||
|
|
||||||
|
frameCount = *frameCountPtr;
|
||||||
|
|
||||||
|
return new Span<SoundIoChannelArea>((void*)arenas, Layout.ChannelCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndWrite() => CheckError(soundio_outstream_end_write(_context));
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_context != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
soundio_outstream_destroy(_context);
|
||||||
|
_context = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SoundIoOutStreamContext()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public static class MarshalEx
|
|
||||||
{
|
|
||||||
public static double ReadDouble(IntPtr handle, int offset = 0)
|
|
||||||
{
|
|
||||||
return BitConverter.Int64BitsToDouble(Marshal.ReadInt64(handle, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteDouble(IntPtr handle, double value)
|
|
||||||
{
|
|
||||||
WriteDouble(handle, 0, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteDouble(IntPtr handle, int offset, double value)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt64(handle, offset, BitConverter.DoubleToInt64Bits(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float ReadFloat(IntPtr handle, int offset = 0)
|
|
||||||
{
|
|
||||||
return BitConverter.Int32BitsToSingle(Marshal.ReadInt32(handle, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteFloat(IntPtr handle, float value)
|
|
||||||
{
|
|
||||||
WriteFloat(handle, 0, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void WriteFloat(IntPtr handle, int offset, float value)
|
|
||||||
{
|
|
||||||
Marshal.WriteInt32(handle, offset, BitConverter.SingleToInt32Bits(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,386 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIO : IDisposable
|
|
||||||
{
|
|
||||||
Pointer<SoundIo> handle;
|
|
||||||
|
|
||||||
public SoundIO()
|
|
||||||
{
|
|
||||||
handle = Natives.soundio_create();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal SoundIO(Pointer<SoundIo> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose ()
|
|
||||||
{
|
|
||||||
foreach (var h in allocated_hglobals)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
Natives.soundio_destroy(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equality (based on handle)
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
var d = other as SoundIO;
|
|
||||||
|
|
||||||
return d != null && this.handle == d.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIO obj1, SoundIO obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIO obj1, SoundIO obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields
|
|
||||||
|
|
||||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
|
||||||
// this kind of code anywhere we need string marshaling.
|
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
|
||||||
|
|
||||||
public string ApplicationName {
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, app_name_offset)); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var existing = Marshal.ReadIntPtr(handle, app_name_offset);
|
|
||||||
if (allocated_hglobals.Contains (existing))
|
|
||||||
{
|
|
||||||
allocated_hglobals.Remove(existing);
|
|
||||||
Marshal.FreeHGlobal(existing);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
|
||||||
Marshal.WriteIntPtr(handle, app_name_offset, ptr);
|
|
||||||
allocated_hglobals.Add(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int app_name_offset = (int)Marshal.OffsetOf<SoundIo>("app_name");
|
|
||||||
|
|
||||||
public SoundIOBackend CurrentBackend
|
|
||||||
{
|
|
||||||
get { return (SoundIOBackend)Marshal.ReadInt32(handle, current_backend_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int current_backend_offset = (int)Marshal.OffsetOf<SoundIo>("current_backend");
|
|
||||||
|
|
||||||
// emit_rtprio_warning
|
|
||||||
public Action EmitRealtimePriorityWarning
|
|
||||||
{
|
|
||||||
get { return emit_rtprio_warning; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
emit_rtprio_warning = value;
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change);
|
|
||||||
|
|
||||||
Marshal.WriteIntPtr(handle, emit_rtprio_warning_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int emit_rtprio_warning_offset = (int)Marshal.OffsetOf<SoundIo>("emit_rtprio_warning");
|
|
||||||
|
|
||||||
Action emit_rtprio_warning;
|
|
||||||
|
|
||||||
// jack_error_callback
|
|
||||||
public Action<string> JackErrorCallback
|
|
||||||
{
|
|
||||||
get { return jack_error_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
jack_error_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
jack_error_callback = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jack_error_callback_native = msg => jack_error_callback(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(jack_error_callback_native);
|
|
||||||
Marshal.WriteIntPtr(handle, jack_error_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int jack_error_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_error_callback");
|
|
||||||
|
|
||||||
Action<string> jack_error_callback;
|
|
||||||
delegate void jack_error_delegate(string message);
|
|
||||||
jack_error_delegate jack_error_callback_native;
|
|
||||||
|
|
||||||
// jack_info_callback
|
|
||||||
public Action<string> JackInfoCallback
|
|
||||||
{
|
|
||||||
get { return jack_info_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
jack_info_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
jack_info_callback = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jack_info_callback_native = msg => jack_info_callback(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(jack_info_callback_native);
|
|
||||||
Marshal.WriteIntPtr(handle, jack_info_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int jack_info_callback_offset = (int)Marshal.OffsetOf<SoundIo>("jack_info_callback");
|
|
||||||
|
|
||||||
Action<string> jack_info_callback;
|
|
||||||
delegate void jack_info_delegate(string message);
|
|
||||||
jack_info_delegate jack_info_callback_native;
|
|
||||||
|
|
||||||
// on_backend_disconnect
|
|
||||||
public Action<int> OnBackendDisconnect
|
|
||||||
{
|
|
||||||
get { return on_backend_disconnect; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
on_backend_disconnect = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
on_backend_disconnect_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
on_backend_disconnect_native = (sio, err) => on_backend_disconnect(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_backend_disconnect_native);
|
|
||||||
Marshal.WriteIntPtr(handle, on_backend_disconnect_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int on_backend_disconnect_offset = (int)Marshal.OffsetOf<SoundIo>("on_backend_disconnect");
|
|
||||||
|
|
||||||
Action<int> on_backend_disconnect;
|
|
||||||
delegate void on_backend_disconnect_delegate(IntPtr handle, int errorCode);
|
|
||||||
on_backend_disconnect_delegate on_backend_disconnect_native;
|
|
||||||
|
|
||||||
// on_devices_change
|
|
||||||
public Action OnDevicesChange
|
|
||||||
{
|
|
||||||
get { return on_devices_change; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
on_devices_change = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
on_devices_change_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
on_devices_change_native = sio => on_devices_change();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_devices_change_native);
|
|
||||||
Marshal.WriteIntPtr(handle, on_devices_change_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int on_devices_change_offset = (int)Marshal.OffsetOf<SoundIo>("on_devices_change");
|
|
||||||
|
|
||||||
Action on_devices_change;
|
|
||||||
delegate void on_devices_change_delegate(IntPtr handle);
|
|
||||||
on_devices_change_delegate on_devices_change_native;
|
|
||||||
|
|
||||||
// on_events_signal
|
|
||||||
public Action OnEventsSignal
|
|
||||||
{
|
|
||||||
get { return on_events_signal; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
on_events_signal = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
on_events_signal_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
on_events_signal_native = sio => on_events_signal();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(on_events_signal_native);
|
|
||||||
Marshal.WriteIntPtr(handle, on_events_signal_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int on_events_signal_offset = (int)Marshal.OffsetOf<SoundIo>("on_events_signal");
|
|
||||||
|
|
||||||
Action on_events_signal;
|
|
||||||
delegate void on_events_signal_delegate(IntPtr handle);
|
|
||||||
on_events_signal_delegate on_events_signal_native;
|
|
||||||
|
|
||||||
|
|
||||||
// functions
|
|
||||||
|
|
||||||
public int BackendCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_backend_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int InputDeviceCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_input_device_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int OutputDeviceCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_output_device_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int DefaultInputDeviceIndex
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_default_input_device_index(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int DefaultOutputDeviceIndex
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_default_output_device_index(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOBackend GetBackend(int index)
|
|
||||||
{
|
|
||||||
return (SoundIOBackend)Natives.soundio_get_backend(handle, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIODevice GetInputDevice(int index)
|
|
||||||
{
|
|
||||||
return new SoundIODevice(Natives.soundio_get_input_device(handle, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIODevice GetOutputDevice(int index)
|
|
||||||
{
|
|
||||||
return new SoundIODevice(Natives.soundio_get_output_device(handle, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Connect()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_connect(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ConnectBackend(SoundIOBackend backend)
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_connect_backend(handle, (SoundIoBackend)backend);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Disconnect()
|
|
||||||
{
|
|
||||||
Natives.soundio_disconnect(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FlushEvents()
|
|
||||||
{
|
|
||||||
Natives.soundio_flush_events(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WaitEvents()
|
|
||||||
{
|
|
||||||
Natives.soundio_wait_events(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Wakeup()
|
|
||||||
{
|
|
||||||
Natives.soundio_wakeup(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForceDeviceScan()
|
|
||||||
{
|
|
||||||
Natives.soundio_force_device_scan(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIORingBuffer CreateRingBuffer(int capacity)
|
|
||||||
{
|
|
||||||
return new SoundIORingBuffer(Natives.soundio_ring_buffer_create(handle, capacity));
|
|
||||||
}
|
|
||||||
|
|
||||||
// static methods
|
|
||||||
|
|
||||||
public static string VersionString
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Natives.soundio_version_string()); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int VersionMajor
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_version_major(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int VersionMinor
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_version_minor(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int VersionPatch
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_version_patch(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetBackendName(SoundIOBackend backend)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringAnsi(Natives.soundio_backend_name((SoundIoBackend)backend));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool HaveBackend(SoundIOBackend backend)
|
|
||||||
{
|
|
||||||
return Natives.soundio_have_backend((SoundIoBackend)backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBytesPerSample(SoundIOFormat format)
|
|
||||||
{
|
|
||||||
return Natives.soundio_get_bytes_per_sample((SoundIoFormat)format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBytesPerFrame(SoundIOFormat format, int channelCount)
|
|
||||||
{
|
|
||||||
return Natives.soundio_get_bytes_per_frame((SoundIoFormat)format, channelCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetBytesPerSecond(SoundIOFormat format, int channelCount, int sampleRate)
|
|
||||||
{
|
|
||||||
return Natives.soundio_get_bytes_per_second((SoundIoFormat)format, channelCount, sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetSoundFormatName(SoundIOFormat format)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringAnsi(Natives.soundio_format_string((SoundIoFormat)format));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,13 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIOBackend
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Jack,
|
|
||||||
PulseAudio,
|
|
||||||
Alsa,
|
|
||||||
CoreAudio,
|
|
||||||
Wasapi,
|
|
||||||
Dummy
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOChannelArea
|
|
||||||
{
|
|
||||||
internal SoundIOChannelArea(Pointer<SoundIoChannelArea> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pointer<SoundIoChannelArea> handle;
|
|
||||||
|
|
||||||
public IntPtr Pointer
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadIntPtr(handle, ptr_offset); }
|
|
||||||
set { Marshal.WriteIntPtr(handle, ptr_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int ptr_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("ptr");
|
|
||||||
|
|
||||||
public int Step
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, step_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int step_offset = (int)Marshal.OffsetOf<SoundIoChannelArea>("step");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,34 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOChannelAreas
|
|
||||||
{
|
|
||||||
static readonly int native_size = Marshal.SizeOf<SoundIoChannelArea>();
|
|
||||||
|
|
||||||
internal SoundIOChannelAreas(IntPtr head, int channelCount, int frameCount)
|
|
||||||
{
|
|
||||||
this.head = head;
|
|
||||||
this.channel_count = channelCount;
|
|
||||||
this.frame_count = frameCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr head;
|
|
||||||
int channel_count;
|
|
||||||
int frame_count;
|
|
||||||
|
|
||||||
public bool IsEmpty
|
|
||||||
{
|
|
||||||
get { return head == IntPtr.Zero; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOChannelArea GetArea(int channel)
|
|
||||||
{
|
|
||||||
return new SoundIOChannelArea(head + native_size * channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ChannelCount => channel_count;
|
|
||||||
public int FrameCount => frame_count;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,75 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIOChannelId
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
FrontLeft,
|
|
||||||
FrontRight,
|
|
||||||
FrontCenter,
|
|
||||||
Lfe,
|
|
||||||
BackLeft,
|
|
||||||
BackRight,
|
|
||||||
FrontLeftCenter,
|
|
||||||
FrontRightCenter,
|
|
||||||
BackCenter,
|
|
||||||
SideLeft,
|
|
||||||
SideRight,
|
|
||||||
TopCenter,
|
|
||||||
TopFrontLeft,
|
|
||||||
TopFrontCenter,
|
|
||||||
TopFrontRight,
|
|
||||||
TopBackLeft,
|
|
||||||
TopBackCenter,
|
|
||||||
TopBackRight,
|
|
||||||
BackLeftCenter,
|
|
||||||
BackRightCenter,
|
|
||||||
FrontLeftWide,
|
|
||||||
FrontRightWide,
|
|
||||||
FrontLeftHigh,
|
|
||||||
FrontCenterHigh,
|
|
||||||
FrontRightHigh,
|
|
||||||
TopFrontLeftCenter,
|
|
||||||
TopFrontRightCenter,
|
|
||||||
TopSideLeft,
|
|
||||||
TopSideRight,
|
|
||||||
LeftLfe,
|
|
||||||
RightLfe,
|
|
||||||
Lfe2,
|
|
||||||
BottomCenter,
|
|
||||||
BottomLeftCenter,
|
|
||||||
BottomRightCenter,
|
|
||||||
MsMid,
|
|
||||||
MsSide,
|
|
||||||
AmbisonicW,
|
|
||||||
AmbisonicX,
|
|
||||||
AmbisonicY,
|
|
||||||
AmbisonicZ,
|
|
||||||
XyX,
|
|
||||||
XyY,
|
|
||||||
HeadphonesLeft,
|
|
||||||
HeadphonesRight,
|
|
||||||
ClickTrack,
|
|
||||||
ForeignLanguage,
|
|
||||||
HearingImpaired,
|
|
||||||
Narration,
|
|
||||||
Haptic,
|
|
||||||
DialogCentricMix,
|
|
||||||
Aux,
|
|
||||||
Aux0,
|
|
||||||
Aux1,
|
|
||||||
Aux2,
|
|
||||||
Aux3,
|
|
||||||
Aux4,
|
|
||||||
Aux5,
|
|
||||||
Aux6,
|
|
||||||
Aux7,
|
|
||||||
Aux8,
|
|
||||||
Aux9,
|
|
||||||
Aux10,
|
|
||||||
Aux11,
|
|
||||||
Aux12,
|
|
||||||
Aux13,
|
|
||||||
Aux14,
|
|
||||||
Aux15
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,116 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOChannelLayout
|
|
||||||
{
|
|
||||||
public static int BuiltInCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_channel_layout_builtin_count(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelLayout GetBuiltIn(int index)
|
|
||||||
{
|
|
||||||
return new SoundIOChannelLayout(Natives.soundio_channel_layout_get_builtin(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelLayout GetDefault(int channelCount)
|
|
||||||
{
|
|
||||||
var handle = Natives.soundio_channel_layout_get_default(channelCount);
|
|
||||||
|
|
||||||
return new SoundIOChannelLayout (handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SoundIOChannelId ParseChannelId(string name)
|
|
||||||
{
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(name);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (SoundIOChannelId)Natives.soundio_parse_channel_id(ptr, name.Length);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// instance members
|
|
||||||
|
|
||||||
internal SoundIOChannelLayout(Pointer<SoundIoChannelLayout> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly Pointer<SoundIoChannelLayout> handle;
|
|
||||||
|
|
||||||
public bool IsNull
|
|
||||||
{
|
|
||||||
get { return handle.Handle == IntPtr.Zero; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IntPtr Handle
|
|
||||||
{
|
|
||||||
get { return handle; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ChannelCount
|
|
||||||
{
|
|
||||||
get { return IsNull ? 0 : Marshal.ReadInt32((IntPtr)handle + channel_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int channel_count_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channel_count");
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return IsNull ? null : Marshal.PtrToStringAnsi(Marshal.ReadIntPtr((IntPtr)handle + name_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("name");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOChannelId> Channels
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsNull) yield break;
|
|
||||||
|
|
||||||
for (int i = 0; i < 24; i++)
|
|
||||||
{
|
|
||||||
yield return (SoundIOChannelId)Marshal.ReadInt32((IntPtr)handle + channels_offset + sizeof(SoundIoChannelId) * i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int channels_offset = (int)Marshal.OffsetOf<SoundIoChannelLayout>("channels");
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
if (!(other is SoundIOChannelLayout)) return false;
|
|
||||||
|
|
||||||
var s = (SoundIOChannelLayout) other;
|
|
||||||
|
|
||||||
return handle == s.handle || Natives.soundio_channel_layout_equal(handle, s.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return handle.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string DetectBuiltInName()
|
|
||||||
{
|
|
||||||
if (IsNull) throw new InvalidOperationException();
|
|
||||||
|
|
||||||
return Natives.soundio_channel_layout_detect_builtin(handle) ? Name : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int FindChannel(SoundIOChannelId channel)
|
|
||||||
{
|
|
||||||
if (IsNull) throw new InvalidOperationException();
|
|
||||||
|
|
||||||
return Natives.soundio_channel_layout_find_channel(handle, (SoundIoChannelId)channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,267 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIODevice
|
|
||||||
{
|
|
||||||
public static SoundIOChannelLayout BestMatchingChannelLayout(SoundIODevice device1, SoundIODevice device2)
|
|
||||||
{
|
|
||||||
var ptr1 = Marshal.ReadIntPtr(device1.handle, layouts_offset);
|
|
||||||
var ptr2 = Marshal.ReadIntPtr(device2.handle, layouts_offset);
|
|
||||||
|
|
||||||
return new SoundIOChannelLayout(Natives.soundio_best_matching_channel_layout(ptr1, device1.LayoutCount, ptr2, device2.LayoutCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal SoundIODevice(Pointer<SoundIoDevice> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
readonly Pointer<SoundIoDevice> handle;
|
|
||||||
|
|
||||||
// Equality (based on handle and native func)
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
var d = other as SoundIODevice;
|
|
||||||
|
|
||||||
return d != null && (this.handle == d.handle || Natives.soundio_device_equal (this.handle, d.handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIODevice obj1, SoundIODevice obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIODevice obj1, SoundIODevice obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields
|
|
||||||
|
|
||||||
public SoundIODeviceAim Aim
|
|
||||||
{
|
|
||||||
get { return (SoundIODeviceAim)Marshal.ReadInt32(handle, aim_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int aim_offset = (int)Marshal.OffsetOf<SoundIoDevice>("aim");
|
|
||||||
|
|
||||||
public SoundIOFormat CurrentFormat
|
|
||||||
{
|
|
||||||
get { return (SoundIOFormat)Marshal.ReadInt32(handle, current_format_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int current_format_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_format");
|
|
||||||
|
|
||||||
public SoundIOChannelLayout CurrentLayout
|
|
||||||
{
|
|
||||||
get { return new SoundIOChannelLayout((IntPtr)handle + current_layout_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int current_layout_offset = (int)Marshal.OffsetOf<SoundIoDevice>("current_layout");
|
|
||||||
|
|
||||||
public int FormatCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, format_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int format_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("format_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOFormat> Formats
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var ptr = Marshal.ReadIntPtr(handle, formats_offset);
|
|
||||||
|
|
||||||
for (int i = 0; i < FormatCount; i++)
|
|
||||||
{
|
|
||||||
yield return (SoundIOFormat)Marshal.ReadInt32(ptr, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int formats_offset = (int)Marshal.OffsetOf<SoundIoDevice>("formats");
|
|
||||||
|
|
||||||
public string Id
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, id_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int id_offset = (int)Marshal.OffsetOf<SoundIoDevice>("id");
|
|
||||||
|
|
||||||
public bool IsRaw
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, is_raw_offset) != 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int is_raw_offset = (int)Marshal.OffsetOf<SoundIoDevice>("is_raw");
|
|
||||||
|
|
||||||
public int LayoutCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, layout_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layout_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layout_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOChannelLayout> Layouts
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var ptr = Marshal.ReadIntPtr (handle, layouts_offset);
|
|
||||||
|
|
||||||
for (int i = 0; i < LayoutCount; i++)
|
|
||||||
{
|
|
||||||
yield return new SoundIOChannelLayout(ptr + i * Marshal.SizeOf<SoundIoChannelLayout>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layouts_offset = (int)Marshal.OffsetOf<SoundIoDevice>("layouts");
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoDevice>("name");
|
|
||||||
|
|
||||||
public int ProbeError
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, probe_error_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int probe_error_offset = (int)Marshal.OffsetOf<SoundIoDevice>("probe_error");
|
|
||||||
|
|
||||||
public int ReferenceCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, ref_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int ref_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("ref_count");
|
|
||||||
|
|
||||||
public int SampleRateCount
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, sample_rate_count_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int sample_rate_count_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rate_count");
|
|
||||||
|
|
||||||
public IEnumerable<SoundIOSampleRateRange> SampleRates
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var ptr = Marshal.ReadIntPtr(handle, sample_rates_offset);
|
|
||||||
|
|
||||||
for (int i = 0; i < SampleRateCount; i++)
|
|
||||||
{
|
|
||||||
yield return new SoundIOSampleRateRange(Marshal.ReadInt32(ptr, i * 2), Marshal.ReadInt32(ptr, i * 2 + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int sample_rates_offset = (int)Marshal.OffsetOf<SoundIoDevice>("sample_rates");
|
|
||||||
|
|
||||||
public double SoftwareLatencyCurrent
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_current_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_current_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_current_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_current");
|
|
||||||
|
|
||||||
public double SoftwareLatencyMin
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_min_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_min_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_min_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_min");
|
|
||||||
|
|
||||||
public double SoftwareLatencyMax
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_max_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_max_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_max_offset = (int)Marshal.OffsetOf<SoundIoDevice>("software_latency_max");
|
|
||||||
|
|
||||||
public SoundIO SoundIO
|
|
||||||
{
|
|
||||||
get { return new SoundIO(Marshal.ReadIntPtr(handle, soundio_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int soundio_offset = (int)Marshal.OffsetOf<SoundIoDevice>("soundio");
|
|
||||||
|
|
||||||
// functions
|
|
||||||
|
|
||||||
public void AddReference()
|
|
||||||
{
|
|
||||||
Natives.soundio_device_ref(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveReference()
|
|
||||||
{
|
|
||||||
Natives.soundio_device_unref(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SortDeviceChannelLayouts()
|
|
||||||
{
|
|
||||||
Natives.soundio_device_sort_channel_layouts(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly SoundIOFormat S16NE = BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
|
||||||
public static readonly SoundIOFormat U16NE = BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
|
||||||
public static readonly SoundIOFormat S24NE = BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
|
||||||
public static readonly SoundIOFormat U24NE = BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
|
||||||
public static readonly SoundIOFormat S32NE = BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
|
||||||
public static readonly SoundIOFormat U32NE = BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
|
||||||
public static readonly SoundIOFormat Float32NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
|
||||||
public static readonly SoundIOFormat Float64NE = BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
|
||||||
public static readonly SoundIOFormat S16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S16LE : SoundIOFormat.S16BE;
|
|
||||||
public static readonly SoundIOFormat U16FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U16LE : SoundIOFormat.U16BE;
|
|
||||||
public static readonly SoundIOFormat S24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S24LE : SoundIOFormat.S24BE;
|
|
||||||
public static readonly SoundIOFormat U24FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U24LE : SoundIOFormat.U24BE;
|
|
||||||
public static readonly SoundIOFormat S32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.S32LE : SoundIOFormat.S32BE;
|
|
||||||
public static readonly SoundIOFormat U32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.U32LE : SoundIOFormat.U32BE;
|
|
||||||
public static readonly SoundIOFormat Float32FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float32LE : SoundIOFormat.Float32BE;
|
|
||||||
public static readonly SoundIOFormat Float64FE = !BitConverter.IsLittleEndian ? SoundIOFormat.Float64LE : SoundIOFormat.Float64BE;
|
|
||||||
|
|
||||||
public bool SupportsFormat(SoundIOFormat format)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_format(handle, (SoundIoFormat)format);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsSampleRate(int sampleRate)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_sample_rate(handle, sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsChannelCount(int channelCount)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_supports_layout(handle, SoundIOChannelLayout.GetDefault(channelCount).Handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetNearestSampleRate(int sampleRate)
|
|
||||||
{
|
|
||||||
return Natives.soundio_device_nearest_sample_rate(handle, sampleRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOInStream CreateInStream()
|
|
||||||
{
|
|
||||||
return new SoundIOInStream(Natives.soundio_instream_create(handle));
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOOutStream CreateOutStream()
|
|
||||||
{
|
|
||||||
return new SoundIOOutStream(Natives.soundio_outstream_create(handle));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,8 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIODeviceAim // soundio.h (228, 6)
|
|
||||||
{
|
|
||||||
Input,
|
|
||||||
Output
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,10 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIOException : Exception
|
|
||||||
{
|
|
||||||
internal SoundIOException(SoundIoError errorCode) : base (Marshal.PtrToStringAnsi(Natives.soundio_strerror((int) errorCode))) { }
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,25 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public enum SoundIOFormat
|
|
||||||
{
|
|
||||||
Invalid,
|
|
||||||
S8,
|
|
||||||
U8,
|
|
||||||
S16LE,
|
|
||||||
S16BE,
|
|
||||||
U16LE,
|
|
||||||
U16BE,
|
|
||||||
S24LE,
|
|
||||||
S24BE,
|
|
||||||
U24LE,
|
|
||||||
U24BE,
|
|
||||||
S32LE,
|
|
||||||
S32BE,
|
|
||||||
U32LE,
|
|
||||||
U32BE,
|
|
||||||
Float32LE,
|
|
||||||
Float32BE,
|
|
||||||
Float64LE,
|
|
||||||
Float64BE
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,293 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIOInStream : IDisposable
|
|
||||||
{
|
|
||||||
internal SoundIOInStream(Pointer<SoundIoInStream> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pointer<SoundIoInStream> handle;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Natives.soundio_instream_destroy(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equality (based on handle)
|
|
||||||
|
|
||||||
public override bool Equals(object other)
|
|
||||||
{
|
|
||||||
var d = other as SoundIOInStream;
|
|
||||||
|
|
||||||
return d != null && (this.handle == d.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIOInStream obj1, SoundIOInStream obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIOInStream obj1, SoundIOInStream obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields
|
|
||||||
|
|
||||||
public SoundIODevice Device
|
|
||||||
{
|
|
||||||
get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoInStream>("device");
|
|
||||||
|
|
||||||
public SoundIOFormat Format
|
|
||||||
{
|
|
||||||
get { return (SoundIOFormat)Marshal.ReadInt32(handle, format_offset); }
|
|
||||||
set { Marshal.WriteInt32(handle, format_offset, (int) value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoInStream>("format");
|
|
||||||
|
|
||||||
public int SampleRate
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, sample_rate_offset); }
|
|
||||||
set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoInStream>("sample_rate");
|
|
||||||
|
|
||||||
public SoundIOChannelLayout Layout
|
|
||||||
{
|
|
||||||
get { return new SoundIOChannelLayout ((IntPtr) handle + layout_offset); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Buffer.MemoryCopy((void*)((IntPtr)handle + layout_offset), (void*)value.Handle, Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout");
|
|
||||||
|
|
||||||
public double SoftwareLatency
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble(handle, software_latency_offset); }
|
|
||||||
set { MarshalEx.WriteDouble(handle, software_latency_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoInStream>("software_latency");
|
|
||||||
|
|
||||||
// error_callback
|
|
||||||
public Action ErrorCallback
|
|
||||||
{
|
|
||||||
get { return error_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
error_callback = value;
|
|
||||||
error_callback_native = _ => error_callback();
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
|
|
||||||
|
|
||||||
Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("error_callback");
|
|
||||||
|
|
||||||
Action error_callback;
|
|
||||||
delegate void error_callback_delegate(IntPtr handle);
|
|
||||||
error_callback_delegate error_callback_native;
|
|
||||||
|
|
||||||
// read_callback
|
|
||||||
public Action<int,int> ReadCallback
|
|
||||||
{
|
|
||||||
get { return read_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
read_callback = value;
|
|
||||||
read_callback_native = (_, minFrameCount, maxFrameCount) => read_callback(minFrameCount, maxFrameCount);
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(read_callback_native);
|
|
||||||
|
|
||||||
Marshal.WriteIntPtr(handle, read_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int read_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("read_callback");
|
|
||||||
|
|
||||||
Action<int, int> read_callback;
|
|
||||||
delegate void read_callback_delegate(IntPtr handle, int min, int max);
|
|
||||||
read_callback_delegate read_callback_native;
|
|
||||||
|
|
||||||
// overflow_callback
|
|
||||||
public Action OverflowCallback
|
|
||||||
{
|
|
||||||
get { return overflow_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
overflow_callback = value;
|
|
||||||
overflow_callback_native = _ => overflow_callback();
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(overflow_callback_native);
|
|
||||||
|
|
||||||
Marshal.WriteIntPtr(handle, overflow_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int overflow_callback_offset = (int)Marshal.OffsetOf<SoundIoInStream>("overflow_callback");
|
|
||||||
|
|
||||||
Action overflow_callback;
|
|
||||||
delegate void overflow_callback_delegate(IntPtr handle);
|
|
||||||
overflow_callback_delegate overflow_callback_native;
|
|
||||||
|
|
||||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
|
||||||
// this kind of code anywhere we need string marshaling.
|
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
|
||||||
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var existing = Marshal.ReadIntPtr(handle, name_offset);
|
|
||||||
if (allocated_hglobals.Contains(existing))
|
|
||||||
{
|
|
||||||
allocated_hglobals.Remove(existing);
|
|
||||||
Marshal.FreeHGlobal(existing);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
|
||||||
Marshal.WriteIntPtr(handle, name_offset, ptr);
|
|
||||||
allocated_hglobals.Add(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoInStream>("name");
|
|
||||||
|
|
||||||
public bool NonTerminalHint
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoInStream>("non_terminal_hint");
|
|
||||||
|
|
||||||
public int BytesPerFrame
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, bytes_per_frame_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoInStream>("bytes_per_frame");
|
|
||||||
|
|
||||||
public int BytesPerSample
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, bytes_per_sample_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoInStream>("bytes_per_sample");
|
|
||||||
|
|
||||||
public string LayoutErrorMessage
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var code = (SoundIoError)Marshal.ReadInt32(handle, layout_error_offset);
|
|
||||||
|
|
||||||
return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi(Natives.soundio_strerror((int)code));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layout_error_offset = (int)Marshal.OffsetOf<SoundIoInStream>("layout_error");
|
|
||||||
|
|
||||||
// functions
|
|
||||||
|
|
||||||
public void Open()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_open(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_start(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOChannelAreas BeginRead(ref int frameCount)
|
|
||||||
{
|
|
||||||
IntPtr ptrs = default;
|
|
||||||
int nativeFrameCount = frameCount;
|
|
||||||
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var frameCountPtr = &nativeFrameCount;
|
|
||||||
var ptrptr = &ptrs;
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_begin_read(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
|
|
||||||
|
|
||||||
frameCount = *frameCountPtr;
|
|
||||||
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndRead()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_end_read(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pause(bool pause)
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_pause(handle, pause);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double GetLatency()
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
double* dptr = null;
|
|
||||||
IntPtr p = new IntPtr(dptr);
|
|
||||||
|
|
||||||
var ret = (SoundIoError)Natives.soundio_instream_get_latency(handle, p);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
dptr = (double*)p;
|
|
||||||
|
|
||||||
return *dptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,331 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIOOutStream : IDisposable
|
|
||||||
{
|
|
||||||
internal SoundIOOutStream (Pointer<SoundIoOutStream> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
Pointer<SoundIoOutStream> handle;
|
|
||||||
|
|
||||||
public void Dispose ()
|
|
||||||
{
|
|
||||||
Natives.soundio_outstream_destroy (handle);
|
|
||||||
}
|
|
||||||
// Equality (based on handle)
|
|
||||||
|
|
||||||
public override bool Equals (object other)
|
|
||||||
{
|
|
||||||
var d = other as SoundIOOutStream;
|
|
||||||
|
|
||||||
return d != null && (this.handle == d.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode ()
|
|
||||||
{
|
|
||||||
return (int)(IntPtr)handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator == (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is null : obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator != (SoundIOOutStream obj1, SoundIOOutStream obj2)
|
|
||||||
{
|
|
||||||
return obj1 is null ? obj2 is object : !obj1.Equals(obj2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fields
|
|
||||||
|
|
||||||
public SoundIODevice Device
|
|
||||||
{
|
|
||||||
get { return new SoundIODevice(Marshal.ReadIntPtr(handle, device_offset)); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int device_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("device");
|
|
||||||
|
|
||||||
public SoundIOFormat Format
|
|
||||||
{
|
|
||||||
get { return (SoundIOFormat) Marshal.ReadInt32(handle, format_offset); }
|
|
||||||
set { Marshal.WriteInt32(handle, format_offset, (int) value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int format_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("format");
|
|
||||||
|
|
||||||
public int SampleRate
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, sample_rate_offset); }
|
|
||||||
set { Marshal.WriteInt32(handle, sample_rate_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int sample_rate_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("sample_rate");
|
|
||||||
|
|
||||||
public SoundIOChannelLayout Layout
|
|
||||||
{
|
|
||||||
get { unsafe { return new SoundIOChannelLayout((IntPtr) (void*)((IntPtr)handle + layout_offset)); } }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Buffer.MemoryCopy((void*)value.Handle, (void*)((IntPtr)handle + layout_offset), Marshal.SizeOf<SoundIoChannelLayout>(), Marshal.SizeOf<SoundIoChannelLayout>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static readonly int layout_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("layout");
|
|
||||||
|
|
||||||
public double SoftwareLatency
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadDouble (handle, software_latency_offset); }
|
|
||||||
set { MarshalEx.WriteDouble (handle, software_latency_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int software_latency_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("software_latency");
|
|
||||||
|
|
||||||
public float Volume
|
|
||||||
{
|
|
||||||
get { return MarshalEx.ReadFloat(handle, volume_offset); }
|
|
||||||
set { MarshalEx.WriteFloat(handle, volume_offset, value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int volume_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("volume");
|
|
||||||
|
|
||||||
// error_callback
|
|
||||||
public Action ErrorCallback
|
|
||||||
{
|
|
||||||
get { return error_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
error_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
error_callback_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
error_callback_native = stream => error_callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate(error_callback_native);
|
|
||||||
Marshal.WriteIntPtr(handle, error_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int error_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("error_callback");
|
|
||||||
|
|
||||||
Action error_callback;
|
|
||||||
delegate void error_callback_delegate (IntPtr handle);
|
|
||||||
error_callback_delegate error_callback_native;
|
|
||||||
|
|
||||||
// write_callback
|
|
||||||
public Action<int, int> WriteCallback
|
|
||||||
{
|
|
||||||
get { return write_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
write_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
write_callback_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
write_callback_native = (h, frame_count_min, frame_count_max) => write_callback(frame_count_min, frame_count_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (write_callback_native);
|
|
||||||
Marshal.WriteIntPtr (handle, write_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int write_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("write_callback");
|
|
||||||
|
|
||||||
Action<int, int> write_callback;
|
|
||||||
delegate void write_callback_delegate(IntPtr handle, int min, int max);
|
|
||||||
write_callback_delegate write_callback_native;
|
|
||||||
|
|
||||||
// underflow_callback
|
|
||||||
public Action UnderflowCallback
|
|
||||||
{
|
|
||||||
get { return underflow_callback; }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
underflow_callback = value;
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
underflow_callback_native = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
underflow_callback_native = h => underflow_callback();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.GetFunctionPointerForDelegate (underflow_callback_native);
|
|
||||||
Marshal.WriteIntPtr (handle, underflow_callback_offset, ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int underflow_callback_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("underflow_callback");
|
|
||||||
|
|
||||||
Action underflow_callback;
|
|
||||||
delegate void underflow_callback_delegate(IntPtr handle);
|
|
||||||
underflow_callback_delegate underflow_callback_native;
|
|
||||||
|
|
||||||
// FIXME: this should be taken care in more centralized/decent manner... we don't want to write
|
|
||||||
// this kind of code anywhere we need string marshaling.
|
|
||||||
List<IntPtr> allocated_hglobals = new List<IntPtr>();
|
|
||||||
|
|
||||||
public string Name {
|
|
||||||
get { return Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(handle, name_offset)); }
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var existing = Marshal.ReadIntPtr(handle, name_offset);
|
|
||||||
if (allocated_hglobals.Contains(existing))
|
|
||||||
{
|
|
||||||
allocated_hglobals.Remove(existing);
|
|
||||||
Marshal.FreeHGlobal(existing);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = Marshal.StringToHGlobalAnsi(value);
|
|
||||||
Marshal.WriteIntPtr(handle, name_offset, ptr);
|
|
||||||
allocated_hglobals.Add(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int name_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("name");
|
|
||||||
|
|
||||||
public bool NonTerminalHint
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, non_terminal_hint_offset) != 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int non_terminal_hint_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("non_terminal_hint");
|
|
||||||
|
|
||||||
public int BytesPerFrame
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, bytes_per_frame_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int bytes_per_frame_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("bytes_per_frame");
|
|
||||||
|
|
||||||
public int BytesPerSample
|
|
||||||
{
|
|
||||||
get { return Marshal.ReadInt32(handle, bytes_per_sample_offset); }
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int bytes_per_sample_offset = (int)Marshal.OffsetOf<SoundIoOutStream>("bytes_per_sample");
|
|
||||||
|
|
||||||
public string LayoutErrorMessage
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var code = (SoundIoError)Marshal.ReadInt32(handle, layout_error_offset);
|
|
||||||
|
|
||||||
return code == SoundIoError.SoundIoErrorNone ? null : Marshal.PtrToStringAnsi(Natives.soundio_strerror((int)code));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static readonly int layout_error_offset = (int)Marshal.OffsetOf<SoundIoOutStream> ("layout_error");
|
|
||||||
|
|
||||||
// functions
|
|
||||||
|
|
||||||
public void Open ()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_open(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Start ()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_start(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SoundIOChannelAreas BeginWrite(ref int frameCount)
|
|
||||||
{
|
|
||||||
IntPtr ptrs = default;
|
|
||||||
int nativeFrameCount = frameCount;
|
|
||||||
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
var frameCountPtr = &nativeFrameCount;
|
|
||||||
var ptrptr = &ptrs;
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_begin_write(handle, (IntPtr)ptrptr, (IntPtr)frameCountPtr);
|
|
||||||
|
|
||||||
frameCount = *frameCountPtr;
|
|
||||||
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SoundIOChannelAreas(ptrs, Layout.ChannelCount, frameCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndWrite ()
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_end_write(handle);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearBuffer ()
|
|
||||||
{
|
|
||||||
_ = Natives.soundio_outstream_clear_buffer(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Pause (bool pause)
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_pause(handle, pause);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double GetLatency ()
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
double* dptr = null;
|
|
||||||
IntPtr p = new IntPtr(dptr);
|
|
||||||
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_get_latency(handle, p);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
dptr = (double*)p;
|
|
||||||
|
|
||||||
return *dptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetVolume (double volume)
|
|
||||||
{
|
|
||||||
var ret = (SoundIoError)Natives.soundio_outstream_set_volume(handle, volume);
|
|
||||||
if (ret != SoundIoError.SoundIoErrorNone)
|
|
||||||
{
|
|
||||||
throw new SoundIOException(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,58 +0,0 @@
|
|||||||
using System;
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public class SoundIORingBuffer : IDisposable
|
|
||||||
{
|
|
||||||
internal SoundIORingBuffer(IntPtr handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr handle;
|
|
||||||
|
|
||||||
public int Capacity
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_ring_buffer_capacity(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
Natives.soundio_ring_buffer_clear(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Natives.soundio_ring_buffer_destroy(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int FillCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_ring_buffer_fill_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public int FreeCount
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_ring_buffer_free_count(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr ReadPointer
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_ring_buffer_read_ptr(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr WritePointer
|
|
||||||
{
|
|
||||||
get { return Natives.soundio_ring_buffer_write_ptr(handle); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdvanceReadPointer(int count)
|
|
||||||
{
|
|
||||||
Natives.soundio_ring_buffer_advance_read_ptr(handle, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdvanceWritePointer(int count)
|
|
||||||
{
|
|
||||||
Natives.soundio_ring_buffer_advance_write_ptr(handle, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
public struct SoundIOSampleRateRange
|
|
||||||
{
|
|
||||||
internal SoundIOSampleRateRange(int min, int max)
|
|
||||||
{
|
|
||||||
Min = min;
|
|
||||||
Max = max;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly int Min;
|
|
||||||
public readonly int Max;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,643 +0,0 @@
|
|||||||
// This source file is generated by nclang PInvokeGenerator.
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using delegate0 = SoundIOSharp.Delegates.delegate0;
|
|
||||||
using delegate1 = SoundIOSharp.Delegates.delegate1;
|
|
||||||
using delegate2 = SoundIOSharp.Delegates.delegate2;
|
|
||||||
using delegate3 = SoundIOSharp.Delegates.delegate3;
|
|
||||||
using delegate4 = SoundIOSharp.Delegates.delegate4;
|
|
||||||
using delegate5 = SoundIOSharp.Delegates.delegate5;
|
|
||||||
using delegate6 = SoundIOSharp.Delegates.delegate6;
|
|
||||||
using delegate7 = SoundIOSharp.Delegates.delegate7;
|
|
||||||
using delegate8 = SoundIOSharp.Delegates.delegate8;
|
|
||||||
using delegate9 = SoundIOSharp.Delegates.delegate9;
|
|
||||||
|
|
||||||
namespace SoundIOSharp
|
|
||||||
{
|
|
||||||
enum SoundIoError // soundio.h (72, 6)
|
|
||||||
{
|
|
||||||
SoundIoErrorNone = 0,
|
|
||||||
SoundIoErrorNoMem = 1,
|
|
||||||
SoundIoErrorInitAudioBackend = 2,
|
|
||||||
SoundIoErrorSystemResources = 3,
|
|
||||||
SoundIoErrorOpeningDevice = 4,
|
|
||||||
SoundIoErrorNoSuchDevice = 5,
|
|
||||||
SoundIoErrorInvalid = 6,
|
|
||||||
SoundIoErrorBackendUnavailable = 7,
|
|
||||||
SoundIoErrorStreaming = 8,
|
|
||||||
SoundIoErrorIncompatibleDevice = 9,
|
|
||||||
SoundIoErrorNoSuchClient = 10,
|
|
||||||
SoundIoErrorIncompatibleBackend = 11,
|
|
||||||
SoundIoErrorBackendDisconnected = 12,
|
|
||||||
SoundIoErrorInterrupted = 13,
|
|
||||||
SoundIoErrorUnderflow = 14,
|
|
||||||
SoundIoErrorEncodingString = 15,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SoundIoChannelId // soundio.h (106, 6)
|
|
||||||
{
|
|
||||||
SoundIoChannelIdInvalid = 0,
|
|
||||||
SoundIoChannelIdFrontLeft = 1,
|
|
||||||
SoundIoChannelIdFrontRight = 2,
|
|
||||||
SoundIoChannelIdFrontCenter = 3,
|
|
||||||
SoundIoChannelIdLfe = 4,
|
|
||||||
SoundIoChannelIdBackLeft = 5,
|
|
||||||
SoundIoChannelIdBackRight = 6,
|
|
||||||
SoundIoChannelIdFrontLeftCenter = 7,
|
|
||||||
SoundIoChannelIdFrontRightCenter = 8,
|
|
||||||
SoundIoChannelIdBackCenter = 9,
|
|
||||||
SoundIoChannelIdSideLeft = 10,
|
|
||||||
SoundIoChannelIdSideRight = 11,
|
|
||||||
SoundIoChannelIdTopCenter = 12,
|
|
||||||
SoundIoChannelIdTopFrontLeft = 13,
|
|
||||||
SoundIoChannelIdTopFrontCenter = 14,
|
|
||||||
SoundIoChannelIdTopFrontRight = 15,
|
|
||||||
SoundIoChannelIdTopBackLeft = 16,
|
|
||||||
SoundIoChannelIdTopBackCenter = 17,
|
|
||||||
SoundIoChannelIdTopBackRight = 18,
|
|
||||||
SoundIoChannelIdBackLeftCenter = 19,
|
|
||||||
SoundIoChannelIdBackRightCenter = 20,
|
|
||||||
SoundIoChannelIdFrontLeftWide = 21,
|
|
||||||
SoundIoChannelIdFrontRightWide = 22,
|
|
||||||
SoundIoChannelIdFrontLeftHigh = 23,
|
|
||||||
SoundIoChannelIdFrontCenterHigh = 24,
|
|
||||||
SoundIoChannelIdFrontRightHigh = 25,
|
|
||||||
SoundIoChannelIdTopFrontLeftCenter = 26,
|
|
||||||
SoundIoChannelIdTopFrontRightCenter = 27,
|
|
||||||
SoundIoChannelIdTopSideLeft = 28,
|
|
||||||
SoundIoChannelIdTopSideRight = 29,
|
|
||||||
SoundIoChannelIdLeftLfe = 30,
|
|
||||||
SoundIoChannelIdRightLfe = 31,
|
|
||||||
SoundIoChannelIdLfe2 = 32,
|
|
||||||
SoundIoChannelIdBottomCenter = 33,
|
|
||||||
SoundIoChannelIdBottomLeftCenter = 34,
|
|
||||||
SoundIoChannelIdBottomRightCenter = 35,
|
|
||||||
SoundIoChannelIdMsMid = 36,
|
|
||||||
SoundIoChannelIdMsSide = 37,
|
|
||||||
SoundIoChannelIdAmbisonicW = 38,
|
|
||||||
SoundIoChannelIdAmbisonicX = 39,
|
|
||||||
SoundIoChannelIdAmbisonicY = 40,
|
|
||||||
SoundIoChannelIdAmbisonicZ = 41,
|
|
||||||
SoundIoChannelIdXyX = 42,
|
|
||||||
SoundIoChannelIdXyY = 43,
|
|
||||||
SoundIoChannelIdHeadphonesLeft = 44,
|
|
||||||
SoundIoChannelIdHeadphonesRight = 45,
|
|
||||||
SoundIoChannelIdClickTrack = 46,
|
|
||||||
SoundIoChannelIdForeignLanguage = 47,
|
|
||||||
SoundIoChannelIdHearingImpaired = 48,
|
|
||||||
SoundIoChannelIdNarration = 49,
|
|
||||||
SoundIoChannelIdHaptic = 50,
|
|
||||||
SoundIoChannelIdDialogCentricMix = 51,
|
|
||||||
SoundIoChannelIdAux = 52,
|
|
||||||
SoundIoChannelIdAux0 = 53,
|
|
||||||
SoundIoChannelIdAux1 = 54,
|
|
||||||
SoundIoChannelIdAux2 = 55,
|
|
||||||
SoundIoChannelIdAux3 = 56,
|
|
||||||
SoundIoChannelIdAux4 = 57,
|
|
||||||
SoundIoChannelIdAux5 = 58,
|
|
||||||
SoundIoChannelIdAux6 = 59,
|
|
||||||
SoundIoChannelIdAux7 = 60,
|
|
||||||
SoundIoChannelIdAux8 = 61,
|
|
||||||
SoundIoChannelIdAux9 = 62,
|
|
||||||
SoundIoChannelIdAux10 = 63,
|
|
||||||
SoundIoChannelIdAux11 = 64,
|
|
||||||
SoundIoChannelIdAux12 = 65,
|
|
||||||
SoundIoChannelIdAux13 = 66,
|
|
||||||
SoundIoChannelIdAux14 = 67,
|
|
||||||
SoundIoChannelIdAux15 = 68,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SoundIoChannelLayoutId // soundio.h (189, 6)
|
|
||||||
{
|
|
||||||
SoundIoChannelLayoutIdMono = 0,
|
|
||||||
SoundIoChannelLayoutIdStereo = 1,
|
|
||||||
SoundIoChannelLayoutId2Point1 = 2,
|
|
||||||
SoundIoChannelLayoutId3Point0 = 3,
|
|
||||||
SoundIoChannelLayoutId3Point0Back = 4,
|
|
||||||
SoundIoChannelLayoutId3Point1 = 5,
|
|
||||||
SoundIoChannelLayoutId4Point0 = 6,
|
|
||||||
SoundIoChannelLayoutIdQuad = 7,
|
|
||||||
SoundIoChannelLayoutIdQuadSide = 8,
|
|
||||||
SoundIoChannelLayoutId4Point1 = 9,
|
|
||||||
SoundIoChannelLayoutId5Point0Back = 10,
|
|
||||||
SoundIoChannelLayoutId5Point0Side = 11,
|
|
||||||
SoundIoChannelLayoutId5Point1 = 12,
|
|
||||||
SoundIoChannelLayoutId5Point1Back = 13,
|
|
||||||
SoundIoChannelLayoutId6Point0Side = 14,
|
|
||||||
SoundIoChannelLayoutId6Point0Front = 15,
|
|
||||||
SoundIoChannelLayoutIdHexagonal = 16,
|
|
||||||
SoundIoChannelLayoutId6Point1 = 17,
|
|
||||||
SoundIoChannelLayoutId6Point1Back = 18,
|
|
||||||
SoundIoChannelLayoutId6Point1Front = 19,
|
|
||||||
SoundIoChannelLayoutId7Point0 = 20,
|
|
||||||
SoundIoChannelLayoutId7Point0Front = 21,
|
|
||||||
SoundIoChannelLayoutId7Point1 = 22,
|
|
||||||
SoundIoChannelLayoutId7Point1Wide = 23,
|
|
||||||
SoundIoChannelLayoutId7Point1WideBack = 24,
|
|
||||||
SoundIoChannelLayoutIdOctagonal = 25,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SoundIoBackend // soundio.h (218, 6)
|
|
||||||
{
|
|
||||||
SoundIoBackendNone = 0,
|
|
||||||
SoundIoBackendJack = 1,
|
|
||||||
SoundIoBackendPulseAudio = 2,
|
|
||||||
SoundIoBackendAlsa = 3,
|
|
||||||
SoundIoBackendCoreAudio = 4,
|
|
||||||
SoundIoBackendWasapi = 5,
|
|
||||||
SoundIoBackendDummy = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SoundIoDeviceAim // soundio.h (228, 6)
|
|
||||||
{
|
|
||||||
SoundIoDeviceAimInput = 0,
|
|
||||||
SoundIoDeviceAimOutput = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SoundIoFormat // soundio.h (235, 6)
|
|
||||||
{
|
|
||||||
SoundIoFormatInvalid = 0,
|
|
||||||
SoundIoFormatS8 = 1,
|
|
||||||
SoundIoFormatU8 = 2,
|
|
||||||
SoundIoFormatS16LE = 3,
|
|
||||||
SoundIoFormatS16BE = 4,
|
|
||||||
SoundIoFormatU16LE = 5,
|
|
||||||
SoundIoFormatU16BE = 6,
|
|
||||||
SoundIoFormatS24LE = 7,
|
|
||||||
SoundIoFormatS24BE = 8,
|
|
||||||
SoundIoFormatU24LE = 9,
|
|
||||||
SoundIoFormatU24BE = 10,
|
|
||||||
SoundIoFormatS32LE = 11,
|
|
||||||
SoundIoFormatS32BE = 12,
|
|
||||||
SoundIoFormatU32LE = 13,
|
|
||||||
SoundIoFormatU32BE = 14,
|
|
||||||
SoundIoFormatFloat32LE = 15,
|
|
||||||
SoundIoFormatFloat32BE = 16,
|
|
||||||
SoundIoFormatFloat64LE = 17,
|
|
||||||
SoundIoFormatFloat64BE = 18,
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoChannelLayout // soundio.h (306, 8)
|
|
||||||
{
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
|
||||||
public int @channel_count;
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
|
|
||||||
[CTypeDetails("ConstArrayOf<SoundIoChannelId>")] public SoundIoChannelId[] @channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoSampleRateRange // soundio.h (313, 8)
|
|
||||||
{
|
|
||||||
public int @min;
|
|
||||||
public int @max;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoChannelArea // soundio.h (319, 8)
|
|
||||||
{
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @ptr;
|
|
||||||
public int @step;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIo // soundio.h (328, 8)
|
|
||||||
{
|
|
||||||
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIo *)>")] public delegate0 @on_devices_change;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIo *, int)>")] public delegate1 @on_backend_disconnect;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIo *)>")] public Delegates.delegate0 @on_events_signal;
|
|
||||||
public SoundIoBackend @current_backend;
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @app_name;
|
|
||||||
[CTypeDetails("Pointer<void ()>")] public delegate2 @emit_rtprio_warning;
|
|
||||||
[CTypeDetails("Pointer<void (const char *)>")] public delegate3 @jack_info_callback;
|
|
||||||
[CTypeDetails("Pointer<void (const char *)>")] public Delegates.delegate3 @jack_error_callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoDevice // soundio.h (387, 8)
|
|
||||||
{
|
|
||||||
[CTypeDetails("Pointer<SoundIo>")] public System.IntPtr @soundio;
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @id;
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
|
||||||
public SoundIoDeviceAim @aim;
|
|
||||||
[CTypeDetails("Pointer<SoundIoChannelLayout>")] public System.IntPtr @layouts;
|
|
||||||
public int @layout_count;
|
|
||||||
public SoundIoChannelLayout @current_layout;
|
|
||||||
[CTypeDetails("Pointer<SoundIoFormat>")] public System.IntPtr @formats;
|
|
||||||
public int @format_count;
|
|
||||||
public SoundIoFormat @current_format;
|
|
||||||
[CTypeDetails("Pointer<SoundIoSampleRateRange>")] public System.IntPtr @sample_rates;
|
|
||||||
public int @sample_rate_count;
|
|
||||||
public int @sample_rate_current;
|
|
||||||
public double @software_latency_min;
|
|
||||||
public double @software_latency_max;
|
|
||||||
public double @software_latency_current;
|
|
||||||
public bool @is_raw;
|
|
||||||
public int @ref_count;
|
|
||||||
public int @probe_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoOutStream // soundio.h (497, 8)
|
|
||||||
{
|
|
||||||
[CTypeDetails("Pointer<SoundIoDevice>")] public System.IntPtr @device;
|
|
||||||
public SoundIoFormat @format;
|
|
||||||
public int @sample_rate;
|
|
||||||
public SoundIoChannelLayout @layout;
|
|
||||||
public double @software_latency;
|
|
||||||
public float @volume;
|
|
||||||
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIoOutStream *, int, int)>")] public delegate4 @write_callback;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIoOutStream *)>")] public delegate5 @underflow_callback;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIoOutStream *, int)>")] public delegate6 @error_callback;
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
|
||||||
public bool @non_terminal_hint;
|
|
||||||
public int @bytes_per_frame;
|
|
||||||
public int @bytes_per_sample;
|
|
||||||
public int @layout_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoInStream // soundio.h (600, 8)
|
|
||||||
{
|
|
||||||
[CTypeDetails("Pointer<SoundIoDevice>")] public System.IntPtr @device;
|
|
||||||
public SoundIoFormat @format;
|
|
||||||
public int @sample_rate;
|
|
||||||
public SoundIoChannelLayout @layout;
|
|
||||||
public double @software_latency;
|
|
||||||
[CTypeDetails("Pointer<void>")] public System.IntPtr @userdata;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIoInStream *, int, int)>")] public delegate7 @read_callback;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIoInStream *)>")] public delegate8 @overflow_callback;
|
|
||||||
[CTypeDetails("Pointer<void (SoundIoInStream *, int)>")] public delegate9 @error_callback;
|
|
||||||
[CTypeDetails("Pointer<byte>")] public System.IntPtr @name;
|
|
||||||
public bool @non_terminal_hint;
|
|
||||||
public int @bytes_per_frame;
|
|
||||||
public int @bytes_per_sample;
|
|
||||||
public int @layout_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
struct SoundIoRingBuffer // soundio.h (1170, 8)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
partial class Natives
|
|
||||||
{
|
|
||||||
const string LibraryName = "libsoundio";
|
|
||||||
// function soundio_version_string - soundio.h (682, 28)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_version_string();
|
|
||||||
|
|
||||||
// function soundio_version_major - soundio.h (684, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_version_major();
|
|
||||||
|
|
||||||
// function soundio_version_minor - soundio.h (686, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_version_minor();
|
|
||||||
|
|
||||||
// function soundio_version_patch - soundio.h (688, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_version_patch();
|
|
||||||
|
|
||||||
// function soundio_create - soundio.h (694, 32)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_create();
|
|
||||||
|
|
||||||
// function soundio_destroy - soundio.h (695, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_destroy([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_connect - soundio.h (705, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_connect([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_connect_backend - soundio.h (717, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_connect_backend([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, SoundIoBackend @backend);
|
|
||||||
|
|
||||||
// function soundio_disconnect - soundio.h (718, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_disconnect([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_strerror - soundio.h (721, 28)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_strerror(int @error);
|
|
||||||
|
|
||||||
// function soundio_backend_name - soundio.h (723, 28)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_backend_name(SoundIoBackend @backend);
|
|
||||||
|
|
||||||
// function soundio_backend_count - soundio.h (726, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_backend_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_get_backend - soundio.h (729, 36)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern SoundIoBackend soundio_get_backend([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
|
|
||||||
|
|
||||||
// function soundio_have_backend - soundio.h (732, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_have_backend(SoundIoBackend @backend);
|
|
||||||
|
|
||||||
// function soundio_flush_events - soundio.h (756, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_flush_events([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_wait_events - soundio.h (760, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_wait_events([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_wakeup - soundio.h (763, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_wakeup([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_force_device_scan - soundio.h (780, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_force_device_scan([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_channel_layout_equal - soundio.h (787, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_channel_layout_equal([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @a, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @b);
|
|
||||||
|
|
||||||
// function soundio_get_channel_name - soundio.h (791, 28)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_get_channel_name(SoundIoChannelId @id);
|
|
||||||
|
|
||||||
// function soundio_parse_channel_id - soundio.h (795, 38)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern SoundIoChannelId soundio_parse_channel_id([CTypeDetails("Pointer<byte>")]System.IntPtr @str, int @str_len);
|
|
||||||
|
|
||||||
// function soundio_channel_layout_builtin_count - soundio.h (798, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_channel_layout_builtin_count();
|
|
||||||
|
|
||||||
// function soundio_channel_layout_get_builtin - soundio.h (803, 51)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_channel_layout_get_builtin(int @index);
|
|
||||||
|
|
||||||
// function soundio_channel_layout_get_default - soundio.h (806, 51)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_channel_layout_get_default(int @channel_count);
|
|
||||||
|
|
||||||
// function soundio_channel_layout_find_channel - soundio.h (809, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_channel_layout_find_channel([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout, SoundIoChannelId @channel);
|
|
||||||
|
|
||||||
// function soundio_channel_layout_detect_builtin - soundio.h (814, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_channel_layout_detect_builtin([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout);
|
|
||||||
|
|
||||||
// function soundio_best_matching_channel_layout - soundio.h (819, 51)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_best_matching_channel_layout([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @preferred_layouts, int @preferred_layout_count, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @available_layouts, int @available_layout_count);
|
|
||||||
|
|
||||||
// function soundio_sort_channel_layouts - soundio.h (824, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_sort_channel_layouts([CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layouts, int @layout_count);
|
|
||||||
|
|
||||||
// function soundio_get_bytes_per_sample - soundio.h (830, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_get_bytes_per_sample(SoundIoFormat @format);
|
|
||||||
|
|
||||||
// function soundio_get_bytes_per_frame - soundio.h (833, 19)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_get_bytes_per_frame(SoundIoFormat @format, int @channel_count);
|
|
||||||
|
|
||||||
// function soundio_get_bytes_per_second - soundio.h (838, 19)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_get_bytes_per_second(SoundIoFormat @format, int @channel_count, int @sample_rate);
|
|
||||||
|
|
||||||
// function soundio_format_string - soundio.h (845, 29)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_format_string(SoundIoFormat @format);
|
|
||||||
|
|
||||||
// function soundio_input_device_count - soundio.h (861, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_input_device_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_output_device_count - soundio.h (864, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_output_device_count([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_get_input_device - soundio.h (870, 38)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_get_input_device([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
|
|
||||||
|
|
||||||
// function soundio_get_output_device - soundio.h (875, 38)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_get_output_device([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @index);
|
|
||||||
|
|
||||||
// function soundio_default_input_device_index - soundio.h (880, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_default_input_device_index([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_default_output_device_index - soundio.h (885, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_default_output_device_index([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio);
|
|
||||||
|
|
||||||
// function soundio_device_ref - soundio.h (888, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_device_ref([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
|
||||||
|
|
||||||
// function soundio_device_unref - soundio.h (891, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_device_unref([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
|
||||||
|
|
||||||
// function soundio_device_equal - soundio.h (895, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_device_equal([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @a, [CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @b);
|
|
||||||
|
|
||||||
// function soundio_device_sort_channel_layouts - soundio.h (900, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_device_sort_channel_layouts([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
|
||||||
|
|
||||||
// function soundio_device_supports_format - soundio.h (904, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_device_supports_format([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, SoundIoFormat @format);
|
|
||||||
|
|
||||||
// function soundio_device_supports_layout - soundio.h (909, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_device_supports_layout([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, [CTypeDetails("Pointer<SoundIoChannelLayout>")]System.IntPtr @layout);
|
|
||||||
|
|
||||||
// function soundio_device_supports_sample_rate - soundio.h (914, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern bool soundio_device_supports_sample_rate([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, int @sample_rate);
|
|
||||||
|
|
||||||
// function soundio_device_nearest_sample_rate - soundio.h (919, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_device_nearest_sample_rate([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device, int @sample_rate);
|
|
||||||
|
|
||||||
// function soundio_outstream_create - soundio.h (929, 41)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_outstream_create([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
|
||||||
|
|
||||||
// function soundio_outstream_destroy - soundio.h (931, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_outstream_destroy([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
|
||||||
|
|
||||||
// function soundio_outstream_open - soundio.h (954, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_open([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
|
||||||
|
|
||||||
// function soundio_outstream_start - soundio.h (965, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_start([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
|
||||||
|
|
||||||
// function soundio_outstream_begin_write - soundio.h (997, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_begin_write([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, [CTypeDetails("Pointer<System.IntPtr>")]System.IntPtr @areas, [CTypeDetails("Pointer<int>")]System.IntPtr @frame_count);
|
|
||||||
|
|
||||||
// function soundio_outstream_end_write - soundio.h (1009, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_end_write([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
|
||||||
|
|
||||||
// function soundio_outstream_clear_buffer - soundio.h (1024, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_clear_buffer([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream);
|
|
||||||
|
|
||||||
// function soundio_outstream_pause - soundio.h (1045, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_pause([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, bool @pause);
|
|
||||||
|
|
||||||
// function soundio_outstream_get_latency - soundio.h (1058, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_get_latency([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, [CTypeDetails("Pointer<double>")]System.IntPtr @out_latency);
|
|
||||||
|
|
||||||
// function soundio_outstream_set_volume - soundio.h (1061, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_outstream_set_volume([CTypeDetails("Pointer<SoundIoOutStream>")]System.IntPtr @outstream, double @volume);
|
|
||||||
|
|
||||||
// function soundio_instream_create - soundio.h (1071, 40)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_instream_create([CTypeDetails("Pointer<SoundIoDevice>")]System.IntPtr @device);
|
|
||||||
|
|
||||||
// function soundio_instream_destroy - soundio.h (1073, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_instream_destroy([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
|
||||||
|
|
||||||
// function soundio_instream_open - soundio.h (1093, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_instream_open([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
|
||||||
|
|
||||||
// function soundio_instream_start - soundio.h (1102, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_instream_start([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
|
||||||
|
|
||||||
// function soundio_instream_begin_read - soundio.h (1133, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_instream_begin_read([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, [CTypeDetails("Pointer<System.IntPtr>")]System.IntPtr @areas, [CTypeDetails("Pointer<int>")]System.IntPtr @frame_count);
|
|
||||||
|
|
||||||
// function soundio_instream_end_read - soundio.h (1143, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_instream_end_read([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream);
|
|
||||||
|
|
||||||
// function soundio_instream_pause - soundio.h (1156, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_instream_pause([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, bool @pause);
|
|
||||||
|
|
||||||
// function soundio_instream_get_latency - soundio.h (1166, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_instream_get_latency([CTypeDetails("Pointer<SoundIoInStream>")]System.IntPtr @instream, [CTypeDetails("Pointer<double>")]System.IntPtr @out_latency);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_create - soundio.h (1181, 42)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_ring_buffer_create([CTypeDetails("Pointer<SoundIo>")]System.IntPtr @soundio, int @requested_capacity);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_destroy - soundio.h (1182, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_ring_buffer_destroy([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_capacity - soundio.h (1186, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_ring_buffer_capacity([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_write_ptr - soundio.h (1189, 22)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_ring_buffer_write_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_advance_write_ptr - soundio.h (1191, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_ring_buffer_advance_write_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer, int @count);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_read_ptr - soundio.h (1194, 22)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern System.IntPtr soundio_ring_buffer_read_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_advance_read_ptr - soundio.h (1196, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_ring_buffer_advance_read_ptr([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer, int @count);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_fill_count - soundio.h (1199, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_ring_buffer_fill_count([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_free_count - soundio.h (1202, 20)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern int soundio_ring_buffer_free_count([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
// function soundio_ring_buffer_clear - soundio.h (1205, 21)
|
|
||||||
[DllImport(LibraryName)]
|
|
||||||
internal static extern void soundio_ring_buffer_clear([CTypeDetails("Pointer<SoundIoRingBuffer>")]System.IntPtr @ring_buffer);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Delegates
|
|
||||||
{
|
|
||||||
public delegate void delegate0(System.IntPtr p0);
|
|
||||||
public delegate void delegate1(System.IntPtr p0, int p1);
|
|
||||||
public delegate void delegate2();
|
|
||||||
public delegate void delegate3(System.IntPtr p0);
|
|
||||||
public delegate void delegate4(System.IntPtr p0, int p1, int p2);
|
|
||||||
public delegate void delegate5(System.IntPtr p0);
|
|
||||||
public delegate void delegate6(System.IntPtr p0, int p1);
|
|
||||||
public delegate void delegate7(System.IntPtr p0, int p1, int p2);
|
|
||||||
public delegate void delegate8(System.IntPtr p0);
|
|
||||||
public delegate void delegate9(System.IntPtr p0, int p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Pointer<T>
|
|
||||||
{
|
|
||||||
public IntPtr Handle;
|
|
||||||
public static implicit operator IntPtr(Pointer<T> value) { return value.Handle; }
|
|
||||||
public static implicit operator Pointer<T>(IntPtr value) { return new Pointer<T>(value); }
|
|
||||||
|
|
||||||
public Pointer(IntPtr handle)
|
|
||||||
{
|
|
||||||
Handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
|
||||||
{
|
|
||||||
return obj is Pointer<T> && this == (Pointer<T>)obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return (int)Handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator ==(Pointer<T> p1, Pointer<T> p2)
|
|
||||||
{
|
|
||||||
return p1.Handle == p2.Handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool operator !=(Pointer<T> p1, Pointer<T> p2)
|
|
||||||
{
|
|
||||||
return p1.Handle != p2.Handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public struct ArrayOf<T> { }
|
|
||||||
public struct ConstArrayOf<T> { }
|
|
||||||
public class CTypeDetailsAttribute : Attribute
|
|
||||||
{
|
|
||||||
public CTypeDetailsAttribute(string value)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,19 +1,20 @@
|
|||||||
using Ryujinx.Audio.Common;
|
using Ryujinx.Audio.Backends.SoundIo.Native;
|
||||||
|
using Ryujinx.Audio.Common;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using SoundIOSharp;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
|
||||||
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Backends.SoundIo
|
namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
{
|
{
|
||||||
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
public class SoundIoHardwareDeviceDriver : IHardwareDeviceDriver
|
||||||
{
|
{
|
||||||
private readonly SoundIO _audioContext;
|
private readonly SoundIoContext _audioContext;
|
||||||
private readonly SoundIODevice _audioDevice;
|
private readonly SoundIoDeviceContext _audioDevice;
|
||||||
private readonly ManualResetEvent _updateRequiredEvent;
|
private readonly ManualResetEvent _updateRequiredEvent;
|
||||||
private readonly ManualResetEvent _pauseEvent;
|
private readonly ManualResetEvent _pauseEvent;
|
||||||
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
private readonly ConcurrentDictionary<SoundIoHardwareDeviceSession, byte> _sessions;
|
||||||
@@ -21,7 +22,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
|
|
||||||
public SoundIoHardwareDeviceDriver()
|
public SoundIoHardwareDeviceDriver()
|
||||||
{
|
{
|
||||||
_audioContext = new SoundIO();
|
_audioContext = SoundIoContext.Create();
|
||||||
_updateRequiredEvent = new ManualResetEvent(false);
|
_updateRequiredEvent = new ManualResetEvent(false);
|
||||||
_pauseEvent = new ManualResetEvent(true);
|
_pauseEvent = new ManualResetEvent(true);
|
||||||
_sessions = new ConcurrentDictionary<SoundIoHardwareDeviceSession, byte>();
|
_sessions = new ConcurrentDictionary<SoundIoHardwareDeviceSession, byte>();
|
||||||
@@ -29,24 +30,23 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
_audioContext.Connect();
|
_audioContext.Connect();
|
||||||
_audioContext.FlushEvents();
|
_audioContext.FlushEvents();
|
||||||
|
|
||||||
_audioDevice = FindNonRawDefaultAudioDevice(_audioContext, true);
|
_audioDevice = FindValidAudioDevice(_audioContext, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsSupported => IsSupportedInternal();
|
public static bool IsSupported => IsSupportedInternal();
|
||||||
|
|
||||||
private static bool IsSupportedInternal()
|
private static bool IsSupportedInternal()
|
||||||
{
|
{
|
||||||
SoundIO context = null;
|
SoundIoContext context = null;
|
||||||
SoundIODevice device = null;
|
SoundIoDeviceContext device = null;
|
||||||
SoundIOOutStream stream = null;
|
SoundIoOutStreamContext stream = null;
|
||||||
|
|
||||||
bool backendDisconnected = false;
|
bool backendDisconnected = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
context = new SoundIO();
|
context = SoundIoContext.Create();
|
||||||
|
context.OnBackendDisconnect = err =>
|
||||||
context.OnBackendDisconnect = (i) =>
|
|
||||||
{
|
{
|
||||||
backendDisconnected = true;
|
backendDisconnected = true;
|
||||||
};
|
};
|
||||||
@@ -64,7 +64,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
device = FindNonRawDefaultAudioDevice(context);
|
device = FindValidAudioDevice(context);
|
||||||
|
|
||||||
if (device == null || backendDisconnected)
|
if (device == null || backendDisconnected)
|
||||||
{
|
{
|
||||||
@@ -86,30 +86,23 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (stream != null)
|
stream?.Dispose();
|
||||||
{
|
context?.Dispose();
|
||||||
stream.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context != null)
|
|
||||||
{
|
|
||||||
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)
|
if (!defaultAudioDevice.IsRaw)
|
||||||
{
|
{
|
||||||
return defaultAudioDevice;
|
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)
|
if (audioDevice.Id == defaultAudioDevice.Id && !audioDevice.IsRaw)
|
||||||
{
|
{
|
||||||
@@ -161,22 +154,22 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
return _sessions.TryRemove(session, out _);
|
return _sessions.TryRemove(session, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SoundIOFormat GetSoundIoFormat(SampleFormat format)
|
public static SoundIoFormat GetSoundIoFormat(SampleFormat format)
|
||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
{
|
{
|
||||||
SampleFormat.PcmInt8 => SoundIOFormat.S8,
|
SampleFormat.PcmInt8 => SoundIoFormat.S8,
|
||||||
SampleFormat.PcmInt16 => SoundIOFormat.S16LE,
|
SampleFormat.PcmInt16 => SoundIoFormat.S16LE,
|
||||||
SampleFormat.PcmInt24 => SoundIOFormat.S24LE,
|
SampleFormat.PcmInt24 => SoundIoFormat.S24LE,
|
||||||
SampleFormat.PcmInt32 => SoundIOFormat.S32LE,
|
SampleFormat.PcmInt32 => SoundIoFormat.S32LE,
|
||||||
SampleFormat.PcmFloat => SoundIOFormat.Float32LE,
|
SampleFormat.PcmFloat => SoundIoFormat.Float32LE,
|
||||||
_ => throw new ArgumentException ($"Unsupported sample format {format}"),
|
_ => 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))
|
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}");
|
throw new ArgumentException($"This sound device does not support channel count {requestedChannelCount}");
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundIOOutStream result = _audioDevice.CreateOutStream();
|
SoundIoOutStreamContext result = _audioDevice.CreateOutStream();
|
||||||
|
|
||||||
result.Name = "Ryujinx";
|
result.Name = "Ryujinx";
|
||||||
result.Layout = SoundIOChannelLayout.GetDefault((int)requestedChannelCount);
|
result.Layout = SoundIoChannelLayout.GetDefaultValue((int)requestedChannelCount);
|
||||||
result.Format = driverSampleFormat;
|
result.Format = driverSampleFormat;
|
||||||
result.SampleRate = (int)requestedSampleRate;
|
result.SampleRate = (int)requestedSampleRate;
|
||||||
|
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
using Ryujinx.Audio.Backends.Common;
|
using Ryujinx.Audio.Backends.Common;
|
||||||
|
using Ryujinx.Audio.Backends.SoundIo.Native;
|
||||||
using Ryujinx.Audio.Common;
|
using Ryujinx.Audio.Common;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using SoundIOSharp;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Backends.SoundIo
|
namespace Ryujinx.Audio.Backends.SoundIo
|
||||||
{
|
{
|
||||||
@@ -13,7 +14,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
{
|
{
|
||||||
private SoundIoHardwareDeviceDriver _driver;
|
private SoundIoHardwareDeviceDriver _driver;
|
||||||
private ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers;
|
private ConcurrentQueue<SoundIoAudioBuffer> _queuedBuffers;
|
||||||
private SoundIOOutStream _outputStream;
|
private SoundIoOutStreamContext _outputStream;
|
||||||
private DynamicRingBuffer _ringBuffer;
|
private DynamicRingBuffer _ringBuffer;
|
||||||
private ulong _playedSampleCount;
|
private ulong _playedSampleCount;
|
||||||
private ManualResetEvent _updateRequiredEvent;
|
private ManualResetEvent _updateRequiredEvent;
|
||||||
@@ -106,9 +107,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
return;
|
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];
|
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
|
// This is a huge ugly block of code, but we save
|
||||||
// a significant amount of time over the generic
|
// a significant amount of time over the generic
|
||||||
// loop that handles other channel counts.
|
// loop that handles other channel counts.
|
||||||
// TODO: Is this still right in 2021?
|
// TODO: Is this still right in 2022?
|
||||||
|
|
||||||
// Mono
|
// Mono
|
||||||
if (channelCount == 1)
|
if (channelCount == 1)
|
||||||
{
|
{
|
||||||
SoundIOChannelArea area = areas.GetArea(0);
|
ref SoundIoChannelArea area = ref areas[0];
|
||||||
|
|
||||||
fixed (byte* srcptr = samples)
|
fixed (byte* srcptr = samples)
|
||||||
{
|
{
|
||||||
@@ -167,8 +168,8 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
// Stereo
|
// Stereo
|
||||||
else if (channelCount == 2)
|
else if (channelCount == 2)
|
||||||
{
|
{
|
||||||
SoundIOChannelArea area1 = areas.GetArea(0);
|
ref SoundIoChannelArea area1 = ref areas[0];
|
||||||
SoundIOChannelArea area2 = areas.GetArea(1);
|
ref SoundIoChannelArea area2 = ref areas[1];
|
||||||
|
|
||||||
fixed (byte* srcptr = samples)
|
fixed (byte* srcptr = samples)
|
||||||
{
|
{
|
||||||
@@ -233,12 +234,12 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
// Surround
|
// Surround
|
||||||
else if (channelCount == 6)
|
else if (channelCount == 6)
|
||||||
{
|
{
|
||||||
SoundIOChannelArea area1 = areas.GetArea(0);
|
ref SoundIoChannelArea area1 = ref areas[0];
|
||||||
SoundIOChannelArea area2 = areas.GetArea(1);
|
ref SoundIoChannelArea area2 = ref areas[1];
|
||||||
SoundIOChannelArea area3 = areas.GetArea(2);
|
ref SoundIoChannelArea area3 = ref areas[2];
|
||||||
SoundIOChannelArea area4 = areas.GetArea(3);
|
ref SoundIoChannelArea area4 = ref areas[3];
|
||||||
SoundIOChannelArea area5 = areas.GetArea(4);
|
ref SoundIoChannelArea area5 = ref areas[4];
|
||||||
SoundIOChannelArea area6 = areas.GetArea(5);
|
ref SoundIoChannelArea area6 = ref areas[5];
|
||||||
|
|
||||||
fixed (byte* srcptr = samples)
|
fixed (byte* srcptr = samples)
|
||||||
{
|
{
|
||||||
@@ -367,23 +368,17 @@ namespace Ryujinx.Audio.Backends.SoundIo
|
|||||||
// Every other channel count
|
// Every other channel count
|
||||||
else
|
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)
|
fixed (byte* srcptr = samples)
|
||||||
{
|
{
|
||||||
for (int frame = 0; frame < frameCount; frame++)
|
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!
|
// Copy channel by channel, frame by frame. This is slow!
|
||||||
Unsafe.CopyBlockUnaligned((byte*)channels[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample);
|
Unsafe.CopyBlockUnaligned((byte*)areas[channel].Pointer, srcptr + (frame * bytesPerFrame) + (channel * bytesPerSample), bytesPerSample);
|
||||||
|
|
||||||
channels[channel].Pointer += channels[channel].Step;
|
areas[channel].Pointer += areas[channel].Step;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -51,6 +51,40 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SampleFormat SelectHardwareSampleFormat(SampleFormat targetSampleFormat)
|
||||||
|
{
|
||||||
|
if (_realDriver.SupportsSampleFormat(targetSampleFormat))
|
||||||
|
{
|
||||||
|
return targetSampleFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt conversion from PCM16.
|
||||||
|
if (targetSampleFormat == SampleFormat.PcmInt16)
|
||||||
|
{
|
||||||
|
// Prefer PCM32 if we need to convert.
|
||||||
|
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt32))
|
||||||
|
{
|
||||||
|
return SampleFormat.PcmInt32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not supported, PCM float provides the best quality with a cost lower than PCM24.
|
||||||
|
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmFloat))
|
||||||
|
{
|
||||||
|
return SampleFormat.PcmFloat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement PCM24 conversion.
|
||||||
|
|
||||||
|
// If nothing is truly supported, attempt PCM8 at the cost of loosing quality.
|
||||||
|
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt8))
|
||||||
|
{
|
||||||
|
return SampleFormat.PcmInt8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException("No valid sample format configuration found!");
|
||||||
|
}
|
||||||
|
|
||||||
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
|
||||||
{
|
{
|
||||||
if (channelCount == 0)
|
if (channelCount == 0)
|
||||||
@@ -77,15 +111,26 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
|
||||||
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
|
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;
|
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)
|
if (direction == Direction.Input)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Audio, $"The selected audio backend doesn't support the requested audio input configuration, fallback to dummy...");
|
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.
|
// 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)
|
public bool SupportsChannelCount(uint channelCount)
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Audio.Backends.Common;
|
using Ryujinx.Audio.Backends.Common;
|
||||||
using Ryujinx.Audio.Common;
|
using Ryujinx.Audio.Common;
|
||||||
|
using Ryujinx.Audio.Renderer.Dsp;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@@ -8,11 +9,13 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase
|
||||||
{
|
{
|
||||||
private HardwareDeviceSessionOutputBase _realSession;
|
private HardwareDeviceSessionOutputBase _realSession;
|
||||||
|
private SampleFormat _userSampleFormat;
|
||||||
private uint _userChannelCount;
|
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;
|
_realSession = realSession;
|
||||||
|
_userSampleFormat = userSampleFormat;
|
||||||
_userChannelCount = userChannelCount;
|
_userChannelCount = userChannelCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,53 +41,86 @@ namespace Ryujinx.Audio.Backends.CompatLayer
|
|||||||
|
|
||||||
public override void QueueBuffer(AudioBuffer buffer)
|
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);
|
_realSession.QueueBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
|
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)
|
if (samples == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
short[] downmixedBufferPCM16;
|
if (_userChannelCount != _realSession.RequestedChannelCount)
|
||||||
|
{
|
||||||
|
if (_userSampleFormat != SampleFormat.PcmInt16)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
|
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
|
||||||
|
|
||||||
if (_userChannelCount == 6)
|
if (_userChannelCount == 6)
|
||||||
{
|
{
|
||||||
downmixedBufferPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
|
samplesPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
|
||||||
|
|
||||||
if (_realSession.RequestedChannelCount == 1)
|
if (_realSession.RequestedChannelCount == 1)
|
||||||
{
|
{
|
||||||
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(downmixedBufferPCM16);
|
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
|
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
|
||||||
{
|
{
|
||||||
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
|
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
|
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] downmixedBuffer = MemoryMarshal.Cast<short, byte>(downmixedBufferPCM16).ToArray();
|
samples = MemoryMarshal.Cast<short, byte>(samplesPCM16).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
AudioBuffer fakeBuffer = new AudioBuffer
|
AudioBuffer fakeBuffer = new AudioBuffer
|
||||||
{
|
{
|
||||||
BufferTag = buffer.BufferTag,
|
BufferTag = buffer.BufferTag,
|
||||||
DataPointer = buffer.DataPointer,
|
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)
|
if (result)
|
||||||
{
|
{
|
||||||
|
@@ -48,6 +48,11 @@ namespace Ryujinx.Audio.Renderer.Common
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Effect to capture mixes (via auxiliary buffers).
|
/// Effect to capture mixes (via auxiliary buffers).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CaptureBuffer
|
CaptureBuffer,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Effect applying a compressor filter (DRC).
|
||||||
|
/// </summary>
|
||||||
|
Compressor,
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,6 +14,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
|||||||
Reverb3d,
|
Reverb3d,
|
||||||
PcmFloat,
|
PcmFloat,
|
||||||
Limiter,
|
Limiter,
|
||||||
CaptureBuffer
|
CaptureBuffer,
|
||||||
|
Compressor
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common
|
|||||||
|
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
ulong alignedOffset = BitUtils.AlignUp(Offset, align);
|
ulong alignedOffset = BitUtils.AlignUp<ulong>(Offset, (ulong)align);
|
||||||
|
|
||||||
if (alignedOffset + size <= (ulong)BackingMemory.Length)
|
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
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -31,6 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
LimiterVersion1,
|
LimiterVersion1,
|
||||||
LimiterVersion2,
|
LimiterVersion2,
|
||||||
GroupedBiquadFilter,
|
GroupedBiquadFilter,
|
||||||
CaptureBuffer
|
CaptureBuffer,
|
||||||
|
Compressor
|
||||||
}
|
}
|
||||||
}
|
}
|
173
Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
Normal file
173
Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
|
{
|
||||||
|
public class CompressorCommand : ICommand
|
||||||
|
{
|
||||||
|
private const int FixedPointPrecision = 15;
|
||||||
|
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
public int NodeId { get; }
|
||||||
|
|
||||||
|
public CommandType CommandType => CommandType.Compressor;
|
||||||
|
|
||||||
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
|
public CompressorParameter Parameter => _parameter;
|
||||||
|
public Memory<CompressorState> State { get; }
|
||||||
|
public ushort[] OutputBufferIndices { get; }
|
||||||
|
public ushort[] InputBufferIndices { get; }
|
||||||
|
public bool IsEffectEnabled { get; }
|
||||||
|
|
||||||
|
private CompressorParameter _parameter;
|
||||||
|
|
||||||
|
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||||
|
{
|
||||||
|
Enabled = true;
|
||||||
|
NodeId = nodeId;
|
||||||
|
_parameter = parameter;
|
||||||
|
State = state;
|
||||||
|
|
||||||
|
IsEffectEnabled = isEnabled;
|
||||||
|
|
||||||
|
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||||
|
|
||||||
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
|
{
|
||||||
|
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
||||||
|
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Process(CommandList context)
|
||||||
|
{
|
||||||
|
ref CompressorState state = ref State.Span[0];
|
||||||
|
|
||||||
|
if (IsEffectEnabled)
|
||||||
|
{
|
||||||
|
if (_parameter.Status == Server.Effect.UsageState.Invalid)
|
||||||
|
{
|
||||||
|
state = new CompressorState(ref _parameter);
|
||||||
|
}
|
||||||
|
else if (_parameter.Status == Server.Effect.UsageState.New)
|
||||||
|
{
|
||||||
|
state.UpdateParameter(ref _parameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessCompressor(context, ref state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void ProcessCompressor(CommandList context, ref CompressorState state)
|
||||||
|
{
|
||||||
|
Debug.Assert(_parameter.IsChannelCountValid());
|
||||||
|
|
||||||
|
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||||
|
{
|
||||||
|
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
|
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||||
|
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
|
||||||
|
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
||||||
|
float unknown4 = state.Unknown4;
|
||||||
|
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
||||||
|
float previousCompressionEmaAlpha = state.PreviousCompressionEmaAlpha;
|
||||||
|
|
||||||
|
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||||
|
{
|
||||||
|
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||||
|
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||||
|
{
|
||||||
|
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||||
|
{
|
||||||
|
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
||||||
|
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
||||||
|
float z = 0.0f;
|
||||||
|
|
||||||
|
bool unknown10OutOfRange = false;
|
||||||
|
|
||||||
|
if (newMean < 1.0e-10f)
|
||||||
|
{
|
||||||
|
z = 1.0f;
|
||||||
|
|
||||||
|
unknown10OutOfRange = state.Unknown10 < -100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y >= state.Unknown10 || unknown10OutOfRange)
|
||||||
|
{
|
||||||
|
float tmpGain;
|
||||||
|
|
||||||
|
if (y >= state.Unknown14)
|
||||||
|
{
|
||||||
|
tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmpGain = (y - state.Unknown10) * ((y - state.Unknown10) * -state.CompressorGainReduction);
|
||||||
|
}
|
||||||
|
|
||||||
|
z = FloatingPointHelper.DecibelToLinearExtended(tmpGain);
|
||||||
|
}
|
||||||
|
|
||||||
|
float unknown4New = z;
|
||||||
|
float compressionEmaAlpha;
|
||||||
|
|
||||||
|
if ((unknown4 - z) <= 0.08f)
|
||||||
|
{
|
||||||
|
compressionEmaAlpha = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
|
if ((unknown4 - z) >= -0.08f)
|
||||||
|
{
|
||||||
|
if (MathF.Abs(compressionGainAverage.Read() - z) >= 0.001f)
|
||||||
|
{
|
||||||
|
unknown4New = unknown4;
|
||||||
|
}
|
||||||
|
|
||||||
|
compressionEmaAlpha = previousCompressionEmaAlpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
compressionEmaAlpha = Parameter.AttackCoefficient;
|
||||||
|
}
|
||||||
|
|
||||||
|
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
|
||||||
|
|
||||||
|
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||||
|
{
|
||||||
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
unknown4 = unknown4New;
|
||||||
|
previousCompressionEmaAlpha = compressionEmaAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.InputMovingAverage = inputMovingAverage;
|
||||||
|
state.Unknown4 = unknown4;
|
||||||
|
state.CompressionGainAverage = compressionGainAverage;
|
||||||
|
state.PreviousCompressionEmaAlpha = previousCompressionEmaAlpha;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||||
|
{
|
||||||
|
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||||
|
{
|
||||||
|
context.CopyBuffer(OutputBufferIndices[i], InputBufferIndices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -90,32 +90,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (sampleInputMax > state.DectectorAverage[channelIndex])
|
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||||
{
|
{
|
||||||
inputCoefficient = Parameter.AttackCoefficient;
|
inputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]);
|
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||||
|
|
||||||
float attenuation = 1.0f;
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
if (state.DectectorAverage[channelIndex] > Parameter.Threshold)
|
if (detectorValue > Parameter.Threshold)
|
||||||
{
|
{
|
||||||
attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex];
|
attenuation = Parameter.Threshold / detectorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float outputCoefficient = Parameter.ReleaseCoefficient;
|
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (state.CompressionGain[channelIndex] > attenuation)
|
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||||
{
|
{
|
||||||
outputCoefficient = Parameter.AttackCoefficient;
|
outputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]);
|
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
||||||
|
|
||||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||||
|
|
||||||
float outputSample = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain;
|
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
|
||||||
|
|
||||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||||
|
|
||||||
|
@@ -101,32 +101,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (sampleInputMax > state.DectectorAverage[channelIndex])
|
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||||
{
|
{
|
||||||
inputCoefficient = Parameter.AttackCoefficient;
|
inputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.DectectorAverage[channelIndex] += inputCoefficient * (sampleInputMax - state.DectectorAverage[channelIndex]);
|
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||||
|
|
||||||
float attenuation = 1.0f;
|
float attenuation = 1.0f;
|
||||||
|
|
||||||
if (state.DectectorAverage[channelIndex] > Parameter.Threshold)
|
if (detectorValue > Parameter.Threshold)
|
||||||
{
|
{
|
||||||
attenuation = Parameter.Threshold / state.DectectorAverage[channelIndex];
|
attenuation = Parameter.Threshold / detectorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float outputCoefficient = Parameter.ReleaseCoefficient;
|
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||||
|
|
||||||
if (state.CompressionGain[channelIndex] > attenuation)
|
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||||
{
|
{
|
||||||
outputCoefficient = Parameter.AttackCoefficient;
|
outputCoefficient = Parameter.AttackCoefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.CompressionGain[channelIndex] += outputCoefficient * (attenuation - state.CompressionGain[channelIndex]);
|
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
||||||
|
|
||||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||||
|
|
||||||
float outputSample = delayedSample * state.CompressionGain[channelIndex] * Parameter.OutputGain;
|
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
|
||||||
|
|
||||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||||
|
|
||||||
@@ -144,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||||
|
|
||||||
statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
|
statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax);
|
||||||
statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], state.CompressionGain[channelIndex]);
|
statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], compressionGain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Dsp.Effect
|
||||||
|
{
|
||||||
|
public struct ExponentialMovingAverage
|
||||||
|
{
|
||||||
|
private float _mean;
|
||||||
|
|
||||||
|
public ExponentialMovingAverage(float mean)
|
||||||
|
{
|
||||||
|
_mean = mean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Read()
|
||||||
|
{
|
||||||
|
return _mean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float Update(float value, float alpha)
|
||||||
|
{
|
||||||
|
_mean += alpha * (value - _mean);
|
||||||
|
|
||||||
|
return _mean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,6 +16,12 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
return (float)value / (1 << qBits);
|
return (float)value / (1 << qBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float ConvertFloat(float value, int qBits)
|
||||||
|
{
|
||||||
|
return value / (1 << qBits);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int ToFixed(float value, int qBits)
|
public static int ToFixed(float value, int qBits)
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Reflection.Metadata;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp
|
namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
@@ -46,6 +47,53 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
return MathF.Pow(10, x);
|
return MathF.Pow(10, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float Log10(float x)
|
||||||
|
{
|
||||||
|
// NOTE: Nintendo uses an approximation of log10, we don't.
|
||||||
|
// As such, we support the same ranges as Nintendo to avoid unexpected behaviours.
|
||||||
|
return MathF.Pow(10, MathF.Max(x, 1.0e-10f));
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float MeanSquare(ReadOnlySpan<float> inputs)
|
||||||
|
{
|
||||||
|
float res = 0.0f;
|
||||||
|
|
||||||
|
foreach (float input in inputs)
|
||||||
|
{
|
||||||
|
res += (input * input);
|
||||||
|
}
|
||||||
|
|
||||||
|
res /= inputs.Length;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map decibel to linear.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The decibel value to convert</param>
|
||||||
|
/// <returns>Converted linear value/returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float DecibelToLinear(float db)
|
||||||
|
{
|
||||||
|
return MathF.Pow(10.0f, db / 20.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Map decibel to linear in [0, 2] range.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">The decibel value to convert</param>
|
||||||
|
/// <returns>Converted linear value in [0, 2] range</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static float DecibelToLinearExtended(float db)
|
||||||
|
{
|
||||||
|
float tmp = MathF.Log2(DecibelToLinear(db));
|
||||||
|
|
||||||
|
return MathF.Truncate(tmp) + MathF.Pow(2.0f, tmp - MathF.Truncate(tmp));
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static float DegreesToRadians(float degrees)
|
public static float DegreesToRadians(float degrees)
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp
|
namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
@@ -23,6 +24,44 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
return GetCountToDecode(startSampleOffset, endSampleOffset, offset, count) * Unsafe.SizeOf<T>();
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int Decode(Span<short> output, ReadOnlySpan<short> input, int startSampleOffset, int endSampleOffset, int channelIndex, int channelCount)
|
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++)
|
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;
|
return decodedCount;
|
||||||
|
51
Ryujinx.Audio/Renderer/Dsp/State/CompressorState.cs
Normal file
51
Ryujinx.Audio/Renderer/Dsp/State/CompressorState.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
|
{
|
||||||
|
public class CompressorState
|
||||||
|
{
|
||||||
|
public ExponentialMovingAverage InputMovingAverage;
|
||||||
|
public float Unknown4;
|
||||||
|
public ExponentialMovingAverage CompressionGainAverage;
|
||||||
|
public float CompressorGainReduction;
|
||||||
|
public float Unknown10;
|
||||||
|
public float Unknown14;
|
||||||
|
public float PreviousCompressionEmaAlpha;
|
||||||
|
public float MakeupGain;
|
||||||
|
public float OutputGain;
|
||||||
|
|
||||||
|
public CompressorState(ref CompressorParameter parameter)
|
||||||
|
{
|
||||||
|
InputMovingAverage = new ExponentialMovingAverage(0.0f);
|
||||||
|
Unknown4 = 1.0f;
|
||||||
|
CompressionGainAverage = new ExponentialMovingAverage(1.0f);
|
||||||
|
|
||||||
|
UpdateParameter(ref parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateParameter(ref CompressorParameter parameter)
|
||||||
|
{
|
||||||
|
float threshold = parameter.Threshold;
|
||||||
|
float ratio = 1.0f / parameter.Ratio;
|
||||||
|
float attackCoefficient = parameter.AttackCoefficient;
|
||||||
|
float makeupGain;
|
||||||
|
|
||||||
|
if (parameter.MakeupGainEnabled)
|
||||||
|
{
|
||||||
|
makeupGain = (threshold * 0.5f * (ratio - 1.0f)) - 3.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
makeupGain = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
PreviousCompressionEmaAlpha = attackCoefficient;
|
||||||
|
MakeupGain = makeupGain;
|
||||||
|
CompressorGainReduction = (1.0f - ratio) / Constants.ChannelCountMax;
|
||||||
|
Unknown10 = threshold - 1.5f;
|
||||||
|
Unknown14 = threshold + 1.5f;
|
||||||
|
OutputGain = FloatingPointHelper.DecibelToLinearExtended(parameter.OutputGain + makeupGain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -5,20 +6,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
{
|
{
|
||||||
public class LimiterState
|
public class LimiterState
|
||||||
{
|
{
|
||||||
public float[] DectectorAverage;
|
public ExponentialMovingAverage[] DetectorAverage;
|
||||||
public float[] CompressionGain;
|
public ExponentialMovingAverage[] CompressionGainAverage;
|
||||||
public float[] DelayedSampleBuffer;
|
public float[] DelayedSampleBuffer;
|
||||||
public int[] DelayedSampleBufferPosition;
|
public int[] DelayedSampleBufferPosition;
|
||||||
|
|
||||||
public LimiterState(ref LimiterParameter parameter, ulong workBuffer)
|
public LimiterState(ref LimiterParameter parameter, ulong workBuffer)
|
||||||
{
|
{
|
||||||
DectectorAverage = new float[parameter.ChannelCount];
|
DetectorAverage = new ExponentialMovingAverage[parameter.ChannelCount];
|
||||||
CompressionGain = new float[parameter.ChannelCount];
|
CompressionGainAverage = new ExponentialMovingAverage[parameter.ChannelCount];
|
||||||
DelayedSampleBuffer = new float[parameter.ChannelCount * parameter.DelayBufferSampleCountMax];
|
DelayedSampleBuffer = new float[parameter.ChannelCount * parameter.DelayBufferSampleCountMax];
|
||||||
DelayedSampleBufferPosition = new int[parameter.ChannelCount];
|
DelayedSampleBufferPosition = new int[parameter.ChannelCount];
|
||||||
|
|
||||||
DectectorAverage.AsSpan().Fill(0.0f);
|
DetectorAverage.AsSpan().Fill(new ExponentialMovingAverage(0.0f));
|
||||||
CompressionGain.AsSpan().Fill(1.0f);
|
CompressionGainAverage.AsSpan().Fill(new ExponentialMovingAverage(1.0f));
|
||||||
DelayedSampleBufferPosition.AsSpan().Fill(0);
|
DelayedSampleBufferPosition.AsSpan().Fill(0);
|
||||||
DelayedSampleBuffer.AsSpan().Fill(0.0f);
|
DelayedSampleBuffer.AsSpan().Fill(0.0f);
|
||||||
|
|
||||||
|
115
Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs
Normal file
115
Ryujinx.Audio/Renderer/Parameter/Effect/CompressorParameter.cs
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="IEffectInParameter.SpecificData"/> for <see cref="Common.EffectType.Compressor"/>.
|
||||||
|
/// </summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||||
|
public struct CompressorParameter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The input channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Array6<byte> Input;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The output channel indices that will be used by the <see cref="Dsp.AudioProcessor"/>.
|
||||||
|
/// </summary>
|
||||||
|
public Array6<byte> Output;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum number of channels supported.
|
||||||
|
/// </summary>
|
||||||
|
public ushort ChannelCountMax;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total channel count used.
|
||||||
|
/// </summary>
|
||||||
|
public ushort ChannelCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The target sample rate.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This is in kHz.</remarks>
|
||||||
|
public int SampleRate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The threshold.
|
||||||
|
/// </summary>
|
||||||
|
public float Threshold;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The compressor ratio.
|
||||||
|
/// </summary>
|
||||||
|
public float Ratio;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The attack time.
|
||||||
|
/// <remarks>This is in microseconds.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public int AttackTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The release time.
|
||||||
|
/// <remarks>This is in microseconds.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public int ReleaseTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The input gain.
|
||||||
|
/// </summary>
|
||||||
|
public float InputGain;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The attack coefficient.
|
||||||
|
/// </summary>
|
||||||
|
public float AttackCoefficient;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The release coefficient.
|
||||||
|
/// </summary>
|
||||||
|
public float ReleaseCoefficient;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The output gain.
|
||||||
|
/// </summary>
|
||||||
|
public float OutputGain;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current usage status of the effect on the client side.
|
||||||
|
/// </summary>
|
||||||
|
public UsageState Status;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicate if the makeup gain should be used.
|
||||||
|
/// </summary>
|
||||||
|
[MarshalAs(UnmanagedType.I1)]
|
||||||
|
public bool MakeupGainEnabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserved/padding.
|
||||||
|
/// </summary>
|
||||||
|
private Array2<byte> _reserved;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the <see cref="ChannelCount"/> is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns true if the <see cref="ChannelCount"/> is valid.</returns>
|
||||||
|
public bool IsChannelCountValid()
|
||||||
|
{
|
||||||
|
return EffectInParameterVersion1.IsChannelCountValid(ChannelCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the <see cref="ChannelCountMax"/> is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Returns true if the <see cref="ChannelCountMax"/> is valid.</returns>
|
||||||
|
public bool IsChannelCountMaxValid()
|
||||||
|
{
|
||||||
|
return EffectInParameterVersion1.IsChannelCountValid(ChannelCountMax);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -167,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return ResultCode.WorkBufferTooSmall;
|
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)
|
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);
|
size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
|
||||||
|
|
||||||
// Depop buffer
|
// 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
|
// Voice
|
||||||
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment);
|
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;
|
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)
|
public ResultCode QuerySystemEvent(out IWritableEvent systemEvent)
|
||||||
|
@@ -93,6 +93,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// REV11:
|
/// REV11:
|
||||||
/// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
|
/// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
|
||||||
|
/// A new effect was added: Compressor. This effect is effectively implemented with a DRC.
|
||||||
/// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
|
/// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
|
||||||
/// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands.
|
/// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -469,6 +469,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||||
|
{
|
||||||
|
if (parameter.IsChannelCountValid())
|
||||||
|
{
|
||||||
|
CompressorCommand command = new CompressorCommand(bufferOffset, parameter, state, isEnabled, nodeId);
|
||||||
|
|
||||||
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
|
AddCommand(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate a new <see cref="VolumeCommand"/>.
|
/// Generate a new <see cref="VolumeCommand"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -606,6 +606,17 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId)
|
||||||
|
{
|
||||||
|
Debug.Assert(effect.Type == EffectType.Compressor);
|
||||||
|
|
||||||
|
_commandBuffer.GenerateCompressorEffect(bufferOffset,
|
||||||
|
effect.Parameter,
|
||||||
|
effect.State,
|
||||||
|
effect.IsEnabled,
|
||||||
|
nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
|
private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect)
|
||||||
{
|
{
|
||||||
int nodeId = mix.NodeId;
|
int nodeId = mix.NodeId;
|
||||||
@@ -650,6 +661,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
case EffectType.CaptureBuffer:
|
case EffectType.CaptureBuffer:
|
||||||
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
||||||
break;
|
break;
|
||||||
|
case EffectType.Compressor:
|
||||||
|
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
||||||
}
|
}
|
||||||
|
@@ -179,5 +179,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint Estimate(CompressorCommand command)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -543,5 +543,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint Estimate(CompressorCommand command)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -747,5 +747,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual uint Estimate(CompressorCommand command)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -232,5 +232,79 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override uint Estimate(CompressorCommand command)
|
||||||
|
{
|
||||||
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
|
if (_sampleCount == 160)
|
||||||
|
{
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 34431;
|
||||||
|
case 2:
|
||||||
|
return 44253;
|
||||||
|
case 4:
|
||||||
|
return 63827;
|
||||||
|
case 6:
|
||||||
|
return 83361;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)630.12f;
|
||||||
|
case 2:
|
||||||
|
return (uint)638.27f;
|
||||||
|
case 4:
|
||||||
|
return (uint)705.86f;
|
||||||
|
case 6:
|
||||||
|
return (uint)782.02f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 51095;
|
||||||
|
case 2:
|
||||||
|
return 65693;
|
||||||
|
case 4:
|
||||||
|
return 95383;
|
||||||
|
case 6:
|
||||||
|
return 124510;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)840.14f;
|
||||||
|
case 2:
|
||||||
|
return (uint)826.1f;
|
||||||
|
case 4:
|
||||||
|
return (uint)901.88f;
|
||||||
|
case 6:
|
||||||
|
return (uint)965.29f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -262,6 +262,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
|||||||
return PerformanceDetailType.Limiter;
|
return PerformanceDetailType.Limiter;
|
||||||
case EffectType.CaptureBuffer:
|
case EffectType.CaptureBuffer:
|
||||||
return PerformanceDetailType.CaptureBuffer;
|
return PerformanceDetailType.CaptureBuffer;
|
||||||
|
case EffectType.Compressor:
|
||||||
|
return PerformanceDetailType.Compressor;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"{Type}");
|
throw new NotImplementedException($"{Type}");
|
||||||
}
|
}
|
||||||
|
67
Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs
Normal file
67
Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using Ryujinx.Audio.Renderer.Common;
|
||||||
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
|
using Ryujinx.Audio.Renderer.Parameter;
|
||||||
|
using Ryujinx.Audio.Renderer.Server.MemoryPool;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Server state for a compressor effect.
|
||||||
|
/// </summary>
|
||||||
|
public class CompressorEffect : BaseEffect
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The compressor parameter.
|
||||||
|
/// </summary>
|
||||||
|
public CompressorParameter Parameter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The compressor state.
|
||||||
|
/// </summary>
|
||||||
|
public Memory<CompressorState> State { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="CompressorEffect"/>.
|
||||||
|
/// </summary>
|
||||||
|
public CompressorEffect()
|
||||||
|
{
|
||||||
|
State = new CompressorState[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
public override EffectType TargetEffectType => EffectType.Compressor;
|
||||||
|
|
||||||
|
public override ulong GetWorkBuffer(int index)
|
||||||
|
{
|
||||||
|
return GetSingleBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
|
||||||
|
{
|
||||||
|
// Nintendo doesn't do anything here but we still require updateErrorInfo to be initialised.
|
||||||
|
updateErrorInfo = new BehaviourParameter.ErrorInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
|
||||||
|
{
|
||||||
|
Debug.Assert(IsTypeValid(ref parameter));
|
||||||
|
|
||||||
|
UpdateParameterBase(ref parameter);
|
||||||
|
|
||||||
|
Parameter = MemoryMarshal.Cast<byte, CompressorParameter>(parameter.SpecificData)[0];
|
||||||
|
IsEnabled = parameter.IsEnabled;
|
||||||
|
|
||||||
|
updateErrorInfo = new BehaviourParameter.ErrorInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void UpdateForCommandGeneration()
|
||||||
|
{
|
||||||
|
UpdateUsageStateForCommandGeneration();
|
||||||
|
|
||||||
|
Parameter.Status = UsageState.Enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -35,5 +35,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
uint Estimate(LimiterCommandVersion2 command);
|
uint Estimate(LimiterCommandVersion2 command);
|
||||||
uint Estimate(GroupedBiquadFilterCommand command);
|
uint Estimate(GroupedBiquadFilterCommand command);
|
||||||
uint Estimate(CaptureBufferCommand command);
|
uint Estimate(CaptureBufferCommand command);
|
||||||
|
uint Estimate(CompressorCommand command);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -240,6 +240,10 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
case EffectType.CaptureBuffer:
|
case EffectType.CaptureBuffer:
|
||||||
effect = new CaptureBufferEffect();
|
effect = new CaptureBufferEffect();
|
||||||
break;
|
break;
|
||||||
|
case EffectType.Compressor:
|
||||||
|
effect = new CompressorEffect();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"EffectType {parameter.Type} not implemented!");
|
throw new NotImplementedException($"EffectType {parameter.Type} not implemented!");
|
||||||
}
|
}
|
||||||
|
@@ -4,6 +4,6 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:sty="using:FluentAvalonia.Styling">
|
xmlns:sty="using:FluentAvalonia.Styling">
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<sty:FluentAvaloniaTheme UseSystemThemeOnWindows="False" />
|
<sty:FluentAvaloniaTheme PreferSystemTheme="False" />
|
||||||
</Application.Styles>
|
</Application.Styles>
|
||||||
</Application>
|
</Application>
|
@@ -5,8 +5,9 @@ using Avalonia.Styling;
|
|||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using FluentAvalonia.Styling;
|
using FluentAvalonia.Styling;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Ui.Controls;
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.Ui.Windows;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
@@ -58,11 +59,11 @@ namespace Ryujinx.Ava
|
|||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
var result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance["DialogThemeRestartMessage"],
|
LocaleManager.Instance[LocaleKeys.DialogThemeRestartMessage],
|
||||||
LocaleManager.Instance["DialogThemeRestartSubMessage"],
|
LocaleManager.Instance[LocaleKeys.DialogThemeRestartSubMessage],
|
||||||
LocaleManager.Instance["InputDialogYes"],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance["InputDialogNo"],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
LocaleManager.Instance["DialogRestartRequiredMessage"]);
|
LocaleManager.Instance[LocaleKeys.DialogRestartRequiredMessage]);
|
||||||
|
|
||||||
if (result == UserResult.Yes)
|
if (result == UserResult.Yes)
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.PTC;
|
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
@@ -11,13 +10,14 @@ using Ryujinx.Audio.Integration;
|
|||||||
using Ryujinx.Ava.Common;
|
using Ryujinx.Ava.Common;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.Ui.Controls;
|
using Ryujinx.Ava.UI.Controls;
|
||||||
using Ryujinx.Ava.Ui.Models;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.Ui.Windows;
|
using Ryujinx.Ava.UI.Models;
|
||||||
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.System;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.GAL.Multithreading;
|
using Ryujinx.Graphics.GAL.Multithreading;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
@@ -125,7 +125,7 @@ namespace Ryujinx.Ava
|
|||||||
_inputManager = inputManager;
|
_inputManager = inputManager;
|
||||||
_accountManager = accountManager;
|
_accountManager = accountManager;
|
||||||
_userChannelPersistence = userChannelPersistence;
|
_userChannelPersistence = userChannelPersistence;
|
||||||
_renderingThread = new Thread(RenderLoop) { Name = "GUI.RenderThread" };
|
_renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" };
|
||||||
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
|
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
|
||||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||||
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
|
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
|
||||||
@@ -279,7 +279,7 @@ namespace Ryujinx.Ava
|
|||||||
_parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
_parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
|
||||||
});
|
});
|
||||||
|
|
||||||
_parent.ViewModel.HandleShaderProgress(Device);
|
_parent.ViewModel.SetUiProgressHandlers(Device);
|
||||||
|
|
||||||
Renderer.SizeChanged += Window_SizeChanged;
|
Renderer.SizeChanged += Window_SizeChanged;
|
||||||
|
|
||||||
@@ -356,8 +356,6 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
DisplaySleep.Restore();
|
DisplaySleep.Restore();
|
||||||
|
|
||||||
Ptc.Close();
|
|
||||||
PtcProfiler.Stop();
|
|
||||||
NpadManager.Dispose();
|
NpadManager.Dispose();
|
||||||
TouchScreenManager.Dispose();
|
TouchScreenManager.Dispose();
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
@@ -431,10 +429,10 @@ namespace Ryujinx.Ava
|
|||||||
if (userError == UserError.NoFirmware)
|
if (userError == UserError.NoFirmware)
|
||||||
{
|
{
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"],
|
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
|
||||||
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString),
|
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString),
|
||||||
LocaleManager.Instance["InputDialogYes"],
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
LocaleManager.Instance["InputDialogNo"],
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
"");
|
"");
|
||||||
|
|
||||||
if (result != UserResult.Yes)
|
if (result != UserResult.Yes)
|
||||||
@@ -462,11 +460,11 @@ namespace Ryujinx.Ava
|
|||||||
_parent.RefreshFirmwareStatus();
|
_parent.RefreshFirmwareStatus();
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(
|
await ContentDialogHelper.CreateInfoDialog(
|
||||||
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
|
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString),
|
||||||
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString),
|
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString),
|
||||||
LocaleManager.Instance["InputDialogOk"],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
"",
|
"",
|
||||||
LocaleManager.Instance["RyujinxInfo"]);
|
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -868,7 +866,7 @@ namespace Ryujinx.Ava
|
|||||||
public void UpdateStatus()
|
public void UpdateStatus()
|
||||||
{
|
{
|
||||||
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued
|
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued
|
||||||
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance["Docked"] : LocaleManager.Instance["Handheld"];
|
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
|
||||||
float scale = GraphicsConfig.ResScale;
|
float scale = GraphicsConfig.ResScale;
|
||||||
|
|
||||||
if (scale != 1)
|
if (scale != 1)
|
||||||
@@ -878,11 +876,11 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||||
Device.EnableDeviceVsync,
|
Device.EnableDeviceVsync,
|
||||||
LocaleManager.Instance["VolumeShort"] + $": {(int)(Device.GetVolume() * 100)}%",
|
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
|
||||||
Renderer.IsVulkan ? "Vulkan" : "OpenGL",
|
Renderer.IsVulkan ? "Vulkan" : "OpenGL",
|
||||||
dockedMode,
|
dockedMode,
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||||
LocaleManager.Instance["Game"] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
LocaleManager.Instance[LocaleKeys.Game] + $": {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||||
$"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
|
$"FIFO: {Device.Statistics.GetFifoPercent():00.00} %",
|
||||||
$"GPU: {_renderer.GetHardwareInfo().GpuVendor}"));
|
$"GPU: {_renderer.GetHardwareInfo().GpuVendor}"));
|
||||||
}
|
}
|
||||||
@@ -948,7 +946,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen)
|
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen)
|
||||||
{
|
{
|
||||||
Ptc.Continue();
|
Device.Application.DiskCacheLoadState?.Cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -81,7 +81,7 @@
|
|||||||
"SettingsTabGeneralRemove": "Entfernen",
|
"SettingsTabGeneralRemove": "Entfernen",
|
||||||
"SettingsTabSystem": "System",
|
"SettingsTabSystem": "System",
|
||||||
"SettingsTabSystemCore": "Kern",
|
"SettingsTabSystemCore": "Kern",
|
||||||
"SettingsTabSystemSystemRegion": "System Region:",
|
"SettingsTabSystemSystemRegion": "Systemregion:",
|
||||||
"SettingsTabSystemSystemRegionJapan": "Japan",
|
"SettingsTabSystemSystemRegionJapan": "Japan",
|
||||||
"SettingsTabSystemSystemRegionUSA": "USA",
|
"SettingsTabSystemSystemRegionUSA": "USA",
|
||||||
"SettingsTabSystemSystemRegionEurope": "Europa",
|
"SettingsTabSystemSystemRegionEurope": "Europa",
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
"SettingsTabSystemEnablePptc": "PPTC Cache (Profiled Persistent Translation Cache)",
|
"SettingsTabSystemEnablePptc": "PPTC Cache (Profiled Persistent Translation Cache)",
|
||||||
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integritätsprüfung",
|
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integritätsprüfung",
|
||||||
"SettingsTabSystemAudioBackend": "Audio-Backend:",
|
"SettingsTabSystemAudioBackend": "Audio-Backend:",
|
||||||
"SettingsTabSystemAudioBackendDummy": "Dummy",
|
"SettingsTabSystemAudioBackendDummy": "Ohne Funktion",
|
||||||
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
|
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
|
||||||
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
||||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||||
@@ -157,11 +157,11 @@
|
|||||||
"SettingsTabLoggingEnableFsAccessLogs": "Aktiviere Fs Zugriff-Logs",
|
"SettingsTabLoggingEnableFsAccessLogs": "Aktiviere Fs Zugriff-Logs",
|
||||||
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Globaler Zugriff-Log-Modus:",
|
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Globaler Zugriff-Log-Modus:",
|
||||||
"SettingsTabLoggingDeveloperOptions": "Entwickleroptionen (WARNUNG: Beeinträchtigt die Leistung)",
|
"SettingsTabLoggingDeveloperOptions": "Entwickleroptionen (WARNUNG: Beeinträchtigt die Leistung)",
|
||||||
"SettingsTabLoggingOpenglLogLevel": "OpenGL Logstufe:",
|
"SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:",
|
||||||
"SettingsTabLoggingOpenglLogLevelNone": "Keine",
|
"SettingsTabLoggingGraphicsBackendLogLevelNone": "Keine",
|
||||||
"SettingsTabLoggingOpenglLogLevelError": "Fehler",
|
"SettingsTabLoggingGraphicsBackendLogLevelError": "Fehler",
|
||||||
"SettingsTabLoggingOpenglLogLevelPerformance": "Verlangsamungen",
|
"SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Verlangsamungen",
|
||||||
"SettingsTabLoggingOpenglLogLevelAll": "Alle",
|
"SettingsTabLoggingGraphicsBackendLogLevelAll": "Alle",
|
||||||
"SettingsTabLoggingEnableDebugLogs": "Aktiviere Debug-Log",
|
"SettingsTabLoggingEnableDebugLogs": "Aktiviere Debug-Log",
|
||||||
"SettingsTabInput": "Eingabe",
|
"SettingsTabInput": "Eingabe",
|
||||||
"SettingsTabInputEnableDockedMode": "Docked Modus",
|
"SettingsTabInputEnableDockedMode": "Docked Modus",
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
"ControllerSettingsControllerTypeJoyConLeft": "Joy-Con Links",
|
"ControllerSettingsControllerTypeJoyConLeft": "Joy-Con Links",
|
||||||
"ControllerSettingsControllerTypeJoyConRight": "Joy-Con Rechts",
|
"ControllerSettingsControllerTypeJoyConRight": "Joy-Con Rechts",
|
||||||
"ControllerSettingsProfile": "Profil",
|
"ControllerSettingsProfile": "Profil",
|
||||||
"ControllerSettingsProfileDefault": "Default",
|
"ControllerSettingsProfileDefault": "Standard",
|
||||||
"ControllerSettingsLoad": "Laden",
|
"ControllerSettingsLoad": "Laden",
|
||||||
"ControllerSettingsAdd": "Hinzufügen",
|
"ControllerSettingsAdd": "Hinzufügen",
|
||||||
"ControllerSettingsRemove": "Entfernen",
|
"ControllerSettingsRemove": "Entfernen",
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
"ControllerSettingsTriggersRight": "Rechter Trigger",
|
"ControllerSettingsTriggersRight": "Rechter Trigger",
|
||||||
"ControllerSettingsTriggersButtonsLeft": "Linke Schultertaste",
|
"ControllerSettingsTriggersButtonsLeft": "Linke Schultertaste",
|
||||||
"ControllerSettingsTriggersButtonsRight": "Rechte Schultertaste",
|
"ControllerSettingsTriggersButtonsRight": "Rechte Schultertaste",
|
||||||
"ControllerSettingsTriggers": "Triggers",
|
"ControllerSettingsTriggers": "Trigger",
|
||||||
"ControllerSettingsTriggerL": "L",
|
"ControllerSettingsTriggerL": "L",
|
||||||
"ControllerSettingsTriggerR": "R",
|
"ControllerSettingsTriggerR": "R",
|
||||||
"ControllerSettingsTriggerZL": "ZL",
|
"ControllerSettingsTriggerZL": "ZL",
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
"ControllerSettingsTriggerThreshold": "Empfindlichkeit:",
|
"ControllerSettingsTriggerThreshold": "Empfindlichkeit:",
|
||||||
"ControllerSettingsMotion": "Bewegung",
|
"ControllerSettingsMotion": "Bewegung",
|
||||||
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook kompatible Bewegungssteuerung",
|
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "CemuHook kompatible Bewegungssteuerung",
|
||||||
"ControllerSettingsMotionControllerSlot": "Controller Slot:",
|
"ControllerSettingsMotionControllerSlot": "Kontroller Slot:",
|
||||||
"ControllerSettingsMotionMirrorInput": "Spiegele Eingabe",
|
"ControllerSettingsMotionMirrorInput": "Spiegele Eingabe",
|
||||||
"ControllerSettingsMotionRightJoyConSlot": "Rechter Joy-Con Slot:",
|
"ControllerSettingsMotionRightJoyConSlot": "Rechter Joy-Con Slot:",
|
||||||
"ControllerSettingsMotionServerHost": "Server Host:",
|
"ControllerSettingsMotionServerHost": "Server Host:",
|
||||||
@@ -280,7 +280,7 @@
|
|||||||
"ControllerSettingsRemoveProfileToolTip": "Entfernt ein Profil",
|
"ControllerSettingsRemoveProfileToolTip": "Entfernt ein Profil",
|
||||||
"ControllerSettingsSaveProfileToolTip": "Speichert ein Profil",
|
"ControllerSettingsSaveProfileToolTip": "Speichert ein Profil",
|
||||||
"MenuBarFileToolsTakeScreenshot": "Screenshot aufnehmen",
|
"MenuBarFileToolsTakeScreenshot": "Screenshot aufnehmen",
|
||||||
"MenuBarFileToolsHideUi": "Verstecke UI",
|
"MenuBarFileToolsHideUi": "Hide UI",
|
||||||
"GameListContextMenuToggleFavorite": "Als Favoriten hinzufügen/entfernen",
|
"GameListContextMenuToggleFavorite": "Als Favoriten hinzufügen/entfernen",
|
||||||
"GameListContextMenuToggleFavoriteToolTip": "Aktiviert den Favoriten-Status des Spiels",
|
"GameListContextMenuToggleFavoriteToolTip": "Aktiviert den Favoriten-Status des Spiels",
|
||||||
"SettingsTabGeneralTheme": "Design",
|
"SettingsTabGeneralTheme": "Design",
|
||||||
@@ -315,8 +315,8 @@
|
|||||||
"DialogUpdaterConvertFailedMessage": "Die Konvertierung der aktuellen Ryujinx Version ist fehlgeschlagen.",
|
"DialogUpdaterConvertFailedMessage": "Die Konvertierung der aktuellen Ryujinx Version ist fehlgeschlagen.",
|
||||||
"DialogUpdaterCancelUpdateMessage": "Download wird abgebrochen!",
|
"DialogUpdaterCancelUpdateMessage": "Download wird abgebrochen!",
|
||||||
"DialogUpdaterAlreadyOnLatestVersionMessage": "Es wird bereits die aktuellste Version von Ryujinx benutzt",
|
"DialogUpdaterAlreadyOnLatestVersionMessage": "Es wird bereits die aktuellste Version von Ryujinx benutzt",
|
||||||
"DialogUpdaterFailedToGetVersionMessage": "Bei dem Versuch Versionsinformationen von AppVeyor zu erhalten, ist ein Fehler aufgetreten ",
|
"DialogUpdaterFailedToGetVersionMessage": "An error has occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes.",
|
||||||
"DialogUpdaterConvertFailedAppveyorMessage": "Die von AppVeyor erhaltene Ryujinx Version konnte nicht konvertiert werden.",
|
"DialogUpdaterConvertFailedGithubMessage": "Failed to convert the received Ryujinx version from Github Release.",
|
||||||
"DialogUpdaterDownloadingMessage": "Update wird Heruntergeladen...",
|
"DialogUpdaterDownloadingMessage": "Update wird Heruntergeladen...",
|
||||||
"DialogUpdaterExtractionMessage": "Update wird entpackt...",
|
"DialogUpdaterExtractionMessage": "Update wird entpackt...",
|
||||||
"DialogUpdaterRenamingMessage": "Update wird umbenannt...",
|
"DialogUpdaterRenamingMessage": "Update wird umbenannt...",
|
||||||
@@ -412,6 +412,8 @@
|
|||||||
"DlcManagerTableHeadingContainerPathLabel": "Container-Pfad",
|
"DlcManagerTableHeadingContainerPathLabel": "Container-Pfad",
|
||||||
"DlcManagerTableHeadingFullPathLabel": "Vollständiger-Pfad",
|
"DlcManagerTableHeadingFullPathLabel": "Vollständiger-Pfad",
|
||||||
"DlcManagerRemoveAllButton": "Entferne alle",
|
"DlcManagerRemoveAllButton": "Entferne alle",
|
||||||
|
"DlcManagerEnableAllButton": "Alle aktivieren",
|
||||||
|
"DlcManagerDisableAllButton": "Alle deaktivieren",
|
||||||
"MenuBarOptionsChangeLanguage": "Sprache ändern",
|
"MenuBarOptionsChangeLanguage": "Sprache ändern",
|
||||||
"CommonSort": "Sortieren",
|
"CommonSort": "Sortieren",
|
||||||
"CommonShowNames": "Spiel-Namen anzeigen",
|
"CommonShowNames": "Spiel-Namen anzeigen",
|
||||||
@@ -456,8 +458,8 @@
|
|||||||
"StubLogTooltip": "Ausgabe von Stub-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
"StubLogTooltip": "Ausgabe von Stub-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
||||||
"InfoLogTooltip": "Ausgabe von Info-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
"InfoLogTooltip": "Ausgabe von Info-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
||||||
"WarnLogTooltip": "Ausgabe von Warn-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
"WarnLogTooltip": "Ausgabe von Warn-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
||||||
"TraceLogTooltip": "Ausgabe von Trace-Log in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
|
||||||
"ErrorLogTooltip": "Ausgabe von Fehler-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
"ErrorLogTooltip": "Ausgabe von Fehler-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
||||||
|
"TraceLogTooltip": "Ausgabe von Trace-Log in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
||||||
"GuestLogTooltip": "Ausgabe von Gast-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
"GuestLogTooltip": "Ausgabe von Gast-Logs in der Konsole. Hat keinen Einfluss auf die Leistung.",
|
||||||
"FileAccessLogTooltip": "Ausgabe von FS-Zugriff-Logs in der Konsole.",
|
"FileAccessLogTooltip": "Ausgabe von FS-Zugriff-Logs in der Konsole.",
|
||||||
"FSAccessLogModeTooltip": "Aktiviert die Ausgabe des FS-Zugriff-Logs in der Konsole. Mögliche Modi sind 0-3",
|
"FSAccessLogModeTooltip": "Aktiviert die Ausgabe des FS-Zugriff-Logs in der Konsole. Mögliche Modi sind 0-3",
|
||||||
@@ -590,5 +592,22 @@
|
|||||||
"SettingsTabGraphicsPreferredGpuTooltip": "Wähle die Grafikkarte aus, die mit dem Vulkan Grafik-Backend verwendet werden soll.\n\nDies hat keinen Einfluss auf die GPU die OpenGL verwendet.\n\nIm Zweifelsfall die als \"dGPU\" gekennzeichnete GPU auswählen. Diese Einstellung unberührt lassen, wenn keine zur Auswahl steht.",
|
"SettingsTabGraphicsPreferredGpuTooltip": "Wähle die Grafikkarte aus, die mit dem Vulkan Grafik-Backend verwendet werden soll.\n\nDies hat keinen Einfluss auf die GPU die OpenGL verwendet.\n\nIm Zweifelsfall die als \"dGPU\" gekennzeichnete GPU auswählen. Diese Einstellung unberührt lassen, wenn keine zur Auswahl steht.",
|
||||||
"SettingsAppRequiredRestartMessage": "Ein Neustart von Ryujinx ist erforderlich",
|
"SettingsAppRequiredRestartMessage": "Ein Neustart von Ryujinx ist erforderlich",
|
||||||
"SettingsGpuBackendRestartMessage": "Das Grafik-Backend oder die Grafikkarteneinstellungen wurden geändert. Ein Neustart ist erforderlich um diese Einstellungen anzuwenden.",
|
"SettingsGpuBackendRestartMessage": "Das Grafik-Backend oder die Grafikkarteneinstellungen wurden geändert. Ein Neustart ist erforderlich um diese Einstellungen anzuwenden.",
|
||||||
"SettingsGpuBackendRestartSubMessage": "Ryujinx jetzt neu starten?"
|
"SettingsGpuBackendRestartSubMessage": "Ryujinx jetzt neu starten?",
|
||||||
|
"RyujinxUpdaterMessage": "Möchtest du Ryujinx auf die neueste Version aktualisieren?",
|
||||||
|
"SettingsTabHotkeysVolumeUpHotkey": "Lautstärke erhöhen:",
|
||||||
|
"SettingsTabHotkeysVolumeDownHotkey": "Lautstärke verringern:",
|
||||||
|
"SettingsEnableMacroHLE": "HLE Makros aktivieren",
|
||||||
|
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
|
||||||
|
"VolumeShort": "Vol",
|
||||||
|
"UserProfilesManageSaves": "Speicherstände verwalten",
|
||||||
|
"DeleteUserSave": "Möchtest du den Spielerstand für dieses Spiel löschen?",
|
||||||
|
"IrreversibleActionNote": "Diese Option kann nicht rückgängig gemacht werden.",
|
||||||
|
"SaveManagerHeading": "Spielstände für {0} verwalten",
|
||||||
|
"SaveManagerTitle": "Speicherdaten Manager",
|
||||||
|
"Name": "Name",
|
||||||
|
"Size": "Größe",
|
||||||
|
"Search": "Suche",
|
||||||
|
"UserProfilesRecoverLostAccounts": "Konto wiederherstellen",
|
||||||
|
"Recover": "Wiederherstellen",
|
||||||
|
"UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden"
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"MenuBarFileOpenApplet": "Άνοιγμα Applet",
|
"MenuBarFileOpenApplet": "Άνοιγμα Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία",
|
||||||
"SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού",
|
"SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού",
|
||||||
@@ -65,7 +65,7 @@
|
|||||||
"GameListContextMenuExtractDataExeFSToolTip": "Εξαγωγή της ενότητας ExeFS από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)",
|
"GameListContextMenuExtractDataExeFSToolTip": "Εξαγωγή της ενότητας ExeFS από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)",
|
||||||
"GameListContextMenuExtractDataRomFS": "RomFS",
|
"GameListContextMenuExtractDataRomFS": "RomFS",
|
||||||
"GameListContextMenuExtractDataRomFSToolTip": "Εξαγωγή της ενότητας RomFS από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)",
|
"GameListContextMenuExtractDataRomFSToolTip": "Εξαγωγή της ενότητας RomFS από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)",
|
||||||
"GameListContextMenuExtractDataLogo": "Logo",
|
"GameListContextMenuExtractDataLogo": "Λογότυπο",
|
||||||
"GameListContextMenuExtractDataLogoToolTip": "Εξαγωγή της ενότητας Logo από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)",
|
"GameListContextMenuExtractDataLogoToolTip": "Εξαγωγή της ενότητας Logo από την τρέχουσα διαμόρφωση της εφαρμογής (συμπεριλαμβανομένου ενημερώσεων)",
|
||||||
"StatusBarGamesLoaded": "{0}/{1} Φορτωμένα Παιχνίδια",
|
"StatusBarGamesLoaded": "{0}/{1} Φορτωμένα Παιχνίδια",
|
||||||
"StatusBarSystemVersion": "Έκδοση Συστήματος: {0}",
|
"StatusBarSystemVersion": "Έκδοση Συστήματος: {0}",
|
||||||
@@ -157,18 +157,18 @@
|
|||||||
"SettingsTabLoggingEnableFsAccessLogs": "Ενεργοποίηση Καταγραφής Πρόσβασης FS",
|
"SettingsTabLoggingEnableFsAccessLogs": "Ενεργοποίηση Καταγραφής Πρόσβασης FS",
|
||||||
"SettingsTabLoggingFsGlobalAccessLogMode": "Λειτουργία Καταγραφής Καθολικής Πρόσβασης FS:",
|
"SettingsTabLoggingFsGlobalAccessLogMode": "Λειτουργία Καταγραφής Καθολικής Πρόσβασης FS:",
|
||||||
"SettingsTabLoggingDeveloperOptions": "Επιλογές Προγραμματιστή (ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Η απόδοση Θα μειωθεί)",
|
"SettingsTabLoggingDeveloperOptions": "Επιλογές Προγραμματιστή (ΠΡΟΕΙΔΟΠΟΙΗΣΗ: Η απόδοση Θα μειωθεί)",
|
||||||
"SettingsTabLoggingOpenglLogLevel": "Επίπεδο Καταγραφής OpenGL:",
|
"SettingsTabLoggingGraphicsBackendLogLevel": "Επίπεδο Καταγραφής Διεπαφής Γραφικών:",
|
||||||
"SettingsTabLoggingOpenglLogLevelNone": "Κανένα",
|
"SettingsTabLoggingGraphicsBackendLogLevelNone": "Κανένα",
|
||||||
"SettingsTabLoggingOpenglLogLevelError": "Σφάλμα",
|
"SettingsTabLoggingGraphicsBackendLogLevelError": "Σφάλμα",
|
||||||
"SettingsTabLoggingOpenglLogLevelPerformance": "Επιβραδύνσεις",
|
"SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Επιβραδύνσεις",
|
||||||
"SettingsTabLoggingOpenglLogLevelAll": "Όλα",
|
"SettingsTabLoggingGraphicsBackendLogLevelAll": "Όλα",
|
||||||
"SettingsTabLoggingEnableDebugLogs": "Ενεργοποίηση Αρχείων Καταγραφής Εντοπισμού Σφαλμάτων",
|
"SettingsTabLoggingEnableDebugLogs": "Ενεργοποίηση Αρχείων Καταγραφής Εντοπισμού Σφαλμάτων",
|
||||||
"SettingsTabInput": "Χειρισμός",
|
"SettingsTabInput": "Χειρισμός",
|
||||||
"SettingsTabInputEnableDockedMode": "Ενεργοποίηση Docked Mode",
|
"SettingsTabInputEnableDockedMode": "Ενεργοποίηση Docked Mode",
|
||||||
"SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο",
|
"SettingsTabInputDirectKeyboardAccess": "Άμεση Πρόσβαση στο Πληκτρολόγιο",
|
||||||
"SettingsButtonSave": "Αποθήκευση",
|
"SettingsButtonSave": "Αποθήκευση",
|
||||||
"SettingsButtonClose": "Κλείσιμο",
|
"SettingsButtonClose": "Κλείσιμο",
|
||||||
"SettingsButtonOk": "OK",
|
"SettingsButtonOk": "ΟΚ",
|
||||||
"SettingsButtonCancel": "Ακύρωση",
|
"SettingsButtonCancel": "Ακύρωση",
|
||||||
"SettingsButtonApply": "Εφαρμογή",
|
"SettingsButtonApply": "Εφαρμογή",
|
||||||
"ControllerSettingsPlayer": "Παίχτης",
|
"ControllerSettingsPlayer": "Παίχτης",
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
"ControllerSettingsRefresh": "Ανανέωση",
|
"ControllerSettingsRefresh": "Ανανέωση",
|
||||||
"ControllerSettingsDeviceDisabled": "Απενεργοποιημένο",
|
"ControllerSettingsDeviceDisabled": "Απενεργοποιημένο",
|
||||||
"ControllerSettingsControllerType": "Τύπος Χειριστηρίου",
|
"ControllerSettingsControllerType": "Τύπος Χειριστηρίου",
|
||||||
"ControllerSettingsControllerTypeHandheld": "Handheld",
|
"ControllerSettingsControllerTypeHandheld": "Φορητό",
|
||||||
"ControllerSettingsControllerTypeProController": "Pro Controller",
|
"ControllerSettingsControllerTypeProController": "Pro Controller",
|
||||||
"ControllerSettingsControllerTypeJoyConPair": "Ζεύγος JoyCon",
|
"ControllerSettingsControllerTypeJoyConPair": "Ζεύγος JoyCon",
|
||||||
"ControllerSettingsControllerTypeJoyConLeft": "Αριστερό JoyCon",
|
"ControllerSettingsControllerTypeJoyConLeft": "Αριστερό JoyCon",
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
"ControllerSettingsAdd": "Προσθήκη",
|
"ControllerSettingsAdd": "Προσθήκη",
|
||||||
"ControllerSettingsRemove": "Αφαίρεση",
|
"ControllerSettingsRemove": "Αφαίρεση",
|
||||||
"ControllerSettingsButtons": "Κουμπιά",
|
"ControllerSettingsButtons": "Κουμπιά",
|
||||||
"ControllerSettingsButtonA": "A",
|
"ControllerSettingsButtonA": "Α",
|
||||||
"ControllerSettingsButtonB": "B",
|
"ControllerSettingsButtonB": "B",
|
||||||
"ControllerSettingsButtonX": "X",
|
"ControllerSettingsButtonX": "X",
|
||||||
"ControllerSettingsButtonY": "Y",
|
"ControllerSettingsButtonY": "Y",
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
"ControllerSettingsLStickDown": "Κάτω",
|
"ControllerSettingsLStickDown": "Κάτω",
|
||||||
"ControllerSettingsLStickLeft": "Αριστερά",
|
"ControllerSettingsLStickLeft": "Αριστερά",
|
||||||
"ControllerSettingsLStickRight": "Δεξιά",
|
"ControllerSettingsLStickRight": "Δεξιά",
|
||||||
"ControllerSettingsLStickStick": "Stick",
|
"ControllerSettingsLStickStick": "Μοχλός",
|
||||||
"ControllerSettingsLStickInvertXAxis": "Αντιστροφή Μοχλού X",
|
"ControllerSettingsLStickInvertXAxis": "Αντιστροφή Μοχλού X",
|
||||||
"ControllerSettingsLStickInvertYAxis": "Αντιστροφή Μοχλού Y",
|
"ControllerSettingsLStickInvertYAxis": "Αντιστροφή Μοχλού Y",
|
||||||
"ControllerSettingsLStickDeadzone": "Νεκρή Ζώνη:",
|
"ControllerSettingsLStickDeadzone": "Νεκρή Ζώνη:",
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
"ControllerSettingsRStickDown": "Κάτω",
|
"ControllerSettingsRStickDown": "Κάτω",
|
||||||
"ControllerSettingsRStickLeft": "Αριστερά",
|
"ControllerSettingsRStickLeft": "Αριστερά",
|
||||||
"ControllerSettingsRStickRight": "Δεξιά",
|
"ControllerSettingsRStickRight": "Δεξιά",
|
||||||
"ControllerSettingsRStickStick": "Stick",
|
"ControllerSettingsRStickStick": "Μοχλός",
|
||||||
"ControllerSettingsRStickInvertXAxis": "Αντιστροφή Μοχλού X",
|
"ControllerSettingsRStickInvertXAxis": "Αντιστροφή Μοχλού X",
|
||||||
"ControllerSettingsRStickInvertYAxis": "Αντιστροφή Μοχλού Y",
|
"ControllerSettingsRStickInvertYAxis": "Αντιστροφή Μοχλού Y",
|
||||||
"ControllerSettingsRStickDeadzone": "Νεκρή Ζώνη:",
|
"ControllerSettingsRStickDeadzone": "Νεκρή Ζώνη:",
|
||||||
@@ -267,7 +267,7 @@
|
|||||||
"ProfileImageSelectionImportImage": "Εισαγωγή Αρχείου Εικόνας",
|
"ProfileImageSelectionImportImage": "Εισαγωγή Αρχείου Εικόνας",
|
||||||
"ProfileImageSelectionSelectAvatar": "Επιλέξτε Avatar από Firmware",
|
"ProfileImageSelectionSelectAvatar": "Επιλέξτε Avatar από Firmware",
|
||||||
"InputDialogTitle": "Διάλογος Εισαγωγής",
|
"InputDialogTitle": "Διάλογος Εισαγωγής",
|
||||||
"InputDialogOk": "OK",
|
"InputDialogOk": "ΟΚ",
|
||||||
"InputDialogCancel": "Ακύρωση",
|
"InputDialogCancel": "Ακύρωση",
|
||||||
"InputDialogAddNewProfileTitle": "Επιλογή Ονόματος Προφίλ",
|
"InputDialogAddNewProfileTitle": "Επιλογή Ονόματος Προφίλ",
|
||||||
"InputDialogAddNewProfileHeader": "Εισαγωγή Ονόματος Προφίλ",
|
"InputDialogAddNewProfileHeader": "Εισαγωγή Ονόματος Προφίλ",
|
||||||
@@ -315,7 +315,7 @@
|
|||||||
"DialogUpdaterConvertFailedMessage": "Αποτυχία μετατροπής της τρέχουσας έκδοσης Ryujinx.",
|
"DialogUpdaterConvertFailedMessage": "Αποτυχία μετατροπής της τρέχουσας έκδοσης Ryujinx.",
|
||||||
"DialogUpdaterCancelUpdateMessage": "Ακύρωση Ενημέρωσης!",
|
"DialogUpdaterCancelUpdateMessage": "Ακύρωση Ενημέρωσης!",
|
||||||
"DialogUpdaterAlreadyOnLatestVersionMessage": "Χρησιμοποιείτε ήδη την πιο ενημερωμένη έκδοση του Ryujinx!",
|
"DialogUpdaterAlreadyOnLatestVersionMessage": "Χρησιμοποιείτε ήδη την πιο ενημερωμένη έκδοση του Ryujinx!",
|
||||||
"DialogUpdaterFailedToGetVersionMessage": "Σφάλμα κατά την προσπάθεια λήψης έκδοσης από το GitHub Release. Αυτό μπορεί να προκληθεί εάν μία νέα έκδοση συντάσσεται από το GitHub Actions. Δοκιμάστε ξανά σε λίγα λεπτά.",
|
"DialogUpdaterFailedToGetVersionMessage": "Προέκυψε ένα σφάλμα στη λήψη πληροφοριών έκδοσης από τα GitHub Releases. Αυτό δύναται να συμβεί αν μία έκδοση χτίζεται αυτή τη στιγμή στα GitHub Actions. Παρακαλούμε προσπαθήστε αργότερα.",
|
||||||
"DialogUpdaterConvertFailedGithubMessage": "Αποτυχία μετατροπής της ληφθείσας έκδοσης Ryujinx από την έκδοση GitHub.",
|
"DialogUpdaterConvertFailedGithubMessage": "Αποτυχία μετατροπής της ληφθείσας έκδοσης Ryujinx από την έκδοση GitHub.",
|
||||||
"DialogUpdaterDownloadingMessage": "Λήψη Ενημέρωσης...",
|
"DialogUpdaterDownloadingMessage": "Λήψη Ενημέρωσης...",
|
||||||
"DialogUpdaterExtractionMessage": "Εξαγωγή Ενημέρωσης...",
|
"DialogUpdaterExtractionMessage": "Εξαγωγή Ενημέρωσης...",
|
||||||
@@ -412,6 +412,8 @@
|
|||||||
"DlcManagerTableHeadingContainerPathLabel": "Τοποθεσία DLC",
|
"DlcManagerTableHeadingContainerPathLabel": "Τοποθεσία DLC",
|
||||||
"DlcManagerTableHeadingFullPathLabel": "Πλήρης τοποθεσία",
|
"DlcManagerTableHeadingFullPathLabel": "Πλήρης τοποθεσία",
|
||||||
"DlcManagerRemoveAllButton": "Αφαίρεση όλων",
|
"DlcManagerRemoveAllButton": "Αφαίρεση όλων",
|
||||||
|
"DlcManagerEnableAllButton": "Ενεργοποίηση Όλων",
|
||||||
|
"DlcManagerDisableAllButton": "Απενεργοποίηση Όλων",
|
||||||
"MenuBarOptionsChangeLanguage": "Αλλαξε γλώσσα",
|
"MenuBarOptionsChangeLanguage": "Αλλαξε γλώσσα",
|
||||||
"CommonSort": "Κατάταξη",
|
"CommonSort": "Κατάταξη",
|
||||||
"CommonShowNames": "Εμφάνιση ονομάτων",
|
"CommonShowNames": "Εμφάνιση ονομάτων",
|
||||||
@@ -493,6 +495,119 @@
|
|||||||
"SettingsTabNetworkConnection": "Σύνδεση δικτύου",
|
"SettingsTabNetworkConnection": "Σύνδεση δικτύου",
|
||||||
"SettingsTabCpuCache": "Προσωρινή Μνήμη CPU",
|
"SettingsTabCpuCache": "Προσωρινή Μνήμη CPU",
|
||||||
"SettingsTabCpuMemory": "Μνήμη CPU",
|
"SettingsTabCpuMemory": "Μνήμη CPU",
|
||||||
|
"DialogUpdaterFlatpakNotSupportedMessage": "Παρακαλούμε ενημερώστε το Ryujinx μέσω FlatHub.",
|
||||||
|
"UpdaterDisabledWarningTitle": "Ο Διαχειριστής Ενημερώσεων Είναι Απενεργοποιημένος!",
|
||||||
|
"GameListContextMenuOpenSdModsDirectory": "Άνοιγμα Της Τοποθεσίας Των Atmosphere Mods",
|
||||||
|
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
||||||
|
"ControllerSettingsRotate90": "Rotate 90° Clockwise",
|
||||||
|
"IconSize": "Icon Size",
|
||||||
|
"IconSizeTooltip": "Change the size of game icons",
|
||||||
|
"MenuBarOptionsShowConsole": "Show Console",
|
||||||
|
"ShaderCachePurgeError": "Error purging shader cache at {0}: {1}",
|
||||||
|
"UserErrorNoKeys": "Keys not found",
|
||||||
|
"UserErrorNoFirmware": "Firmware not found",
|
||||||
|
"UserErrorFirmwareParsingFailed": "Firmware parsing error",
|
||||||
|
"UserErrorApplicationNotFound": "Application not found",
|
||||||
|
"UserErrorUnknown": "Unknown error",
|
||||||
|
"UserErrorUndefined": "Undefined error",
|
||||||
|
"UserErrorNoKeysDescription": "Ryujinx was unable to find your 'prod.keys' file",
|
||||||
|
"UserErrorNoFirmwareDescription": "Ryujinx was unable to find any firmwares installed",
|
||||||
|
"UserErrorFirmwareParsingFailedDescription": "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.",
|
||||||
|
"UserErrorApplicationNotFoundDescription": "Ryujinx couldn't find a valid application at the given path.",
|
||||||
|
"UserErrorUnknownDescription": "An unknown error occured!",
|
||||||
|
"UserErrorUndefinedDescription": "An undefined error occured! This shouldn't happen, please contact a dev!",
|
||||||
|
"OpenSetupGuideMessage": "Open the Setup Guide",
|
||||||
|
"NoUpdate": "No Update",
|
||||||
|
"TitleUpdateVersionLabel": "Version {0} - {1}",
|
||||||
|
"RyujinxInfo": "Ryujinx - Info",
|
||||||
|
"RyujinxConfirm": "Ryujinx - Confirmation",
|
||||||
|
"FileDialogAllTypes": "All types",
|
||||||
|
"Never": "Never",
|
||||||
|
"SwkbdMinCharacters": "Must be at least {0} characters long",
|
||||||
|
"SwkbdMinRangeCharacters": "Must be {0}-{1} characters long",
|
||||||
|
"SoftwareKeyboard": "Software Keyboard",
|
||||||
|
"DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
|
||||||
|
"DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
|
||||||
|
"DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n",
|
||||||
|
"UpdaterRenaming": "Renaming Old Files...",
|
||||||
|
"UpdaterRenameFailed": "Updater was unable to rename file: {0}",
|
||||||
|
"UpdaterAddingFiles": "Adding New Files...",
|
||||||
|
"UpdaterExtracting": "Extracting Update...",
|
||||||
|
"UpdaterDownloading": "Downloading Update...",
|
||||||
|
"Game": "Game",
|
||||||
|
"Docked": "Docked",
|
||||||
|
"Handheld": "Handheld",
|
||||||
|
"ConnectionError": "Connection Error.",
|
||||||
|
"AboutPageDeveloperListMore": "{0} and more...",
|
||||||
|
"ApiError": "API Error.",
|
||||||
|
"LoadingHeading": "Loading {0}",
|
||||||
|
"CompilingPPTC": "Compiling PTC",
|
||||||
|
"CompilingShaders": "Compiling Shaders",
|
||||||
|
"AllKeyboards": "All keyboards",
|
||||||
|
"OpenFileDialogTitle": "Select a supported file to open",
|
||||||
|
"OpenFolderDialogTitle": "Select a folder with an unpacked game",
|
||||||
|
"AllSupportedFormats": "All Supported Formats",
|
||||||
|
"RyujinxUpdater": "Ryujinx Updater",
|
||||||
|
"SettingsTabHotkeys": "Keyboard Hotkeys",
|
||||||
|
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
|
||||||
|
"SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:",
|
||||||
|
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
|
||||||
|
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
|
||||||
|
"SettingsTabHotkeysPauseHotkey": "Pause:",
|
||||||
|
"SettingsTabHotkeysToggleMuteHotkey": "Mute:",
|
||||||
"ControllerMotionTitle": "Motion Control Settings",
|
"ControllerMotionTitle": "Motion Control Settings",
|
||||||
"ControllerRumbleTitle": "Rumble Settings"
|
"ControllerRumbleTitle": "Rumble Settings",
|
||||||
|
"SettingsSelectThemeFileDialogTitle": "Select Theme File",
|
||||||
|
"SettingsXamlThemeFile": "Xaml Theme File",
|
||||||
|
"AvatarWindowTitle": "Manage Accounts - Avatar",
|
||||||
|
"Amiibo": "Amiibo",
|
||||||
|
"Unknown": "Unknown",
|
||||||
|
"Usage": "Usage",
|
||||||
|
"Writable": "Writable",
|
||||||
|
"SelectDlcDialogTitle": "Select DLC files",
|
||||||
|
"SelectUpdateDialogTitle": "Select update files",
|
||||||
|
"UserProfileWindowTitle": "User Profiles Manager",
|
||||||
|
"CheatWindowTitle": "Cheats Manager",
|
||||||
|
"DlcWindowTitle": "Downloadable Content Manager",
|
||||||
|
"UpdateWindowTitle": "Title Update Manager",
|
||||||
|
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||||
|
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||||
|
"UserProfilesEditProfile": "Edit Selected",
|
||||||
|
"Cancel": "Cancel",
|
||||||
|
"Save": "Save",
|
||||||
|
"Discard": "Discard",
|
||||||
|
"UserProfilesSetProfileImage": "Set Profile Image",
|
||||||
|
"UserProfileEmptyNameError": "Name is required",
|
||||||
|
"UserProfileNoImageError": "Profile image must be set",
|
||||||
|
"GameUpdateWindowHeading": "{0} Update(s) available for {1} ({2})",
|
||||||
|
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
|
||||||
|
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
|
||||||
|
"UserProfilesName": "Name:",
|
||||||
|
"UserProfilesUserId": "User Id:",
|
||||||
|
"SettingsTabGraphicsBackend": "Graphics Backend",
|
||||||
|
"SettingsTabGraphicsBackendTooltip": "Graphics Backend to use",
|
||||||
|
"SettingsEnableTextureRecompression": "Enable Texture Recompression",
|
||||||
|
"SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GiB VRAM.\n\nLeave OFF if unsure.",
|
||||||
|
"SettingsTabGraphicsPreferredGpu": "Preferred GPU",
|
||||||
|
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
|
||||||
|
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
|
||||||
|
"SettingsGpuBackendRestartMessage": "Graphics Backend or GPU settings have been modified. This will require a restart to be applied",
|
||||||
|
"SettingsGpuBackendRestartSubMessage": "Do you want to restart now?",
|
||||||
|
"RyujinxUpdaterMessage": "Do you want to update Ryujinx to the latest version?",
|
||||||
|
"SettingsTabHotkeysVolumeUpHotkey": "Increase Volume:",
|
||||||
|
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
|
||||||
|
"SettingsEnableMacroHLE": "Enable Macro HLE",
|
||||||
|
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
|
||||||
|
"VolumeShort": "Vol",
|
||||||
|
"UserProfilesManageSaves": "Manage Saves",
|
||||||
|
"DeleteUserSave": "Do you want to delete user save for this game?",
|
||||||
|
"IrreversibleActionNote": "This action is not reversible.",
|
||||||
|
"SaveManagerHeading": "Manage Saves for {0}",
|
||||||
|
"SaveManagerTitle": "Save Manager",
|
||||||
|
"Name": "Name",
|
||||||
|
"Size": "Size",
|
||||||
|
"Search": "Search",
|
||||||
|
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
||||||
|
"Recover": "Recover",
|
||||||
|
"UserProfilesRecoverHeading": "Saves were found for the following accounts"
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user