DataSourceHelper.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. //
  2. // Copyright (c) 2019-2020 Ryujinx
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. //
  17. using Ryujinx.Audio.Renderer.Common;
  18. using Ryujinx.Audio.Renderer.Dsp.State;
  19. using Ryujinx.Common.Logging;
  20. using Ryujinx.Cpu;
  21. using System;
  22. using System.Buffers;
  23. using System.Diagnostics;
  24. using System.Runtime.CompilerServices;
  25. using System.Runtime.InteropServices;
  26. using System.Runtime.Intrinsics;
  27. using System.Runtime.Intrinsics.X86;
  28. using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
  29. namespace Ryujinx.Audio.Renderer.Dsp
  30. {
  31. public static class DataSourceHelper
  32. {
  33. private const int FixedPointPrecision = 15;
  34. public class WaveBufferInformation
  35. {
  36. public Memory<VoiceUpdateState> State;
  37. public uint SourceSampleRate;
  38. public SampleFormat SampleFormat;
  39. public float Pitch;
  40. public DecodingBehaviour DecodingBehaviour;
  41. public WaveBuffer[] WaveBuffers;
  42. public ulong ExtraParameter;
  43. public ulong ExtraParameterSize;
  44. public int ChannelIndex;
  45. public int ChannelCount;
  46. public SampleRateConversionQuality SrcQuality;
  47. }
  48. private static int GetPitchLimitBySrcQuality(SampleRateConversionQuality quality)
  49. {
  50. switch (quality)
  51. {
  52. case SampleRateConversionQuality.Default:
  53. case SampleRateConversionQuality.Low:
  54. return 4;
  55. case SampleRateConversionQuality.High:
  56. return 8;
  57. default:
  58. throw new ArgumentException($"{quality}");
  59. }
  60. }
  61. public static void ProcessWaveBuffers(MemoryManager memoryManager, Span<float> outputBuffer, WaveBufferInformation info, uint targetSampleRate, int sampleCount)
  62. {
  63. const int tempBufferSize = 0x3F00;
  64. ref VoiceUpdateState state = ref info.State.Span[0];
  65. short[] tempBuffer = ArrayPool<short>.Shared.Rent(tempBufferSize);
  66. float sampleRateRatio = ((float)info.SourceSampleRate / targetSampleRate * info.Pitch);
  67. float fraction = state.Fraction;
  68. int waveBufferIndex = (int)state.WaveBufferIndex;
  69. ulong playedSampleCount = state.PlayedSampleCount;
  70. int offset = state.Offset;
  71. uint waveBufferConsumed = state.WaveBufferConsumed;
  72. int pitchMaxLength = GetPitchLimitBySrcQuality(info.SrcQuality);
  73. int totalNeededSize = (int)MathF.Truncate(fraction + sampleRateRatio * sampleCount);
  74. if (totalNeededSize + pitchMaxLength <= tempBufferSize && totalNeededSize >= 0)
  75. {
  76. int sourceSampleCountToProcess = sampleCount;
  77. int maxSampleCountPerIteration = Math.Min((int)MathF.Truncate((tempBufferSize - fraction) / sampleRateRatio), sampleCount);
  78. bool isStarving = false;
  79. int i = 0;
  80. while (i < sourceSampleCountToProcess)
  81. {
  82. int tempBufferIndex = 0;
  83. if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
  84. {
  85. state.Pitch.ToSpan().Slice(0, pitchMaxLength).CopyTo(tempBuffer.AsSpan());
  86. tempBufferIndex += pitchMaxLength;
  87. }
  88. int sampleCountToProcess = Math.Min(sourceSampleCountToProcess, maxSampleCountPerIteration);
  89. int y = 0;
  90. int sampleCountToDecode = (int)MathF.Truncate(fraction + sampleRateRatio * sampleCountToProcess);
  91. while (y < sampleCountToDecode)
  92. {
  93. if (waveBufferIndex >= RendererConstants.VoiceWaveBufferCount)
  94. {
  95. Logger.Error?.Print(LogClass.AudioRenderer, $"Invalid WaveBuffer index {waveBufferIndex}");
  96. waveBufferIndex = 0;
  97. playedSampleCount = 0;
  98. }
  99. if (!state.IsWaveBufferValid[waveBufferIndex])
  100. {
  101. isStarving = true;
  102. break;
  103. }
  104. ref WaveBuffer waveBuffer = ref info.WaveBuffers[waveBufferIndex];
  105. if (offset == 0 && info.SampleFormat == SampleFormat.Adpcm && waveBuffer.Context != 0)
  106. {
  107. state.LoopContext = memoryManager.Read<AdpcmLoopContext>(waveBuffer.Context);
  108. }
  109. Span<short> tempSpan = tempBuffer.AsSpan().Slice(tempBufferIndex + y);
  110. int decodedSampleCount = -1;
  111. int targetSampleStartOffset;
  112. int targetSampleEndOffset;
  113. if (state.LoopCount > 0 && waveBuffer.LoopStartSampleOffset != 0 && waveBuffer.LoopEndSampleOffset != 0 && waveBuffer.LoopStartSampleOffset <= waveBuffer.LoopEndSampleOffset)
  114. {
  115. targetSampleStartOffset = (int)waveBuffer.LoopStartSampleOffset;
  116. targetSampleEndOffset = (int)waveBuffer.LoopEndSampleOffset;
  117. }
  118. else
  119. {
  120. targetSampleStartOffset = (int)waveBuffer.StartSampleOffset;
  121. targetSampleEndOffset = (int)waveBuffer.EndSampleOffset;
  122. }
  123. int targetWaveBufferSampleCount = targetSampleEndOffset - targetSampleStartOffset;
  124. switch (info.SampleFormat)
  125. {
  126. case SampleFormat.Adpcm:
  127. ReadOnlySpan<byte> waveBufferAdpcm = ReadOnlySpan<byte>.Empty;
  128. if (waveBuffer.Buffer != 0 && waveBuffer.BufferSize != 0)
  129. {
  130. // TODO: we are possibly copying a lot of unneeded data here, we should only take what we need.
  131. waveBufferAdpcm = memoryManager.GetSpan(waveBuffer.Buffer, (int)waveBuffer.BufferSize);
  132. }
  133. ReadOnlySpan<short> coefficients = MemoryMarshal.Cast<byte, short>(memoryManager.GetSpan(info.ExtraParameter, (int)info.ExtraParameterSize));
  134. decodedSampleCount = AdpcmHelper.Decode(tempSpan, waveBufferAdpcm, targetSampleStartOffset, targetSampleEndOffset, offset, sampleCountToDecode - y, coefficients, ref state.LoopContext);
  135. break;
  136. case SampleFormat.PcmInt16:
  137. ReadOnlySpan<short> waveBufferPcm16 = ReadOnlySpan<short>.Empty;
  138. if (waveBuffer.Buffer != 0 && waveBuffer.BufferSize != 0)
  139. {
  140. ulong bufferOffset = waveBuffer.Buffer + PcmHelper.GetBufferOffset<short>(targetSampleStartOffset, offset, info.ChannelCount);
  141. int bufferSize = PcmHelper.GetBufferSize<short>(targetSampleStartOffset, targetSampleEndOffset, offset, sampleCountToDecode - y) * info.ChannelCount;
  142. waveBufferPcm16 = MemoryMarshal.Cast<byte, short>(memoryManager.GetSpan(bufferOffset, bufferSize));
  143. }
  144. decodedSampleCount = PcmHelper.Decode(tempSpan, waveBufferPcm16, targetSampleStartOffset, targetSampleEndOffset, info.ChannelIndex, info.ChannelCount);
  145. break;
  146. case SampleFormat.PcmFloat:
  147. ReadOnlySpan<float> waveBufferPcmFloat = ReadOnlySpan<float>.Empty;
  148. if (waveBuffer.Buffer != 0 && waveBuffer.BufferSize != 0)
  149. {
  150. ulong bufferOffset = waveBuffer.Buffer + PcmHelper.GetBufferOffset<float>(targetSampleStartOffset, offset, info.ChannelCount);
  151. int bufferSize = PcmHelper.GetBufferSize<float>(targetSampleStartOffset, targetSampleEndOffset, offset, sampleCountToDecode - y) * info.ChannelCount;
  152. waveBufferPcmFloat = MemoryMarshal.Cast<byte, float>(memoryManager.GetSpan(bufferOffset, bufferSize));
  153. }
  154. decodedSampleCount = PcmHelper.Decode(tempSpan, waveBufferPcmFloat, targetSampleStartOffset, targetSampleEndOffset, info.ChannelIndex, info.ChannelCount);
  155. break;
  156. default:
  157. Logger.Warning?.Print(LogClass.AudioRenderer, $"Unsupported sample format {info.SampleFormat}");
  158. break;
  159. }
  160. Debug.Assert(decodedSampleCount <= sampleCountToDecode);
  161. if (decodedSampleCount < 0)
  162. {
  163. Logger.Warning?.Print(LogClass.AudioRenderer, $"Decoding failed, skipping WaveBuffer");
  164. state.MarkEndOfBufferWaveBufferProcessing(ref waveBuffer, ref waveBufferIndex, ref waveBufferConsumed, ref playedSampleCount);
  165. decodedSampleCount = 0;
  166. }
  167. y += decodedSampleCount;
  168. offset += decodedSampleCount;
  169. playedSampleCount += (uint)decodedSampleCount;
  170. if (offset >= targetWaveBufferSampleCount || decodedSampleCount == 0)
  171. {
  172. offset = 0;
  173. if (waveBuffer.Looping)
  174. {
  175. state.LoopCount++;
  176. if (waveBuffer.LoopCount >= 0)
  177. {
  178. if (decodedSampleCount == 0 || state.LoopCount > waveBuffer.LoopCount)
  179. {
  180. state.MarkEndOfBufferWaveBufferProcessing(ref waveBuffer, ref waveBufferIndex, ref waveBufferConsumed, ref playedSampleCount);
  181. }
  182. }
  183. if (decodedSampleCount == 0)
  184. {
  185. isStarving = true;
  186. break;
  187. }
  188. if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.PlayedSampleCountResetWhenLooping))
  189. {
  190. playedSampleCount = 0;
  191. }
  192. }
  193. else
  194. {
  195. state.MarkEndOfBufferWaveBufferProcessing(ref waveBuffer, ref waveBufferIndex, ref waveBufferConsumed, ref playedSampleCount);
  196. }
  197. }
  198. }
  199. Span<float> outputSpan = outputBuffer.Slice(i);
  200. Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputSpan);
  201. if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
  202. {
  203. for (int j = 0; j < y; j++)
  204. {
  205. outputBuffer[j] = tempBuffer[j];
  206. }
  207. }
  208. else
  209. {
  210. Span<short> tempSpan = tempBuffer.AsSpan().Slice(tempBufferIndex + y);
  211. tempSpan.Slice(0, sampleCountToDecode - y).Fill(0);
  212. ToFloat(outputBuffer, outputSpanInt, sampleCountToProcess);
  213. ResamplerHelper.Resample(outputBuffer, tempBuffer, sampleRateRatio, ref fraction, sampleCountToProcess, info.SrcQuality, y != sourceSampleCountToProcess || info.Pitch != 1.0f);
  214. tempBuffer.AsSpan().Slice(sampleCountToDecode, pitchMaxLength).CopyTo(state.Pitch.ToSpan());
  215. }
  216. i += sampleCountToProcess;
  217. }
  218. Debug.Assert(sourceSampleCountToProcess == i || !isStarving);
  219. state.WaveBufferConsumed = waveBufferConsumed;
  220. state.Offset = offset;
  221. state.PlayedSampleCount = playedSampleCount;
  222. state.WaveBufferIndex = (uint)waveBufferIndex;
  223. state.Fraction = fraction;
  224. }
  225. ArrayPool<short>.Shared.Return(tempBuffer);
  226. }
  227. private static void ToFloatAvx(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  228. {
  229. ReadOnlySpan<Vector256<int>> inputVec = MemoryMarshal.Cast<int, Vector256<int>>(input);
  230. Span<Vector256<float>> outputVec = MemoryMarshal.Cast<float, Vector256<float>>(output);
  231. int sisdStart = inputVec.Length * 8;
  232. for (int i = 0; i < inputVec.Length; i++)
  233. {
  234. outputVec[i] = Avx.ConvertToVector256Single(inputVec[i]);
  235. }
  236. for (int i = sisdStart; i < sampleCount; i++)
  237. {
  238. output[i] = input[i];
  239. }
  240. }
  241. private static void ToFloatSse2(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  242. {
  243. ReadOnlySpan<Vector128<int>> inputVec = MemoryMarshal.Cast<int, Vector128<int>>(input);
  244. Span<Vector128<float>> outputVec = MemoryMarshal.Cast<float, Vector128<float>>(output);
  245. int sisdStart = inputVec.Length * 4;
  246. for (int i = 0; i < inputVec.Length; i++)
  247. {
  248. outputVec[i] = Sse2.ConvertToVector128Single(inputVec[i]);
  249. }
  250. for (int i = sisdStart; i < sampleCount; i++)
  251. {
  252. output[i] = input[i];
  253. }
  254. }
  255. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  256. public static void ToFloatSlow(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  257. {
  258. for (int i = 0; i < sampleCount; i++)
  259. {
  260. output[i] = input[i];
  261. }
  262. }
  263. public static void ToFloat(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  264. {
  265. if (Avx.IsSupported)
  266. {
  267. ToFloatAvx(output, input, sampleCount);
  268. }
  269. else if (Sse2.IsSupported)
  270. {
  271. ToFloatSse2(output, input, sampleCount);
  272. }
  273. else
  274. {
  275. ToFloatSlow(output, input, sampleCount);
  276. }
  277. }
  278. public static void ToIntAvx(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  279. {
  280. ReadOnlySpan<Vector256<float>> inputVec = MemoryMarshal.Cast<float, Vector256<float>>(input);
  281. Span<Vector256<int>> outputVec = MemoryMarshal.Cast<int, Vector256<int>>(output);
  282. int sisdStart = inputVec.Length * 8;
  283. for (int i = 0; i < inputVec.Length; i++)
  284. {
  285. outputVec[i] = Avx.ConvertToVector256Int32(inputVec[i]);
  286. }
  287. for (int i = sisdStart; i < sampleCount; i++)
  288. {
  289. output[i] = (int)input[i];
  290. }
  291. }
  292. public static void ToIntSse2(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  293. {
  294. ReadOnlySpan<Vector128<float>> inputVec = MemoryMarshal.Cast<float, Vector128<float>>(input);
  295. Span<Vector128<int>> outputVec = MemoryMarshal.Cast<int, Vector128<int>>(output);
  296. int sisdStart = inputVec.Length * 4;
  297. for (int i = 0; i < inputVec.Length; i++)
  298. {
  299. outputVec[i] = Avx.ConvertToVector128Int32(inputVec[i]);
  300. }
  301. for (int i = sisdStart; i < sampleCount; i++)
  302. {
  303. output[i] = (int)input[i];
  304. }
  305. }
  306. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  307. public static void ToIntSlow(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  308. {
  309. for (int i = 0; i < sampleCount; i++)
  310. {
  311. output[i] = (int)input[i];
  312. }
  313. }
  314. public static void ToInt(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  315. {
  316. if (Avx.IsSupported)
  317. {
  318. ToIntAvx(output, input, sampleCount);
  319. }
  320. else if (Sse2.IsSupported)
  321. {
  322. ToIntSse2(output, input, sampleCount);
  323. }
  324. else
  325. {
  326. ToIntSlow(output, input, sampleCount);
  327. }
  328. }
  329. }
  330. }