WinMMSound.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. 
  2. // Use -lwinmm for linking to the winmm library in GCC/G++
  3. #include "../DFPSR/api/soundAPI.h"
  4. #include <windows.h>
  5. #include <mmsystem.h>
  6. #include "../DFPSR/implementation/math/scalar.h"
  7. #include "../DFPSR/base/simd.h"
  8. namespace dsr {
  9. static const int samplesPerChannel = 2048;
  10. static int bufferElements = 0;
  11. static Buffer outputBuffer, floatBuffer;
  12. static SafePointer<int16_t> outputData[2];
  13. static SafePointer<float> floatData;
  14. // Aligning memory to allow using the widest available floating-point SIMD vector.
  15. static const int soundBufferAlignment = DSR_FLOAT_ALIGNMENT;
  16. static void allocateBuffers(int neededElements) {
  17. int64_t roundedElements = roundUp(neededElements, soundBufferAlignment / 2);
  18. int64_t outputSize = roundedElements * sizeof(int16_t);
  19. outputBuffer = buffer_create(outputSize * 2);
  20. floatBuffer = buffer_create(roundedElements * sizeof(float), soundBufferAlignment);
  21. SafePointer<int16_t> allOutputData = buffer_getSafeData<int16_t>(outputBuffer, "Output data");
  22. outputData[0] = allOutputData.slice("Output data 0", 0, outputSize);
  23. outputData[1] = allOutputData.slice("Output data 1", outputSize, outputSize);
  24. floatData = buffer_getSafeData<float>(floatBuffer, "Output data");
  25. bufferElements = neededElements;
  26. }
  27. static HWAVEOUT waveOutput;
  28. static WAVEHDR header[2] = {0};
  29. static HANDLE bufferEndEvent = 0;
  30. static bool running = true;
  31. static void terminateSound() {
  32. printText("Terminating sound.\n");
  33. running = false;
  34. if (waveOutput) {
  35. waveOutReset(waveOutput);
  36. for (int b = 0; b < 2; b++) {
  37. waveOutUnprepareHeader(waveOutput, &header[b], sizeof(WAVEHDR));
  38. }
  39. waveOutClose(waveOutput);
  40. waveOutput = 0;
  41. }
  42. if (bufferEndEvent) {
  43. CloseHandle(bufferEndEvent);
  44. bufferEndEvent = 0;
  45. }
  46. }
  47. bool sound_streamToSpeakers(int32_t channels, int32_t sampleRate, std::function<bool(SafePointer<float> data, int32_t length)> soundOutput) {
  48. bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0);
  49. if (bufferEndEvent == 0) {
  50. terminateSound();
  51. sendWarning(U"Failed to create buffer end event!");
  52. return false;
  53. }
  54. int totalSamples = samplesPerChannel * channels;
  55. allocateBuffers(totalSamples);
  56. WAVEFORMATEX format;
  57. ZeroMemory(&format, sizeof(WAVEFORMATEX));
  58. format.nChannels = (WORD)channels;
  59. format.nSamplesPerSec = (DWORD)sampleRate;
  60. format.wFormatTag = WAVE_FORMAT_PCM;
  61. format.wBitsPerSample = 16;
  62. format.nBlockAlign = format.nChannels * sizeof(int16_t);
  63. format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
  64. format.cbSize = 0;
  65. if(waveOutOpen(&waveOutput, WAVE_MAPPER, &format, (DWORD_PTR)bufferEndEvent, (DWORD_PTR)NULL, CALLBACK_EVENT) != MMSYSERR_NOERROR) {
  66. terminateSound();
  67. sendWarning(U"Failed to open wave output!");
  68. return false;
  69. }
  70. if(waveOutSetVolume(waveOutput, 0xFFFFFFFF) != MMSYSERR_NOERROR) {
  71. terminateSound();
  72. sendWarning(U"Failed to set volume!");
  73. return false;
  74. }
  75. for (int b = 0; b < 2; b++) {
  76. ZeroMemory(&header[b], sizeof(WAVEHDR));
  77. header[b].dwBufferLength = totalSamples * sizeof(int16_t);
  78. header[b].lpData = (LPSTR)(outputData[b].getUnsafe());
  79. if (waveOutPrepareHeader(waveOutput, &header[b], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
  80. terminateSound();
  81. sendWarning(U"Failed to prepare buffer for streaming!");
  82. return false;
  83. }
  84. }
  85. running = true;
  86. while (running) {
  87. for (int b = 0; b < 2; b++) {
  88. if ((header[b].dwFlags & WHDR_INQUEUE) == 0) {
  89. // When one of the buffers is done playing, generate new sound and write more data to the output.
  90. safeMemorySet(floatData, 0, totalSamples * sizeof(float));
  91. running = soundOutput(floatData, samplesPerChannel);
  92. //for (int i = 0; i < totalSamples; i++) {
  93. // outputData[b][i] = sound_convertF32ToI16(floatBuffer[i]);
  94. //}
  95. SafePointer<int16_t> target = outputData[b];
  96. // Convert to target format so that the sound can be played
  97. for (uint32_t t = 0; t < totalSamples; t+=8) {
  98. // SIMD vectorized sound conversion with scaling and clamping to signed 16-bit integers.
  99. F32x4 lowerFloats = F32x4::readAligned(floatData + t, "sound_streamToSpeakers: Reading lower floats");
  100. F32x4 upperFloats = F32x4::readAligned(floatData + t + 4, "sound_streamToSpeakers: Reading upper floats");
  101. I32x4 lowerInts = truncateToI32(clamp(F32x4(-32768.0f), lowerFloats * 32767.0f, F32x4(32767.0f)));
  102. I32x4 upperInts = truncateToI32(clamp(F32x4(-32768.0f), upperFloats * 32767.0f, F32x4(32767.0f)));
  103. // TODO: Create I16x8 SIMD vectors for processing sound as 16-bit integers?
  104. // Or just move unzip into simd.h with a fallback solution and remove simdExtra.h.
  105. // Or just implement reading and writing of 16-bit signed integers using multiple SIMD registers or smaller memory regions.
  106. IVector4D lower = lowerInts.get();
  107. IVector4D upper = upperInts.get();
  108. target[t+0] = (int16_t)lower.x;
  109. target[t+1] = (int16_t)lower.y;
  110. target[t+2] = (int16_t)lower.z;
  111. target[t+3] = (int16_t)lower.w;
  112. target[t+4] = (int16_t)upper.x;
  113. target[t+5] = (int16_t)upper.y;
  114. target[t+6] = (int16_t)upper.z;
  115. target[t+7] = (int16_t)upper.w;
  116. }
  117. if (waveOutWrite(waveOutput, &header[b], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
  118. terminateSound();
  119. sendWarning(U"Failed to write wave output!");
  120. return false;
  121. }
  122. if (!running) { break; }
  123. }
  124. }
  125. WaitForSingleObject(bufferEndEvent, INFINITE);
  126. }
  127. terminateSound();
  128. return true;
  129. }
  130. }