Add a new JIT compiler for CPU code (#693)
* Start of the ARMeilleure project
* Refactoring around the old IRAdapter, now renamed to PreAllocator
* Optimize the LowestBitSet method
* Add CLZ support and fix CLS implementation
* Add missing Equals and GetHashCode overrides on some structs, misc small tweaks
* Implement the ByteSwap IR instruction, and some refactoring on the assembler
* Implement the DivideUI IR instruction and fix 64-bits IDIV
* Correct constant operand type on CSINC
* Move division instructions implementation to InstEmitDiv
* Fix destination type for the ConditionalSelect IR instruction
* Implement UMULH and SMULH, with new IR instructions
* Fix some issues with shift instructions
* Fix constant types for BFM instructions
* Fix up new tests using the new V128 struct
* Update tests
* Move DIV tests to a separate file
* Add support for calls, and some instructions that depends on them
* Start adding support for SIMD & FP types, along with some of the related ARM instructions
* Fix some typos and the divide instruction with FP operands
* Fix wrong method call on Clz_V
* Implement ARM FP & SIMD move instructions, Saddlv_V, and misc. fixes
* Implement SIMD logical instructions and more misc. fixes
* Fix PSRAD x86 instruction encoding, TRN, UABD and UABDL implementations
* Implement float conversion instruction, merge in LDj3SNuD fixes, and some other misc. fixes
* Implement SIMD shift instruction and fix Dup_V
* Add SCVTF and UCVTF (vector, fixed-point) variants to the opcode table
* Fix check with tolerance on tester
* Implement FP & SIMD comparison instructions, and some fixes
* Update FCVT (Scalar) encoding on the table to support the Half-float variants
* Support passing V128 structs, some cleanup on the register allocator, merge LDj3SNuD fixes
* Use old memory access methods, made a start on SIMD memory insts support, some fixes
* Fix float constant passed to functions, save and restore non-volatile XMM registers, other fixes
* Fix arguments count with struct return values, other fixes
* More instructions
* Misc. fixes and integrate LDj3SNuD fixes
* Update tests
* Add a faster linear scan allocator, unwinding support on windows, and other changes
* Update Ryujinx.HLE
* Update Ryujinx.Graphics
* Fix V128 return pointer passing, RCX is clobbered
* Update Ryujinx.Tests
* Update ITimeZoneService
* Stop using GetFunctionPointer as that can't be called from native code, misc. fixes and tweaks
* Use generic GetFunctionPointerForDelegate method and other tweaks
* Some refactoring on the code generator, assert on invalid operations and use a separate enum for intrinsics
* Remove some unused code on the assembler
* Fix REX.W prefix regression on float conversion instructions, add some sort of profiler
* Add hardware capability detection
* Fix regression on Sha1h and revert Fcm** changes
* Add SSE2-only paths on vector extract and insert, some refactoring on the pre-allocator
* Fix silly mistake introduced on last commit on CpuId
* Generate inline stack probes when the stack allocation is too large
* Initial support for the System-V ABI
* Support multiple destination operands
* Fix SSE2 VectorInsert8 path, and other fixes
* Change placement of XMM callee save and restore code to match other compilers
* Rename Dest to Destination and Inst to Instruction
* Fix a regression related to calls and the V128 type
* Add an extra space on comments to match code style
* Some refactoring
* Fix vector insert FP32 SSE2 path
* Port over the ARM32 instructions
* Avoid memory protection races on JIT Cache
* Another fix on VectorInsert FP32 (thanks to LDj3SNuD
* Float operands don't need to use the same register when VEX is supported
* Add a new register allocator, higher quality code for hot code (tier up), and other tweaks
* Some nits, small improvements on the pre allocator
* CpuThreadState is gone
* Allow changing CPU emulators with a config entry
* Add runtime identifiers on the ARMeilleure project
* Allow switching between CPUs through a config entry (pt. 2)
* Change win10-x64 to win-x64 on projects
* Update the Ryujinx project to use ARMeilleure
* Ensure that the selected register is valid on the hybrid allocator
* Allow exiting on returns to 0 (should fix test regression)
* Remove register assignments for most used variables on the hybrid allocator
* Do not use fixed registers as spill temp
* Add missing namespace and remove unneeded using
* Address PR feedback
* Fix types, etc
* Enable AssumeStrictAbiCompliance by default
* Ensure that Spill and Fill don't load or store any more than necessary
2019-08-08 20:56:22 +02:00
|
|
|
using ARMeilleure.Memory;
|
2018-07-15 04:57:41 +02:00
|
|
|
using Ryujinx.Audio;
|
|
|
|
using Ryujinx.Audio.Adpcm;
|
2018-10-17 19:15:50 +02:00
|
|
|
using Ryujinx.Common.Logging;
|
2018-08-17 01:47:36 +02:00
|
|
|
using Ryujinx.HLE.HOS.Ipc;
|
2018-12-18 06:33:36 +01:00
|
|
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
|
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
2018-08-17 01:47:36 +02:00
|
|
|
using Ryujinx.HLE.Utilities;
|
2018-07-15 04:57:41 +02:00
|
|
|
using System;
|
|
|
|
using System.Runtime.InteropServices;
|
2018-11-15 03:22:50 +01:00
|
|
|
using System.Runtime.Intrinsics;
|
|
|
|
using System.Runtime.Intrinsics.X86;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2019-09-19 02:45:11 +02:00
|
|
|
namespace Ryujinx.HLE.HOS.Services.Audio.AudioRendererManager
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
|
|
|
class IAudioRenderer : IpcService, IDisposable
|
|
|
|
{
|
2019-07-02 04:39:22 +02:00
|
|
|
// This is the amount of samples that are going to be appended
|
|
|
|
// each time that RequestUpdateAudioRenderer is called. Ideally,
|
|
|
|
// this value shouldn't be neither too small (to avoid the player
|
|
|
|
// starving due to running out of samples) or too large (to avoid
|
|
|
|
// high latency).
|
2018-07-15 04:57:41 +02:00
|
|
|
private const int MixBufferSamplesCount = 960;
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private KEvent _updateEvent;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2019-10-31 19:09:03 +01:00
|
|
|
private MemoryManager _memory;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private IAalOutput _audioOut;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private AudioRendererParameter _params;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private MemoryPoolContext[] _memoryPools;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private VoiceContext[] _voices;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2020-01-17 10:07:27 +01:00
|
|
|
private EffectContext[] _effects;
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private int _track;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private PlayState _playState;
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2020-04-30 05:03:05 +02:00
|
|
|
private ulong _elapsedFrameCount;
|
|
|
|
|
2018-09-19 01:36:43 +02:00
|
|
|
public IAudioRenderer(
|
2018-12-06 12:16:24 +01:00
|
|
|
Horizon system,
|
2019-10-31 19:09:03 +01:00
|
|
|
MemoryManager memory,
|
2018-12-06 12:16:24 +01:00
|
|
|
IAalOutput audioOut,
|
2020-01-17 10:07:27 +01:00
|
|
|
AudioRendererParameter rendererParams)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
_updateEvent = new KEvent(system);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_memory = memory;
|
|
|
|
_audioOut = audioOut;
|
2020-01-17 10:07:27 +01:00
|
|
|
_params = rendererParams;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_track = audioOut.OpenTrack(
|
2019-09-20 01:49:05 +02:00
|
|
|
AudioRendererConsts.HostSampleRate,
|
|
|
|
AudioRendererConsts.HostChannelsCount,
|
2018-07-15 04:57:41 +02:00
|
|
|
AudioCallback);
|
|
|
|
|
2020-01-17 10:07:27 +01:00
|
|
|
_memoryPools = CreateArray<MemoryPoolContext>(rendererParams.EffectCount + rendererParams.VoiceCount * 4);
|
|
|
|
|
|
|
|
_voices = CreateArray<VoiceContext>(rendererParams.VoiceCount);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2020-01-17 10:07:27 +01:00
|
|
|
_effects = CreateArray<EffectContext>(rendererParams.EffectCount);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2020-04-30 05:03:05 +02:00
|
|
|
_elapsedFrameCount = 0;
|
|
|
|
|
2018-07-15 04:57:41 +02:00
|
|
|
InitializeAudioOut();
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_playState = PlayState.Stopped;
|
2018-10-07 17:12:11 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(0)]
|
|
|
|
// GetSampleRate() -> u32
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode GetSampleRate(ServiceCtx context)
|
2018-10-07 17:12:11 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
context.ResponseData.Write(_params.SampleRate);
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-10-07 17:12:11 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(1)]
|
|
|
|
// GetSampleCount() -> u32
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode GetSampleCount(ServiceCtx context)
|
2018-10-07 17:12:11 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
context.ResponseData.Write(_params.SampleCount);
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-10-07 17:12:11 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(2)]
|
2018-10-07 17:12:11 +02:00
|
|
|
// GetMixBufferCount() -> u32
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode GetMixBufferCount(ServiceCtx context)
|
2018-10-07 17:12:11 +02:00
|
|
|
{
|
2019-09-20 01:49:05 +02:00
|
|
|
context.ResponseData.Write(_params.SubMixCount);
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-10-07 17:12:11 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(3)]
|
2018-10-07 17:12:11 +02:00
|
|
|
// GetState() -> u32
|
2019-07-14 23:22:53 +02:00
|
|
|
public ResultCode GetState(ServiceCtx context)
|
2018-10-07 17:12:11 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
context.ResponseData.Write((int)_playState);
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-01-11 01:11:46 +01:00
|
|
|
Logger.PrintStub(LogClass.ServiceAudio, new { State = Enum.GetName(typeof(PlayState), _playState) });
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void AudioCallback()
|
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
_updateEvent.ReadableEvent.Signal();
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private static T[] CreateArray<T>(int size) where T : new()
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
T[] output = new T[size];
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
for (int index = 0; index < size; index++)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
output[index] = new T();
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
return output;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void InitializeAudioOut()
|
|
|
|
{
|
|
|
|
AppendMixedBuffer(0);
|
|
|
|
AppendMixedBuffer(1);
|
|
|
|
AppendMixedBuffer(2);
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_audioOut.Start(_track);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(4)]
|
|
|
|
// RequestUpdateAudioRenderer(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 5>)
|
|
|
|
// -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6>, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6>)
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode RequestUpdateAudioRenderer(ServiceCtx context)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
long outputPosition = context.Request.ReceiveBuff[0].Position;
|
|
|
|
long outputSize = context.Request.ReceiveBuff[0].Size;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
MemoryHelper.FillWithZeros(context.Memory, outputPosition, (int)outputSize);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
long inputPosition = context.Request.SendBuff[0].Position;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
StructReader reader = new StructReader(context.Memory, inputPosition);
|
|
|
|
StructWriter writer = new StructWriter(context.Memory, outputPosition);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
UpdateDataHeader inputHeader = reader.Read<UpdateDataHeader>();
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2019-09-20 01:49:05 +02:00
|
|
|
BehaviorInfo behaviorInfo = new BehaviorInfo();
|
|
|
|
|
|
|
|
behaviorInfo.SetUserLibRevision(inputHeader.Revision);
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
reader.Read<BehaviorIn>(inputHeader.BehaviorSize);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
MemoryPoolIn[] memoryPoolsIn = reader.Read<MemoryPoolIn>(inputHeader.MemoryPoolSize);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
for (int index = 0; index < memoryPoolsIn.Length; index++)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
MemoryPoolIn memoryPool = memoryPoolsIn[index];
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
if (memoryPool.State == MemoryPoolState.RequestAttach)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
_memoryPools[index].OutStatus.State = MemoryPoolState.Attached;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
2018-12-06 12:16:24 +01:00
|
|
|
else if (memoryPool.State == MemoryPoolState.RequestDetach)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
_memoryPools[index].OutStatus.State = MemoryPoolState.Detached;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
reader.Read<VoiceChannelResourceIn>(inputHeader.VoiceResourceSize);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
VoiceIn[] voicesIn = reader.Read<VoiceIn>(inputHeader.VoiceSize);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
for (int index = 0; index < voicesIn.Length; index++)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
VoiceIn voice = voicesIn[index];
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
VoiceContext voiceCtx = _voices[index];
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
voiceCtx.SetAcquireState(voice.Acquired != 0);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
if (voice.Acquired == 0)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
if (voice.FirstUpdate != 0)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
voiceCtx.AdpcmCtx = GetAdpcmDecoderContext(
|
|
|
|
voice.AdpcmCoeffsPosition,
|
|
|
|
voice.AdpcmCoeffsSize);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
voiceCtx.SampleFormat = voice.SampleFormat;
|
|
|
|
voiceCtx.SampleRate = voice.SampleRate;
|
|
|
|
voiceCtx.ChannelsCount = voice.ChannelsCount;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
voiceCtx.SetBufferIndex(voice.BaseWaveBufferIndex);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
voiceCtx.WaveBuffers[0] = voice.WaveBuffer0;
|
|
|
|
voiceCtx.WaveBuffers[1] = voice.WaveBuffer1;
|
|
|
|
voiceCtx.WaveBuffers[2] = voice.WaveBuffer2;
|
|
|
|
voiceCtx.WaveBuffers[3] = voice.WaveBuffer3;
|
|
|
|
voiceCtx.Volume = voice.Volume;
|
|
|
|
voiceCtx.PlayState = voice.PlayState;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2020-01-17 10:07:27 +01:00
|
|
|
EffectIn[] effectsIn = reader.Read<EffectIn>(inputHeader.EffectSize);
|
|
|
|
|
|
|
|
for (int index = 0; index < effectsIn.Length; index++)
|
|
|
|
{
|
|
|
|
if (effectsIn[index].IsNew != 0)
|
|
|
|
{
|
|
|
|
_effects[index].OutStatus.State = EffectState.New;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-15 04:57:41 +02:00
|
|
|
UpdateAudio();
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
UpdateDataHeader outputHeader = new UpdateDataHeader();
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
int updateHeaderSize = Marshal.SizeOf<UpdateDataHeader>();
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2019-09-20 01:49:05 +02:00
|
|
|
outputHeader.Revision = AudioRendererConsts.RevMagic;
|
2018-12-06 12:16:24 +01:00
|
|
|
outputHeader.BehaviorSize = 0xb0;
|
|
|
|
outputHeader.MemoryPoolSize = (_params.EffectCount + _params.VoiceCount * 4) * 0x10;
|
|
|
|
outputHeader.VoiceSize = _params.VoiceCount * 0x10;
|
|
|
|
outputHeader.EffectSize = _params.EffectCount * 0x10;
|
|
|
|
outputHeader.SinkSize = _params.SinkCount * 0x20;
|
|
|
|
outputHeader.PerformanceManagerSize = 0x10;
|
2019-09-20 01:49:05 +02:00
|
|
|
|
|
|
|
if (behaviorInfo.IsElapsedFrameCountSupported())
|
|
|
|
{
|
|
|
|
outputHeader.ElapsedFrameCountInfoSize = 0x10;
|
|
|
|
}
|
|
|
|
|
|
|
|
outputHeader.TotalSize = updateHeaderSize +
|
|
|
|
outputHeader.BehaviorSize +
|
|
|
|
outputHeader.MemoryPoolSize +
|
|
|
|
outputHeader.VoiceSize +
|
|
|
|
outputHeader.EffectSize +
|
|
|
|
outputHeader.SinkSize +
|
|
|
|
outputHeader.PerformanceManagerSize +
|
|
|
|
outputHeader.ElapsedFrameCountInfoSize;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
writer.Write(outputHeader);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
foreach (MemoryPoolContext memoryPool in _memoryPools)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
writer.Write(memoryPool.OutStatus);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
foreach (VoiceContext voice in _voices)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
writer.Write(voice.OutStatus);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2020-01-17 10:07:27 +01:00
|
|
|
foreach (EffectContext effect in _effects)
|
|
|
|
{
|
|
|
|
writer.Write(effect.OutStatus);
|
|
|
|
}
|
|
|
|
|
2020-04-30 05:03:05 +02:00
|
|
|
writer.SkipBytes(_params.SinkCount * 0x20);
|
|
|
|
writer.SkipBytes(outputHeader.PerformanceManagerSize);
|
|
|
|
writer.SkipBytes(outputHeader.BehaviorSize);
|
|
|
|
|
|
|
|
if (behaviorInfo.IsElapsedFrameCountSupported())
|
|
|
|
{
|
|
|
|
writer.Write(new RendererInfoOut
|
|
|
|
{
|
|
|
|
ElapsedFrameCount = _elapsedFrameCount
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(5)]
|
|
|
|
// Start()
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode StartAudioRenderer(ServiceCtx context)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2019-01-11 01:11:46 +01:00
|
|
|
Logger.PrintStub(LogClass.ServiceAudio);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_playState = PlayState.Playing;
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(6)]
|
|
|
|
// Stop()
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode StopAudioRenderer(ServiceCtx context)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2019-01-11 01:11:46 +01:00
|
|
|
Logger.PrintStub(LogClass.ServiceAudio);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_playState = PlayState.Stopped;
|
2018-10-07 17:12:11 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2019-07-12 03:13:43 +02:00
|
|
|
[Command(7)]
|
|
|
|
// QuerySystemEvent() -> handle<copy, event>
|
2019-07-14 21:04:38 +02:00
|
|
|
public ResultCode QuerySystemEvent(ServiceCtx context)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
if (context.Process.HandleTable.GenerateHandle(_updateEvent.ReadableEvent, out int handle) != KernelResult.Success)
|
2018-09-23 20:11:46 +02:00
|
|
|
{
|
|
|
|
throw new InvalidOperationException("Out of handles!");
|
|
|
|
}
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2019-07-14 21:04:38 +02:00
|
|
|
return ResultCode.Success;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private AdpcmDecoderContext GetAdpcmDecoderContext(long position, long size)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
if (size == 0)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-09-19 02:45:11 +02:00
|
|
|
AdpcmDecoderContext context = new AdpcmDecoderContext
|
|
|
|
{
|
|
|
|
Coefficients = new short[size >> 1]
|
|
|
|
};
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
for (int offset = 0; offset < size; offset += 2)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
context.Coefficients[offset >> 1] = _memory.ReadInt16(position + offset);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
return context;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void UpdateAudio()
|
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
long[] released = _audioOut.GetReleasedBuffers(_track, 2);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
for (int index = 0; index < released.Length; index++)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
AppendMixedBuffer(released[index]);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
2020-04-30 05:03:05 +02:00
|
|
|
|
|
|
|
_elapsedFrameCount++;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private void AppendMixedBuffer(long tag)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2019-09-20 01:49:05 +02:00
|
|
|
int[] mixBuffer = new int[MixBufferSamplesCount * AudioRendererConsts.HostChannelsCount];
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
foreach (VoiceContext voice in _voices)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2019-02-12 18:05:05 +01:00
|
|
|
if (!voice.Playing || voice.CurrentWaveBuffer.Size == 0)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
int outOffset = 0;
|
|
|
|
int pendingSamples = MixBufferSamplesCount;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
while (pendingSamples > 0)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
int[] samples = voice.GetBufferData(_memory, pendingSamples, out int returnedSamples);
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
if (returnedSamples == 0)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
pendingSamples -= returnedSamples;
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
for (int offset = 0; offset < samples.Length; offset++)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
mixBuffer[outOffset++] += (int)(samples[offset] * voice.Volume);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
_audioOut.AppendBuffer(_track, tag, GetFinalBuffer(mixBuffer));
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
private unsafe static short[] GetFinalBuffer(int[] buffer)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
short[] output = new short[buffer.Length];
|
2018-07-15 04:57:41 +02:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
int offset = 0;
|
2018-11-15 03:22:50 +01:00
|
|
|
|
|
|
|
// Perform Saturation using SSE2 if supported
|
|
|
|
if (Sse2.IsSupported)
|
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
fixed (int* inptr = buffer)
|
|
|
|
fixed (short* outptr = output)
|
2018-11-15 03:22:50 +01:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
for (; offset + 32 <= buffer.Length; offset += 32)
|
2018-11-15 03:22:50 +01:00
|
|
|
{
|
|
|
|
// Unroll the loop a little to ensure the CPU pipeline
|
|
|
|
// is always full.
|
2018-12-06 12:16:24 +01:00
|
|
|
Vector128<int> block1A = Sse2.LoadVector128(inptr + offset + 0);
|
|
|
|
Vector128<int> block1B = Sse2.LoadVector128(inptr + offset + 4);
|
2018-11-15 03:22:50 +01:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
Vector128<int> block2A = Sse2.LoadVector128(inptr + offset + 8);
|
|
|
|
Vector128<int> block2B = Sse2.LoadVector128(inptr + offset + 12);
|
2018-11-15 03:22:50 +01:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
Vector128<int> block3A = Sse2.LoadVector128(inptr + offset + 16);
|
|
|
|
Vector128<int> block3B = Sse2.LoadVector128(inptr + offset + 20);
|
2018-11-15 03:22:50 +01:00
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
Vector128<int> block4A = Sse2.LoadVector128(inptr + offset + 24);
|
|
|
|
Vector128<int> block4B = Sse2.LoadVector128(inptr + offset + 28);
|
2018-11-15 03:22:50 +01:00
|
|
|
|
|
|
|
Vector128<short> output1 = Sse2.PackSignedSaturate(block1A, block1B);
|
|
|
|
Vector128<short> output2 = Sse2.PackSignedSaturate(block2A, block2B);
|
|
|
|
Vector128<short> output3 = Sse2.PackSignedSaturate(block3A, block3B);
|
|
|
|
Vector128<short> output4 = Sse2.PackSignedSaturate(block4A, block4B);
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
Sse2.Store(outptr + offset + 0, output1);
|
|
|
|
Sse2.Store(outptr + offset + 8, output2);
|
|
|
|
Sse2.Store(outptr + offset + 16, output3);
|
|
|
|
Sse2.Store(outptr + offset + 24, output4);
|
2018-11-15 03:22:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process left overs
|
2018-12-06 12:16:24 +01:00
|
|
|
for (; offset < buffer.Length; offset++)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
output[offset] = DspUtils.Saturate(buffer[offset]);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
return output;
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
Dispose(true);
|
|
|
|
}
|
|
|
|
|
2018-12-06 12:16:24 +01:00
|
|
|
protected virtual void Dispose(bool disposing)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
if (disposing)
|
2018-07-15 04:57:41 +02:00
|
|
|
{
|
2018-12-06 12:16:24 +01:00
|
|
|
_audioOut.CloseTrack(_track);
|
2018-07-15 04:57:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-12 03:13:43 +02:00
|
|
|
}
|