WinMMSound.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. 
  2. // Use -lwinmm for linking to the winmm library in GCC/G++
  3. #include "../DFPSR/includeFramework.h"
  4. #include "soundManagers.h"
  5. #include <windows.h>
  6. #include <mmsystem.h>
  7. using namespace dsr;
  8. static const int samplesPerChannel = 2048;
  9. static int bufferElements = 0;
  10. static int16_t *outputBuffer[2] = {nullptr, nullptr};
  11. static float *floatBuffer = nullptr;
  12. static void allocateBuffers(int neededElements) {
  13. // TODO: Use aligned memory with Buffer
  14. outputBuffer[0] = (int16_t *)calloc(roundUp(neededElements, 8), sizeof(int16_t));
  15. outputBuffer[1] = (int16_t *)calloc(roundUp(neededElements, 8), sizeof(int16_t));
  16. floatBuffer = (float *)calloc(roundUp(neededElements, 8), sizeof(float));
  17. bufferElements = neededElements;
  18. }
  19. static HWAVEOUT waveOutput;
  20. static WAVEHDR header[2] = {0};
  21. static HANDLE bufferEndEvent = 0;
  22. static bool running = true;
  23. static void terminateSound() {
  24. printText("Terminating sound.\n");
  25. running = false;
  26. if (waveOutput) {
  27. waveOutReset(waveOutput);
  28. for (int b = 0; b < 2; b++) {
  29. waveOutUnprepareHeader(waveOutput, &header[b], sizeof(WAVEHDR));
  30. }
  31. waveOutClose(waveOutput);
  32. waveOutput = 0;
  33. }
  34. if (bufferEndEvent) {
  35. CloseHandle(bufferEndEvent);
  36. bufferEndEvent = 0;
  37. }
  38. for (int b = 0; b < 2; b++) {
  39. if (outputBuffer[b]) { free(outputBuffer[b]); }
  40. }
  41. if (floatBuffer) { free(floatBuffer); }
  42. }
  43. bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(float*, int)> 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)(outputBuffer[b]);
  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. memset(floatBuffer, 0, totalSamples * sizeof(float));
  83. // TODO: Use 128-bit aligned memory
  84. running = soundOutput(floatBuffer, samplesPerChannel);
  85. // TODO: Use SIMD
  86. for (int i = 0; i < totalSamples; i++) {
  87. outputBuffer[b][i] = sound_convertF32ToI16(floatBuffer[i]);
  88. }
  89. if (waveOutWrite(waveOutput, &header[b], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) {
  90. terminateSound(); throwError(U"Failed to write wave output!");
  91. }
  92. if (!running) { break; }
  93. }
  94. }
  95. WaitForSingleObject(bufferEndEvent, INFINITE);
  96. }
  97. terminateSound();
  98. return true;
  99. }