A modded EditSaber for making beat saber maps.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
8.8 KiB

4 years ago
3 years ago
  1. // Fill out your copyright notice in the Description page of Project Settings.
  2. #include "RenderWaveform.h"
  3. // KISS Headers, that we need for the decompression part
  4. #include "ThirdParty/Kiss_FFT/kiss_fft129/kiss_fft.h"
  5. #include "ThirdParty/Kiss_FFT/kiss_fft129/tools/kiss_fftnd.h"
  6. bool bNormalizeOutputToDb = false;
  7. bool bShowLogDebug = false;
  8. bool bShowWarningDebug = false;
  9. bool bShowErrorDebug = false;
  10. // Log Category
  11. DECLARE_LOG_CATEGORY_EXTERN(LogRenderWave, Log, All);
  12. // Short Defines to faster debug
  13. #define PrintLog(TextToLog) if(bShowLogDebug) UE_LOG(LogRenderWave, Log, TextToLog)
  14. #define PrintWarning(TextToLog) if(bShowWarningDebug) UE_LOG(LogRenderWave, Warning, TextToLog)
  15. #define PrintError(TextToLog) if(bShowErrorDebug) UE_LOG(LogRenderWave, Error, TextToLog)
  16. #include "Sound/SoundWave.h"
  17. #include "AudioDevice.h"
  18. #include "Runtime/Engine/Public/VorbisAudioInfo.h"
  19. #include "Developer/TargetPlatform/Public/Interfaces/IAudioFormat.h"
  20. DEFINE_LOG_CATEGORY(LogRenderWave);
  21. //float GetFFTInValue(const int16 InSampleValue, const int16 InSampleIndex, const int16 InSampleCount)
  22. //{
  23. // float FFTValue = InSampleValue;
  24. //
  25. // // Apply the Hann window
  26. // FFTValue *= 0.5f * (1 - FMath::Cos(2 * PI * InSampleIndex / (InSampleCount - 1)));
  27. //
  28. // return FFTValue;
  29. //}
  30. void CalculateFrequencySpectrum(USoundWave* InSoundWaveRef, const float InStartTime, const float InDuration, TArray<float>& OutFrequencies)
  31. {
  32. // Clear the Array before continuing
  33. OutFrequencies.Empty();
  34. const int32 NumChannels = InSoundWaveRef->NumChannels;
  35. const int32 SampleRate = InSoundWaveRef->SampleRate;
  36. // Make sure the Number of Channels is correct
  37. if (NumChannels > 0 && NumChannels <= 2)
  38. {
  39. // Check if we actually have a Buffer to work with
  40. if (InSoundWaveRef->CachedRealtimeFirstBuffer)
  41. {
  42. // The first sample is just the StartTime * SampleRate
  43. int32 FirstSample = SampleRate * InStartTime;
  44. // The last sample is the SampleRate times (StartTime plus the Duration)
  45. int32 LastSample = SampleRate * (InStartTime + InDuration);
  46. // Get Maximum amount of samples in this Sound
  47. const int32 SampleCount = InSoundWaveRef->RawPCMDataSize / (2 * NumChannels);
  48. // An early check if we can create a Sample window
  49. FirstSample = FMath::Min(SampleCount, FirstSample);
  50. LastSample = FMath::Min(SampleCount, LastSample);
  51. // Actual amount of samples we gonna read
  52. int32 SamplesToRead = LastSample - FirstSample;
  53. if (SamplesToRead < 0) {
  54. PrintError(TEXT("Number of SamplesToRead is < 0!"));
  55. return;
  56. }
  57. // Shift the window enough so that we get a PowerOfTwo. FFT works better with that
  58. int32 PoT = 2;
  59. while (SamplesToRead > PoT) {
  60. PoT *= 2;
  61. }
  62. // Now we have a good PowerOfTwo to work with
  63. SamplesToRead = PoT;
  64. // Create two 2-dim Arrays for complex numbers | Buffer and Output
  65. kiss_fft_cpx* Buffer[2] = {0};
  66. kiss_fft_cpx* Output[2] = {0};
  67. // Create 1-dim Array with one slot for SamplesToRead
  68. int32 Dims[1] = {SamplesToRead};
  69. // alloc once and forget, should probably move to a init/deinit func
  70. static kiss_fftnd_cfg STF = kiss_fftnd_alloc(Dims, 1, 0, nullptr, nullptr);
  71. int16* SamplePtr = reinterpret_cast<int16*>(InSoundWaveRef->CachedRealtimeFirstBuffer);
  72. // Allocate space in the Buffer and Output Arrays for all the data that FFT returns
  73. for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ChannelIndex++)
  74. {
  75. Buffer[ChannelIndex] = (kiss_fft_cpx*)KISS_FFT_MALLOC(sizeof(kiss_fft_cpx) * SamplesToRead);
  76. Output[ChannelIndex] = (kiss_fft_cpx*)KISS_FFT_MALLOC(sizeof(kiss_fft_cpx) * SamplesToRead);
  77. }
  78. // Shift our SamplePointer to the Current "FirstSample"
  79. SamplePtr += FirstSample * NumChannels;
  80. float precomputeMultiplier = 2.f * PI / (SamplesToRead - 1);
  81. for (int32 SampleIndex = 0; SampleIndex < SamplesToRead; SampleIndex++)
  82. {
  83. float rMult = 0.f;
  84. if (SamplePtr != NULL && (SampleIndex + FirstSample < SampleCount))
  85. {
  86. rMult = 0.5f * (1.f - FMath::Cos(precomputeMultiplier * SampleIndex));
  87. }
  88. for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ChannelIndex++)
  89. {
  90. // Make sure the Point is Valid and we don't go out of bounds
  91. if (SamplePtr != NULL && (SampleIndex + FirstSample < SampleCount))
  92. {
  93. // Use Window function to get a better result for the Data (Hann Window)
  94. Buffer[ChannelIndex][SampleIndex].r = rMult * (*SamplePtr);
  95. }
  96. else
  97. {
  98. Buffer[ChannelIndex][SampleIndex].r = 0.f;
  99. }
  100. Buffer[ChannelIndex][SampleIndex].i = 0.f;
  101. // Take the next Sample
  102. SamplePtr++;
  103. }
  104. }
  105. // Now that the Buffer is filled, use the FFT
  106. for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ChannelIndex++)
  107. {
  108. if (Buffer[ChannelIndex])
  109. {
  110. kiss_fftnd(STF, Buffer[ChannelIndex], Output[ChannelIndex]);
  111. }
  112. }
  113. OutFrequencies.AddZeroed(SamplesToRead);
  114. for (int32 SampleIndex = 0; SampleIndex < SamplesToRead; ++SampleIndex)
  115. {
  116. float ChannelSum = 0.0f;
  117. for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ++ChannelIndex)
  118. {
  119. if (Output[ChannelIndex])
  120. {
  121. // With this we get the actual Frequency value for the frequencies from 0hz to ~22000hz
  122. ChannelSum += FMath::Sqrt(FMath::Square(Output[ChannelIndex][SampleIndex].r) + FMath::Square(Output[ChannelIndex][SampleIndex].i));
  123. }
  124. }
  125. if (bNormalizeOutputToDb)
  126. {
  127. OutFrequencies[SampleIndex] = FMath::LogX(10, ChannelSum / NumChannels) * 10;
  128. } else
  129. {
  130. OutFrequencies[SampleIndex] = ChannelSum / NumChannels;
  131. }
  132. }
  133. // Make sure to free up the FFT stuff
  134. // KISS_FFT_FREE(STF);
  135. for (int32 ChannelIndex = 0; ChannelIndex < NumChannels; ++ChannelIndex)
  136. {
  137. KISS_FFT_FREE(Buffer[ChannelIndex]);
  138. KISS_FFT_FREE(Output[ChannelIndex]);
  139. }
  140. } else {
  141. PrintError(TEXT("InSoundVisData.PCMData is a nullptr!"));
  142. }
  143. } else {
  144. PrintError(TEXT("Number of Channels is < 0!"));
  145. }
  146. }
  147. void URenderWaveform::BP_RenderWaveform(USoundWave* InSoundWaveRef, UProceduralMeshComponent* Mesh, float InSongPosition, int SizeX){
  148. if (!IsValid(InSoundWaveRef)){
  149. return;
  150. }
  151. if (!IsValid(Mesh)){
  152. return;
  153. }
  154. int nbVert = Mesh->GetProcMeshSection(0)->ProcVertexBuffer.Num();
  155. bool valid;
  156. TArray<FVector> Vertices;
  157. TArray<FVector> Normals;
  158. TArray<FVector2D> UV0;
  159. TArray<FLinearColor> VertexColors;
  160. TArray<FProcMeshTangent> Tangents;
  161. Vertices.AddDefaulted(nbVert);
  162. Normals.Init(FVector(0.0f, 0.0f, 1.0f), nbVert);
  163. UV0.AddDefaulted(nbVert);
  164. VertexColors.AddDefaulted(nbVert);
  165. Tangents.Init(FProcMeshTangent(1.0f, 0.0f, 0.0f), nbVert);
  166. for (size_t i = 0; i < 160; ++i){
  167. float duration = (1 / 64.f);
  168. float startTime = duration * i + InSongPosition;
  169. valid = true;
  170. if (startTime < 0.0f || startTime >= InSoundWaveRef->Duration || startTime + duration >= InSoundWaveRef->Duration) {
  171. valid = false;
  172. }
  173. TArray<float> results;
  174. if (valid) CalculateFrequencySpectrum(InSoundWaveRef, startTime, duration, results);
  175. for (size_t j = 0; j < 64; ++j){
  176. float height;
  177. if (valid && results.Num() > (j * 8.f)) height = results[j * 8.f] / 50000.f;
  178. else height = 0;
  179. Vertices[To1D(i, j, SizeX)] = FVector(i, j, height);
  180. VertexColors[To1D(i, j, SizeX)] = FLinearColor(height, 0.0f, 0.0f);
  181. }
  182. }
  183. Mesh->UpdateMeshSection_LinearColor(0, Vertices, Normals, UV0, VertexColors, Tangents);
  184. return;
  185. }
  186. void URenderWaveform::BP_GenerateSpectrogramMesh(UProceduralMeshComponent* Mesh, int SizeX, int SizeY)
  187. {
  188. if (!IsValid(Mesh) || SizeX <= 0 || SizeY <= 0) {
  189. return;
  190. }
  191. TArray<FVector> Vertices;
  192. TArray<int> Faces;
  193. TArray<FVector> Normals;
  194. TArray<FVector2D> UV0;
  195. TArray<FLinearColor> VertexColors;
  196. TArray<FProcMeshTangent> Tangents;
  197. Vertices.AddDefaulted(SizeX * SizeY);
  198. Normals.AddDefaulted(SizeX * SizeY);
  199. UV0.AddDefaulted(SizeX * SizeY);
  200. VertexColors.AddDefaulted(SizeX * SizeY);
  201. Tangents.AddDefaulted(SizeX * SizeY);
  202. Faces.AddZeroed((SizeX - 1) * (SizeY - 1) * 6);
  203. for (int j = 0; j < SizeY; ++j)
  204. {
  205. for (int i = 0; i < SizeX; ++i)
  206. {
  207. Vertices[To1D(i, j, SizeX)] = FVector(i,j, 0.0f);
  208. Normals[To1D(i, j, SizeX)] = FVector(0.0f, 0.0f, 1.0f);
  209. UV0[To1D(i, j, SizeX)] = FVector2D(0.0f, 0.0f);
  210. VertexColors[To1D(i, j, SizeX)] = FLinearColor(0.0f, 0.0f, 0.0f);
  211. Tangents[To1D(i, j, SizeX)] = FProcMeshTangent(1.0f, 0.0f, 0.0f);
  212. }
  213. }
  214. for (int j = 0; j < SizeY - 1; ++j)
  215. {
  216. for (int i = 0; i < SizeX - 1; ++i)
  217. {
  218. Faces[To1D(i, j, SizeX - 1) * 6] = To1D(i, j, SizeX);
  219. Faces[To1D(i, j, SizeX - 1) * 6 + 1] = To1D(i, j + 1, SizeX);
  220. Faces[To1D(i, j, SizeX - 1) * 6 + 2] = To1D(i + 1, j, SizeX);
  221. Faces[To1D(i, j, SizeX - 1) * 6 + 3] = To1D(i + 1, j, SizeX);
  222. Faces[To1D(i, j, SizeX - 1) * 6 + 4] = To1D(i, j + 1, SizeX);
  223. Faces[To1D(i, j, SizeX - 1) * 6 + 5] = To1D(i + 1, j + 1, SizeX);
  224. }
  225. }
  226. Mesh->CreateMeshSection_LinearColor(0, Vertices, Faces, Normals, UV0, VertexColors, Tangents, false);
  227. }
  228. int URenderWaveform::To1D(int x, int y, int sizeX)
  229. {
  230. return (sizeX * y) + x;
  231. }