windows_audio.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. #ifdef IRON_A2
  2. #include <iron_audio.h>
  3. #include <iron_system.h>
  4. #include <backends/windows_system.h>
  5. // Windows 7
  6. #define WINVER 0x0601
  7. #ifdef _WIN32_WINNT
  8. #undef _WIN32_WINNT
  9. #endif
  10. #define _WIN32_WINNT 0x0601
  11. #define NOATOM
  12. #define NOCLIPBOARD
  13. #define NOCOLOR
  14. #define NOCOMM
  15. #define NOCTLMGR
  16. #define NODEFERWINDOWPOS
  17. #define NODRAWTEXT
  18. // #define NOGDI
  19. #define NOGDICAPMASKS
  20. #define NOHELP
  21. #define NOICONS
  22. #define NOKANJI
  23. #define NOKEYSTATES
  24. #define NOMB
  25. #define NOMCX
  26. #define NOMEMMGR
  27. #define NOMENUS
  28. #define NOMETAFILE
  29. #define NOMINMAX
  30. // #define NOMSG
  31. #define NONLS
  32. #define NOOPENFILE
  33. #define NOPROFILER
  34. #define NORASTEROPS
  35. #define NOSCROLL
  36. #define NOSERVICE
  37. #define NOSHOWWINDOW
  38. #define NOSOUND
  39. #define NOSYSCOMMANDS
  40. #define NOSYSMETRICS
  41. #define NOTEXTMETRIC
  42. // #define NOUSER
  43. #define NOVIRTUALKEYCODES
  44. #define NOWH
  45. #define NOWINMESSAGES
  46. #define NOWINOFFSETS
  47. #define NOWINSTYLES
  48. #define WIN32_LEAN_AND_MEAN
  49. #include <initguid.h>
  50. #include <AudioClient.h>
  51. #include <mmdeviceapi.h>
  52. #ifndef __MINGW32__
  53. // MIDL_INTERFACE("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
  54. DEFINE_GUID(IID_IAudioClient, 0x1CB9AD4C, 0xDBFA, 0x4c32, 0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2);
  55. // MIDL_INTERFACE("F294ACFC-3146-4483-A7BF-ADDCA7C260E2")
  56. DEFINE_GUID(IID_IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2);
  57. // MIDL_INTERFACE("A95664D2-9614-4F35-A746-DE8DB63617E6")
  58. DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6);
  59. // DECLSPEC_UUID("BCDE0395-E52F-467C-8E3D-C4579291692E")
  60. DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E);
  61. #endif
  62. // based on the implementation in soloud and Microsoft sample code
  63. static iron_a2_buffer_t a2_buffer;
  64. static IMMDeviceEnumerator *deviceEnumerator;
  65. static IMMDevice *device;
  66. static IAudioClient *audioClient = NULL;
  67. static IAudioRenderClient *renderClient = NULL;
  68. static HANDLE bufferEndEvent = 0;
  69. static HANDLE audioProcessingDoneEvent;
  70. static UINT32 bufferFrames;
  71. static WAVEFORMATEX requestedFormat;
  72. static WAVEFORMATEX *closestFormat;
  73. static WAVEFORMATEX *format;
  74. static uint32_t samples_per_second = 44100;
  75. uint32_t iron_a2_samples_per_second(void) {
  76. return samples_per_second;
  77. }
  78. static bool initDefaultDevice() {
  79. if (renderClient != NULL) {
  80. renderClient->lpVtbl->Release(renderClient);
  81. renderClient = NULL;
  82. }
  83. if (audioClient != NULL) {
  84. audioClient->lpVtbl->Release(audioClient);
  85. audioClient = NULL;
  86. }
  87. if (bufferEndEvent != 0) {
  88. CloseHandle(bufferEndEvent);
  89. bufferEndEvent = 0;
  90. }
  91. iron_log("Initializing a new default audio device.");
  92. HRESULT hr = deviceEnumerator->lpVtbl->GetDefaultAudioEndpoint(deviceEnumerator, eRender, eConsole, &device);
  93. if (hr == S_OK) {
  94. hr = device->lpVtbl->Activate(device, &IID_IAudioClient, CLSCTX_ALL, 0, (void **)&audioClient);
  95. }
  96. if (hr == S_OK) {
  97. const int sampleRate = 48000;
  98. format = &requestedFormat;
  99. memset(&requestedFormat, 0, sizeof(WAVEFORMATEX));
  100. requestedFormat.nChannels = 2;
  101. requestedFormat.nSamplesPerSec = sampleRate;
  102. requestedFormat.wFormatTag = WAVE_FORMAT_PCM;
  103. requestedFormat.wBitsPerSample = sizeof(short) * 8;
  104. requestedFormat.nBlockAlign = (requestedFormat.nChannels * requestedFormat.wBitsPerSample) / 8;
  105. requestedFormat.nAvgBytesPerSec = requestedFormat.nSamplesPerSec * requestedFormat.nBlockAlign;
  106. requestedFormat.cbSize = 0;
  107. HRESULT supported = audioClient->lpVtbl->IsFormatSupported(audioClient, AUDCLNT_SHAREMODE_SHARED, format, &closestFormat);
  108. if (supported == S_FALSE) {
  109. iron_log("Falling back to the system's preferred WASAPI mix format.", supported);
  110. if (closestFormat != NULL) {
  111. format = closestFormat;
  112. }
  113. else {
  114. audioClient->lpVtbl->GetMixFormat(audioClient, &format);
  115. }
  116. }
  117. HRESULT result =
  118. audioClient->lpVtbl->Initialize(audioClient, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 40 * 1000 * 10, 0, format, 0);
  119. if (result != S_OK) {
  120. iron_log("Could not initialize WASAPI audio, going silent (error code 0x%x).", result);
  121. return false;
  122. }
  123. uint32_t old_samples_per_second = samples_per_second;
  124. samples_per_second = format->nSamplesPerSec;
  125. if (samples_per_second != old_samples_per_second) {
  126. iron_a2_internal_sample_rate_callback();
  127. }
  128. a2_buffer.channel_count = 2;
  129. bufferFrames = 0;
  130. audioClient->lpVtbl->GetBufferSize(audioClient, &bufferFrames);
  131. audioClient->lpVtbl->GetService(audioClient, &IID_IAudioRenderClient, (void **)&renderClient);
  132. bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0);
  133. audioClient->lpVtbl->SetEventHandle(audioClient, bufferEndEvent);
  134. return true;
  135. }
  136. else {
  137. iron_log("Could not initialize WASAPI audio.");
  138. return false;
  139. }
  140. }
  141. static void copyS16Sample(int16_t *left, int16_t *right) {
  142. float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
  143. float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
  144. a2_buffer.read_location += 1;
  145. if (a2_buffer.read_location >= a2_buffer.data_size) {
  146. a2_buffer.read_location = 0;
  147. }
  148. *left = (int16_t)(left_value * 32767);
  149. *right = (int16_t)(right_value * 32767);
  150. }
  151. static void copyFloatSample(float *left, float *right) {
  152. float left_value = *(float *)&a2_buffer.channels[0][a2_buffer.read_location];
  153. float right_value = *(float *)&a2_buffer.channels[1][a2_buffer.read_location];
  154. a2_buffer.read_location += 1;
  155. if (a2_buffer.read_location >= a2_buffer.data_size) {
  156. a2_buffer.read_location = 0;
  157. }
  158. *left = left_value;
  159. *right = right_value;
  160. }
  161. static void submitEmptyBuffer(unsigned frames) {
  162. BYTE *buffer = NULL;
  163. HRESULT result = renderClient->lpVtbl->GetBuffer(renderClient, frames, &buffer);
  164. if (FAILED(result)) {
  165. return;
  166. }
  167. memset(buffer, 0, frames * format->nBlockAlign);
  168. result = renderClient->lpVtbl->ReleaseBuffer(renderClient, frames, 0);
  169. }
  170. static void submitBuffer(unsigned frames) {
  171. BYTE *buffer = NULL;
  172. HRESULT result = renderClient->lpVtbl->GetBuffer(renderClient, frames, &buffer);
  173. if (FAILED(result)) {
  174. if (result == AUDCLNT_E_DEVICE_INVALIDATED) {
  175. initDefaultDevice();
  176. submitEmptyBuffer(bufferFrames);
  177. audioClient->lpVtbl->Start(audioClient);
  178. }
  179. return;
  180. }
  181. if (iron_a2_internal_callback(&a2_buffer, frames)) {
  182. if (format->wFormatTag == WAVE_FORMAT_PCM) {
  183. for (UINT32 i = 0; i < frames; ++i) {
  184. copyS16Sample((int16_t *)&buffer[i * format->nBlockAlign], (int16_t *)&buffer[i * format->nBlockAlign + 2]);
  185. }
  186. }
  187. else {
  188. for (UINT32 i = 0; i < frames; ++i) {
  189. copyFloatSample((float *)&buffer[i * format->nBlockAlign], (float *)&buffer[i * format->nBlockAlign + 4]);
  190. }
  191. }
  192. }
  193. else {
  194. memset(buffer, 0, frames * format->nBlockAlign);
  195. }
  196. result = renderClient->lpVtbl->ReleaseBuffer(renderClient, frames, 0);
  197. if (FAILED(result)) {
  198. if (result == AUDCLNT_E_DEVICE_INVALIDATED) {
  199. initDefaultDevice();
  200. submitEmptyBuffer(bufferFrames);
  201. audioClient->lpVtbl->Start(audioClient);
  202. }
  203. }
  204. }
  205. static DWORD WINAPI audioThread(LPVOID ignored) {
  206. submitBuffer(bufferFrames);
  207. audioClient->lpVtbl->Start(audioClient);
  208. while (WAIT_OBJECT_0 != WaitForSingleObject(audioProcessingDoneEvent, 0)) {
  209. WaitForSingleObject(bufferEndEvent, INFINITE);
  210. UINT32 padding = 0;
  211. HRESULT result = audioClient->lpVtbl->GetCurrentPadding(audioClient, &padding);
  212. if (FAILED(result)) {
  213. if (result == AUDCLNT_E_DEVICE_INVALIDATED) {
  214. initDefaultDevice();
  215. submitEmptyBuffer(bufferFrames);
  216. audioClient->lpVtbl->Start(audioClient);
  217. }
  218. continue;
  219. }
  220. UINT32 frames = bufferFrames - padding;
  221. submitBuffer(frames);
  222. }
  223. return 0;
  224. }
  225. void iron_windows_co_initialize(void);
  226. static bool initialized = false;
  227. void iron_a2_init() {
  228. if (initialized) {
  229. return;
  230. }
  231. iron_a2_internal_init();
  232. initialized = true;
  233. a2_buffer.read_location = 0;
  234. a2_buffer.write_location = 0;
  235. a2_buffer.data_size = 128 * 1024;
  236. a2_buffer.channel_count = 2;
  237. a2_buffer.channels[0] = (float *)malloc(a2_buffer.data_size * sizeof(float));
  238. a2_buffer.channels[1] = (float *)malloc(a2_buffer.data_size * sizeof(float));
  239. audioProcessingDoneEvent = CreateEvent(0, FALSE, FALSE, 0);
  240. iron_windows_co_initialize();
  241. CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void **)&deviceEnumerator);
  242. if (initDefaultDevice()) {
  243. CreateThread(0, 65536, audioThread, NULL, 0, 0);
  244. }
  245. }
  246. void iron_a2_update() {}
  247. #define SAFE_RELEASE(punk) \
  248. if ((punk) != NULL) { \
  249. (punk)->Release(); \
  250. (punk) = NULL; \
  251. }
  252. void iron_a2_shutdown() {
  253. // Wait for last data in buffer to play before stopping.
  254. // Sleep((DWORD)(hnsActualDuration/REFTIMES_PER_MILLISEC/2));
  255. // affirm(pAudioClient->Stop()); // Stop playing.
  256. // CoTaskMemFree(pwfx);
  257. // SAFE_RELEASE(pEnumerator)
  258. // SAFE_RELEASE(pDevice)
  259. // SAFE_RELEASE(pAudioClient)
  260. // SAFE_RELEASE(pRenderClient)
  261. }
  262. #endif