DataSourceHelper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. using Ryujinx.Audio.Common;
  2. using Ryujinx.Audio.Renderer.Common;
  3. using Ryujinx.Audio.Renderer.Dsp.State;
  4. using Ryujinx.Common.Logging;
  5. using Ryujinx.Memory;
  6. using System;
  7. using System.Diagnostics;
  8. using System.Runtime.CompilerServices;
  9. using System.Runtime.InteropServices;
  10. using System.Runtime.Intrinsics;
  11. using System.Runtime.Intrinsics.Arm;
  12. using System.Runtime.Intrinsics.X86;
  13. using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter;
  14. namespace Ryujinx.Audio.Renderer.Dsp
  15. {
  16. public static class DataSourceHelper
  17. {
  18. private const int FixedPointPrecision = 15;
  19. public struct WaveBufferInformation
  20. {
  21. public uint SourceSampleRate;
  22. public float Pitch;
  23. public ulong ExtraParameter;
  24. public ulong ExtraParameterSize;
  25. public int ChannelIndex;
  26. public int ChannelCount;
  27. public DecodingBehaviour DecodingBehaviour;
  28. public SampleRateConversionQuality SrcQuality;
  29. public SampleFormat SampleFormat;
  30. }
  31. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  32. private static int GetPitchLimitBySrcQuality(SampleRateConversionQuality quality)
  33. {
  34. return quality switch
  35. {
  36. SampleRateConversionQuality.Default or SampleRateConversionQuality.Low => 4,
  37. SampleRateConversionQuality.High => 8,
  38. _ => throw new ArgumentException(quality.ToString()),
  39. };
  40. }
  41. public static void ProcessWaveBuffers(IVirtualMemoryManager memoryManager, Span<float> outputBuffer, ref WaveBufferInformation info, Span<WaveBuffer> wavebuffers, ref VoiceUpdateState voiceState, uint targetSampleRate, int sampleCount)
  42. {
  43. const int tempBufferSize = 0x3F00;
  44. Span<short> tempBuffer = stackalloc short[tempBufferSize];
  45. float sampleRateRatio = (float)info.SourceSampleRate / targetSampleRate * info.Pitch;
  46. float fraction = voiceState.Fraction;
  47. int waveBufferIndex = (int)voiceState.WaveBufferIndex;
  48. ulong playedSampleCount = voiceState.PlayedSampleCount;
  49. int offset = voiceState.Offset;
  50. uint waveBufferConsumed = voiceState.WaveBufferConsumed;
  51. int pitchMaxLength = GetPitchLimitBySrcQuality(info.SrcQuality);
  52. int totalNeededSize = (int)MathF.Truncate(fraction + sampleRateRatio * sampleCount);
  53. if (totalNeededSize + pitchMaxLength <= tempBufferSize && totalNeededSize >= 0)
  54. {
  55. int sourceSampleCountToProcess = sampleCount;
  56. int maxSampleCountPerIteration = Math.Min((int)MathF.Truncate((tempBufferSize - fraction) / sampleRateRatio), sampleCount);
  57. bool isStarving = false;
  58. int i = 0;
  59. while (i < sourceSampleCountToProcess)
  60. {
  61. int tempBufferIndex = 0;
  62. if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
  63. {
  64. voiceState.Pitch.AsSpan().Slice(0, pitchMaxLength).CopyTo(tempBuffer);
  65. tempBufferIndex += pitchMaxLength;
  66. }
  67. int sampleCountToProcess = Math.Min(sourceSampleCountToProcess, maxSampleCountPerIteration);
  68. int y = 0;
  69. int sampleCountToDecode = (int)MathF.Truncate(fraction + sampleRateRatio * sampleCountToProcess);
  70. while (y < sampleCountToDecode)
  71. {
  72. if (waveBufferIndex >= Constants.VoiceWaveBufferCount)
  73. {
  74. waveBufferIndex = 0;
  75. playedSampleCount = 0;
  76. }
  77. if (!voiceState.IsWaveBufferValid[waveBufferIndex])
  78. {
  79. isStarving = true;
  80. break;
  81. }
  82. ref WaveBuffer waveBuffer = ref wavebuffers[waveBufferIndex];
  83. if (offset == 0 && info.SampleFormat == SampleFormat.Adpcm && waveBuffer.Context != 0)
  84. {
  85. voiceState.LoopContext = memoryManager.Read<AdpcmLoopContext>(waveBuffer.Context);
  86. }
  87. Span<short> tempSpan = tempBuffer.Slice(tempBufferIndex + y);
  88. int decodedSampleCount = -1;
  89. int targetSampleStartOffset;
  90. int targetSampleEndOffset;
  91. if (voiceState.LoopCount > 0 && waveBuffer.LoopStartSampleOffset != 0 && waveBuffer.LoopEndSampleOffset != 0 && waveBuffer.LoopStartSampleOffset <= waveBuffer.LoopEndSampleOffset)
  92. {
  93. targetSampleStartOffset = (int)waveBuffer.LoopStartSampleOffset;
  94. targetSampleEndOffset = (int)waveBuffer.LoopEndSampleOffset;
  95. }
  96. else
  97. {
  98. targetSampleStartOffset = (int)waveBuffer.StartSampleOffset;
  99. targetSampleEndOffset = (int)waveBuffer.EndSampleOffset;
  100. }
  101. int targetWaveBufferSampleCount = targetSampleEndOffset - targetSampleStartOffset;
  102. switch (info.SampleFormat)
  103. {
  104. case SampleFormat.Adpcm:
  105. ReadOnlySpan<byte> waveBufferAdpcm = ReadOnlySpan<byte>.Empty;
  106. if (waveBuffer.Buffer != 0 && waveBuffer.BufferSize != 0)
  107. {
  108. // TODO: we are possibly copying a lot of unneeded data here, we should only take what we need.
  109. waveBufferAdpcm = memoryManager.GetSpan(waveBuffer.Buffer, (int)waveBuffer.BufferSize);
  110. }
  111. ReadOnlySpan<short> coefficients = MemoryMarshal.Cast<byte, short>(memoryManager.GetSpan(info.ExtraParameter, (int)info.ExtraParameterSize));
  112. decodedSampleCount = AdpcmHelper.Decode(tempSpan, waveBufferAdpcm, targetSampleStartOffset, targetSampleEndOffset, offset, sampleCountToDecode - y, coefficients, ref voiceState.LoopContext);
  113. break;
  114. case SampleFormat.PcmInt16:
  115. ReadOnlySpan<short> waveBufferPcm16 = ReadOnlySpan<short>.Empty;
  116. if (waveBuffer.Buffer != 0 && waveBuffer.BufferSize != 0)
  117. {
  118. ulong bufferOffset = waveBuffer.Buffer + PcmHelper.GetBufferOffset<short>(targetSampleStartOffset, offset, info.ChannelCount);
  119. int bufferSize = PcmHelper.GetBufferSize<short>(targetSampleStartOffset, targetSampleEndOffset, offset, sampleCountToDecode - y) * info.ChannelCount;
  120. waveBufferPcm16 = MemoryMarshal.Cast<byte, short>(memoryManager.GetSpan(bufferOffset, bufferSize));
  121. }
  122. decodedSampleCount = PcmHelper.Decode(tempSpan, waveBufferPcm16, targetSampleStartOffset, targetSampleEndOffset, info.ChannelIndex, info.ChannelCount);
  123. break;
  124. case SampleFormat.PcmFloat:
  125. ReadOnlySpan<float> waveBufferPcmFloat = ReadOnlySpan<float>.Empty;
  126. if (waveBuffer.Buffer != 0 && waveBuffer.BufferSize != 0)
  127. {
  128. ulong bufferOffset = waveBuffer.Buffer + PcmHelper.GetBufferOffset<float>(targetSampleStartOffset, offset, info.ChannelCount);
  129. int bufferSize = PcmHelper.GetBufferSize<float>(targetSampleStartOffset, targetSampleEndOffset, offset, sampleCountToDecode - y) * info.ChannelCount;
  130. waveBufferPcmFloat = MemoryMarshal.Cast<byte, float>(memoryManager.GetSpan(bufferOffset, bufferSize));
  131. }
  132. decodedSampleCount = PcmHelper.Decode(tempSpan, waveBufferPcmFloat, targetSampleStartOffset, targetSampleEndOffset, info.ChannelIndex, info.ChannelCount);
  133. break;
  134. default:
  135. Logger.Error?.Print(LogClass.AudioRenderer, $"Unsupported sample format " + info.SampleFormat);
  136. break;
  137. }
  138. Debug.Assert(decodedSampleCount <= sampleCountToDecode);
  139. if (decodedSampleCount < 0)
  140. {
  141. Logger.Warning?.Print(LogClass.AudioRenderer, "Decoding failed, skipping WaveBuffer");
  142. voiceState.MarkEndOfBufferWaveBufferProcessing(ref waveBuffer, ref waveBufferIndex, ref waveBufferConsumed, ref playedSampleCount);
  143. decodedSampleCount = 0;
  144. }
  145. y += decodedSampleCount;
  146. offset += decodedSampleCount;
  147. playedSampleCount += (uint)decodedSampleCount;
  148. if (offset >= targetWaveBufferSampleCount || decodedSampleCount == 0)
  149. {
  150. offset = 0;
  151. if (waveBuffer.Looping)
  152. {
  153. voiceState.LoopCount++;
  154. if (waveBuffer.LoopCount >= 0)
  155. {
  156. if (decodedSampleCount == 0 || voiceState.LoopCount > waveBuffer.LoopCount)
  157. {
  158. voiceState.MarkEndOfBufferWaveBufferProcessing(ref waveBuffer, ref waveBufferIndex, ref waveBufferConsumed, ref playedSampleCount);
  159. }
  160. }
  161. if (decodedSampleCount == 0)
  162. {
  163. isStarving = true;
  164. break;
  165. }
  166. if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.PlayedSampleCountResetWhenLooping))
  167. {
  168. playedSampleCount = 0;
  169. }
  170. }
  171. else
  172. {
  173. voiceState.MarkEndOfBufferWaveBufferProcessing(ref waveBuffer, ref waveBufferIndex, ref waveBufferConsumed, ref playedSampleCount);
  174. }
  175. }
  176. }
  177. Span<int> outputSpanInt = MemoryMarshal.Cast<float, int>(outputBuffer.Slice(i));
  178. if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion))
  179. {
  180. for (int j = 0; j < y; j++)
  181. {
  182. outputBuffer[j] = tempBuffer[j];
  183. }
  184. }
  185. else
  186. {
  187. Span<short> tempSpan = tempBuffer.Slice(tempBufferIndex + y);
  188. tempSpan.Slice(0, sampleCountToDecode - y).Fill(0);
  189. ToFloat(outputBuffer, outputSpanInt, sampleCountToProcess);
  190. ResamplerHelper.Resample(outputBuffer, tempBuffer, sampleRateRatio, ref fraction, sampleCountToProcess, info.SrcQuality, y != sourceSampleCountToProcess || info.Pitch != 1.0f);
  191. tempBuffer.Slice(sampleCountToDecode, pitchMaxLength).CopyTo(voiceState.Pitch.AsSpan());
  192. }
  193. i += sampleCountToProcess;
  194. }
  195. Debug.Assert(sourceSampleCountToProcess == i || !isStarving);
  196. voiceState.WaveBufferConsumed = waveBufferConsumed;
  197. voiceState.Offset = offset;
  198. voiceState.PlayedSampleCount = playedSampleCount;
  199. voiceState.WaveBufferIndex = (uint)waveBufferIndex;
  200. voiceState.Fraction = fraction;
  201. }
  202. }
  203. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  204. private static void ToFloatAvx(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  205. {
  206. ReadOnlySpan<Vector256<int>> inputVec = MemoryMarshal.Cast<int, Vector256<int>>(input);
  207. Span<Vector256<float>> outputVec = MemoryMarshal.Cast<float, Vector256<float>>(output);
  208. int sisdStart = inputVec.Length * 8;
  209. for (int i = 0; i < inputVec.Length; i++)
  210. {
  211. outputVec[i] = Avx.ConvertToVector256Single(inputVec[i]);
  212. }
  213. for (int i = sisdStart; i < sampleCount; i++)
  214. {
  215. output[i] = input[i];
  216. }
  217. }
  218. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  219. private static void ToFloatSse2(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  220. {
  221. ReadOnlySpan<Vector128<int>> inputVec = MemoryMarshal.Cast<int, Vector128<int>>(input);
  222. Span<Vector128<float>> outputVec = MemoryMarshal.Cast<float, Vector128<float>>(output);
  223. int sisdStart = inputVec.Length * 4;
  224. for (int i = 0; i < inputVec.Length; i++)
  225. {
  226. outputVec[i] = Sse2.ConvertToVector128Single(inputVec[i]);
  227. }
  228. for (int i = sisdStart; i < sampleCount; i++)
  229. {
  230. output[i] = input[i];
  231. }
  232. }
  233. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  234. private static void ToFloatAdvSimd(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  235. {
  236. ReadOnlySpan<Vector128<int>> inputVec = MemoryMarshal.Cast<int, Vector128<int>>(input);
  237. Span<Vector128<float>> outputVec = MemoryMarshal.Cast<float, Vector128<float>>(output);
  238. int sisdStart = inputVec.Length * 4;
  239. for (int i = 0; i < inputVec.Length; i++)
  240. {
  241. outputVec[i] = AdvSimd.ConvertToSingle(inputVec[i]);
  242. }
  243. for (int i = sisdStart; i < sampleCount; i++)
  244. {
  245. output[i] = input[i];
  246. }
  247. }
  248. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  249. public static void ToFloatSlow(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  250. {
  251. for (int i = 0; i < sampleCount; i++)
  252. {
  253. output[i] = input[i];
  254. }
  255. }
  256. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  257. public static void ToFloat(Span<float> output, ReadOnlySpan<int> input, int sampleCount)
  258. {
  259. if (Avx.IsSupported)
  260. {
  261. ToFloatAvx(output, input, sampleCount);
  262. }
  263. else if (Sse2.IsSupported)
  264. {
  265. ToFloatSse2(output, input, sampleCount);
  266. }
  267. else if (AdvSimd.IsSupported)
  268. {
  269. ToFloatAdvSimd(output, input, sampleCount);
  270. }
  271. else
  272. {
  273. ToFloatSlow(output, input, sampleCount);
  274. }
  275. }
  276. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  277. public static void ToIntAvx(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  278. {
  279. ReadOnlySpan<Vector256<float>> inputVec = MemoryMarshal.Cast<float, Vector256<float>>(input);
  280. Span<Vector256<int>> outputVec = MemoryMarshal.Cast<int, Vector256<int>>(output);
  281. int sisdStart = inputVec.Length * 8;
  282. for (int i = 0; i < inputVec.Length; i++)
  283. {
  284. outputVec[i] = Avx.ConvertToVector256Int32(inputVec[i]);
  285. }
  286. for (int i = sisdStart; i < sampleCount; i++)
  287. {
  288. output[i] = (int)input[i];
  289. }
  290. }
  291. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  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] = Sse2.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 ToIntAdvSimd(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  308. {
  309. ReadOnlySpan<Vector128<float>> inputVec = MemoryMarshal.Cast<float, Vector128<float>>(input);
  310. Span<Vector128<int>> outputVec = MemoryMarshal.Cast<int, Vector128<int>>(output);
  311. int sisdStart = inputVec.Length * 4;
  312. for (int i = 0; i < inputVec.Length; i++)
  313. {
  314. outputVec[i] = AdvSimd.ConvertToInt32RoundToZero(inputVec[i]);
  315. }
  316. for (int i = sisdStart; i < sampleCount; i++)
  317. {
  318. output[i] = (int)input[i];
  319. }
  320. }
  321. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  322. public static void ToIntSlow(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  323. {
  324. for (int i = 0; i < sampleCount; i++)
  325. {
  326. output[i] = (int)input[i];
  327. }
  328. }
  329. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  330. public static void ToInt(Span<int> output, ReadOnlySpan<float> input, int sampleCount)
  331. {
  332. if (Avx.IsSupported)
  333. {
  334. ToIntAvx(output, input, sampleCount);
  335. }
  336. else if (Sse2.IsSupported)
  337. {
  338. ToIntSse2(output, input, sampleCount);
  339. }
  340. else if (AdvSimd.IsSupported)
  341. {
  342. ToIntAdvSimd(output, input, sampleCount);
  343. }
  344. else
  345. {
  346. ToIntSlow(output, input, sampleCount);
  347. }
  348. }
  349. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  350. public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span<ushort> bufferIndices)
  351. {
  352. if (!isSupported && bufferIndices.Length == 6)
  353. {
  354. ushort backLeft = bufferIndices[2];
  355. ushort backRight = bufferIndices[3];
  356. ushort frontCenter = bufferIndices[4];
  357. ushort lowFrequency = bufferIndices[5];
  358. bufferIndices[2] = frontCenter;
  359. bufferIndices[3] = lowFrequency;
  360. bufferIndices[4] = backLeft;
  361. bufferIndices[5] = backRight;
  362. }
  363. }
  364. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  365. public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span<ushort> bufferIndices)
  366. {
  367. if (isSupported && bufferIndices.Length == 6)
  368. {
  369. ushort frontCenter = bufferIndices[2];
  370. ushort lowFrequency = bufferIndices[3];
  371. ushort backLeft = bufferIndices[4];
  372. ushort backRight = bufferIndices[5];
  373. bufferIndices[2] = backLeft;
  374. bufferIndices[3] = backRight;
  375. bufferIndices[4] = frontCenter;
  376. bufferIndices[5] = lowFrequency;
  377. }
  378. }
  379. }
  380. }