WinMMSound.cpp 5.1 KB

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