Ryujinx/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/VoiceContext.cs

196 lines
5.1 KiB
C#
Raw Normal View History

using ChocolArm64.Memory;
using Ryujinx.Audio.Adpcm;
using System;
namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer
{
class VoiceContext
{
2018-12-01 20:01:59 +00:00
private bool _acquired;
private bool _bufferReload;
2018-12-01 20:01:59 +00:00
private int _resamplerFracPart;
2018-12-01 20:01:59 +00:00
private int _bufferIndex;
private int _offset;
public int SampleRate;
public int ChannelsCount;
public float Volume;
public PlayState PlayState;
public SampleFormat SampleFormat;
public AdpcmDecoderContext AdpcmCtx;
public WaveBuffer[] WaveBuffers;
public VoiceOut OutStatus;
2018-12-01 20:01:59 +00:00
private int[] _samples;
2018-12-01 20:01:59 +00:00
public bool Playing => _acquired && PlayState == PlayState.Playing;
public VoiceContext()
{
WaveBuffers = new WaveBuffer[4];
}
2018-12-01 20:01:59 +00:00
public void SetAcquireState(bool newState)
{
2018-12-01 20:01:59 +00:00
if (_acquired && !newState)
{
//Release.
Reset();
}
2018-12-01 20:01:59 +00:00
_acquired = newState;
}
private void Reset()
{
2018-12-01 20:01:59 +00:00
_bufferReload = true;
2018-12-01 20:01:59 +00:00
_bufferIndex = 0;
_offset = 0;
OutStatus.PlayedSamplesCount = 0;
OutStatus.PlayedWaveBuffersCount = 0;
OutStatus.VoiceDropsCount = 0;
}
2018-12-01 20:01:59 +00:00
public int[] GetBufferData(MemoryManager memory, int maxSamples, out int samplesCount)
{
if (!Playing)
{
2018-12-01 20:01:59 +00:00
samplesCount = 0;
return null;
}
2018-12-01 20:01:59 +00:00
if (_bufferReload)
{
2018-12-01 20:01:59 +00:00
_bufferReload = false;
2018-12-01 20:01:59 +00:00
UpdateBuffer(memory);
}
2018-12-01 20:01:59 +00:00
WaveBuffer wb = WaveBuffers[_bufferIndex];
2018-12-01 20:01:59 +00:00
int maxSize = _samples.Length - _offset;
2018-12-01 20:01:59 +00:00
int size = maxSamples * AudioConsts.HostChannelsCount;
2018-12-01 20:01:59 +00:00
if (size > maxSize)
{
2018-12-01 20:01:59 +00:00
size = maxSize;
}
2018-12-01 20:01:59 +00:00
int[] output = new int[size];
2018-12-01 20:01:59 +00:00
Array.Copy(_samples, _offset, output, 0, size);
2018-12-01 20:01:59 +00:00
samplesCount = size / AudioConsts.HostChannelsCount;
2018-12-01 20:01:59 +00:00
OutStatus.PlayedSamplesCount += samplesCount;
2018-12-01 20:01:59 +00:00
_offset += size;
2018-12-01 20:01:59 +00:00
if (_offset == _samples.Length)
{
2018-12-01 20:01:59 +00:00
_offset = 0;
2018-12-01 20:01:59 +00:00
if (wb.Looping == 0)
{
2018-12-01 20:01:59 +00:00
SetBufferIndex((_bufferIndex + 1) & 3);
}
OutStatus.PlayedWaveBuffersCount++;
2018-12-01 20:01:59 +00:00
if (wb.LastBuffer != 0)
{
PlayState = PlayState.Paused;
}
}
2018-12-01 20:01:59 +00:00
return output;
}
2018-12-01 20:01:59 +00:00
private void UpdateBuffer(MemoryManager memory)
{
//TODO: Implement conversion for formats other
//than interleaved stereo (2 channels).
//As of now, it assumes that HostChannelsCount == 2.
2018-12-01 20:01:59 +00:00
WaveBuffer wb = WaveBuffers[_bufferIndex];
2018-12-01 20:01:59 +00:00
if (wb.Position == 0)
{
2018-12-01 20:01:59 +00:00
_samples = new int[0];
return;
}
if (SampleFormat == SampleFormat.PcmInt16)
{
2018-12-01 20:01:59 +00:00
int samplesCount = (int)(wb.Size / (sizeof(short) * ChannelsCount));
2018-12-01 20:01:59 +00:00
_samples = new int[samplesCount * AudioConsts.HostChannelsCount];
if (ChannelsCount == 1)
{
2018-12-01 20:01:59 +00:00
for (int index = 0; index < samplesCount; index++)
{
2018-12-01 20:01:59 +00:00
short sample = memory.ReadInt16(wb.Position + index * 2);
2018-12-01 20:01:59 +00:00
_samples[index * 2 + 0] = sample;
_samples[index * 2 + 1] = sample;
}
}
else
{
2018-12-01 20:01:59 +00:00
for (int index = 0; index < samplesCount * 2; index++)
{
2018-12-01 20:01:59 +00:00
_samples[index] = memory.ReadInt16(wb.Position + index * 2);
}
}
}
else if (SampleFormat == SampleFormat.Adpcm)
{
2018-12-01 20:01:59 +00:00
byte[] buffer = memory.ReadBytes(wb.Position, wb.Size);
2018-12-01 20:01:59 +00:00
_samples = AdpcmDecoder.Decode(buffer, AdpcmCtx);
}
else
{
throw new InvalidOperationException();
}
if (SampleRate != AudioConsts.HostSampleRate)
{
//TODO: We should keep the frames being discarded (see the 4 below)
//on a buffer and include it on the next samples buffer, to allow
//the resampler to do seamless interpolation between wave buffers.
2018-12-01 20:01:59 +00:00
int samplesCount = _samples.Length / AudioConsts.HostChannelsCount;
2018-12-01 20:01:59 +00:00
samplesCount = Math.Max(samplesCount - 4, 0);
2018-12-01 20:01:59 +00:00
_samples = Resampler.Resample2Ch(
_samples,
SampleRate,
AudioConsts.HostSampleRate,
2018-12-01 20:01:59 +00:00
samplesCount,
ref _resamplerFracPart);
}
}
2018-12-01 20:01:59 +00:00
public void SetBufferIndex(int index)
{
2018-12-01 20:01:59 +00:00
_bufferIndex = index & 3;
2018-12-01 20:01:59 +00:00
_bufferReload = true;
}
}
}