DataSourceHelper.cs 19 KB

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