Ryujinx/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs

171 lines
5.2 KiB
C#
Raw Normal View History

using ChocolArm64.Memory;
using Ryujinx.Audio;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Services.Aud.AudioOut;
using System.Collections.Generic;
using System.Text;
using static Ryujinx.HLE.HOS.ErrorCode;
namespace Ryujinx.HLE.HOS.Services.Aud
{
2018-03-22 00:30:10 +01:00
class IAudioOutManager : IpcService
{
private const string DefaultAudioOutput = "DeviceOut";
private const int DefaultSampleRate = 48000;
private const int DefaultChannelsCount = 2;
2018-12-01 21:01:59 +01:00
private Dictionary<int, ServiceProcessRequest> _commands;
2018-12-01 21:01:59 +01:00
public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => _commands;
2018-03-22 00:30:10 +01:00
public IAudioOutManager()
{
2018-12-01 21:01:59 +01:00
_commands = new Dictionary<int, ServiceProcessRequest>()
{
{ 0, ListAudioOuts },
{ 1, OpenAudioOut },
{ 2, ListAudioOutsAuto },
{ 3, OpenAudioOutAuto }
};
}
2018-12-01 21:01:59 +01:00
public long ListAudioOuts(ServiceCtx context)
{
return ListAudioOutsImpl(
2018-12-01 21:01:59 +01:00
context,
context.Request.ReceiveBuff[0].Position,
context.Request.ReceiveBuff[0].Size);
}
2018-12-01 21:01:59 +01:00
public long OpenAudioOut(ServiceCtx context)
{
return OpenAudioOutImpl(
2018-12-01 21:01:59 +01:00
context,
context.Request.SendBuff[0].Position,
context.Request.SendBuff[0].Size,
context.Request.ReceiveBuff[0].Position,
context.Request.ReceiveBuff[0].Size);
}
2018-12-01 21:01:59 +01:00
public long ListAudioOutsAuto(ServiceCtx context)
{
2018-12-01 21:01:59 +01:00
(long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
2018-12-01 21:01:59 +01:00
return ListAudioOutsImpl(context, recvPosition, recvSize);
}
2018-12-01 21:01:59 +01:00
public long OpenAudioOutAuto(ServiceCtx context)
{
2018-12-01 21:01:59 +01:00
(long sendPosition, long sendSize) = context.Request.GetBufferType0x21();
(long recvPosition, long recvSize) = context.Request.GetBufferType0x22();
return OpenAudioOutImpl(
2018-12-01 21:01:59 +01:00
context,
sendPosition,
sendSize,
recvPosition,
recvSize);
}
2018-12-01 21:01:59 +01:00
private long ListAudioOutsImpl(ServiceCtx context, long position, long size)
{
2018-12-01 21:01:59 +01:00
int nameCount = 0;
2018-12-01 21:01:59 +01:00
byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioOutput + "\0");
2018-12-01 21:01:59 +01:00
if ((ulong)deviceNameBuffer.Length <= (ulong)size)
{
2018-12-01 21:01:59 +01:00
context.Memory.WriteBytes(position, deviceNameBuffer);
2018-12-01 21:01:59 +01:00
nameCount++;
}
else
{
2018-12-01 21:01:59 +01:00
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {size} too small!");
}
2018-12-01 21:01:59 +01:00
context.ResponseData.Write(nameCount);
return 0;
}
2018-12-01 21:01:59 +01:00
private long OpenAudioOutImpl(ServiceCtx context, long sendPosition, long sendSize, long receivePosition, long receiveSize)
{
2018-12-01 21:01:59 +01:00
string deviceName = MemoryHelper.ReadAsciiString(
context.Memory,
sendPosition,
sendSize);
2018-12-01 21:01:59 +01:00
if (deviceName == string.Empty)
{
2018-12-01 21:01:59 +01:00
deviceName = DefaultAudioOutput;
}
2018-12-01 21:01:59 +01:00
if (deviceName != DefaultAudioOutput)
{
Logger.PrintWarning(LogClass.Audio, "Invalid device name!");
return MakeError(ErrorModule.Audio, AudErr.DeviceNotFound);
}
2018-12-01 21:01:59 +01:00
byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(deviceName + "\0");
2018-12-01 21:01:59 +01:00
if ((ulong)deviceNameBuffer.Length <= (ulong)receiveSize)
{
2018-12-01 21:01:59 +01:00
context.Memory.WriteBytes(receivePosition, deviceNameBuffer);
}
else
{
2018-12-01 21:01:59 +01:00
Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {receiveSize} too small!");
}
2018-12-01 21:01:59 +01:00
int sampleRate = context.RequestData.ReadInt32();
int channels = context.RequestData.ReadInt32();
2018-12-01 21:01:59 +01:00
if (sampleRate == 0)
{
2018-12-01 21:01:59 +01:00
sampleRate = DefaultSampleRate;
}
2018-12-01 21:01:59 +01:00
if (sampleRate != DefaultSampleRate)
{
Logger.PrintWarning(LogClass.Audio, "Invalid sample rate!");
return MakeError(ErrorModule.Audio, AudErr.UnsupportedSampleRate);
}
2018-12-01 21:01:59 +01:00
channels = (ushort)channels;
2018-12-01 21:01:59 +01:00
if (channels == 0)
{
2018-12-01 21:01:59 +01:00
channels = DefaultChannelsCount;
}
2018-12-01 21:01:59 +01:00
KEvent releaseEvent = new KEvent(context.Device.System);
2018-12-01 21:01:59 +01:00
ReleaseCallback callback = () =>
{
2018-12-01 21:01:59 +01:00
releaseEvent.ReadableEvent.Signal();
};
2018-12-01 21:01:59 +01:00
IAalOutput audioOut = context.Device.AudioOut;
2018-12-01 21:01:59 +01:00
int track = audioOut.OpenTrack(sampleRate, channels, callback);
2018-12-01 21:01:59 +01:00
MakeObject(context, new IAudioOut(audioOut, releaseEvent, track));
2018-12-01 21:01:59 +01:00
context.ResponseData.Write(sampleRate);
context.ResponseData.Write(channels);
context.ResponseData.Write((int)SampleFormat.PcmInt16);
context.ResponseData.Write((int)PlaybackState.Stopped);
return 0;
}
}
}