• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

TiMidity++ 改造版 (Windows 専用)


Commit MetaInfo

修订版cb18f30a8bc3185cc603cfc4f4a5dda1fbe8cdbb (tree)
时间2017-11-12 19:17:33
作者Starg <starg@user...>
CommiterStarg

Log Message

Implemented native WASAPI play mode.

更改概述

差异

--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,6 +47,7 @@ add_definitions(
4747 -D__W32G__
4848
4949 -D_CRT_SECURE_NO_WARNINGS
50+ -DCOBJMACROS
5051
5152 # network
5253 -DWINSOCK
--- a/timidity/CMakeLists.txt
+++ b/timidity/CMakeLists.txt
@@ -110,6 +110,7 @@ set(
110110 winspool
111111 comdlg32
112112 advapi32
113+ avrt
113114 shell32
114115 ole32
115116 oleaut32
--- a/timidity/wasapi_a.c
+++ b/timidity/wasapi_a.c
@@ -21,9 +21,42 @@ wasapi_a.c
2121
2222 Functions to play sound using WASAPI (Windows 7+).
2323
24+Based on <https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/Win7Samples/multimedia/audio/RenderExclusiveEventDriven>,
25+which is distributed under the MIT License:
26+
27+----
28+The MIT License (MIT)
29+
30+Copyright (c) Microsoft Corporation
31+
32+Permission is hereby granted, free of charge, to any person obtaining a copy
33+of this software and associated documentation files (the "Software"), to deal
34+in the Software without restriction, including without limitation the rights
35+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36+copies of the Software, and to permit persons to whom the Software is
37+furnished to do so, subject to the following conditions:
38+
39+The above copyright notice and this permission notice shall be included in
40+all copies or substantial portions of the Software.
41+
42+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48+THE SOFTWARE.
49+
50+Portions of this repo are provided under the SIL Open Font License.
51+See the LICENSE file in individual samples for additional details
52+----
53+
2454 Written by Starg.
55+
2556 */
2657
58+#ifdef AU_WASAPI
59+
2760 #ifdef HAVE_CONFIG_H
2861 #include "config.h"
2962 #endif /* HAVE_CONFIG_H */
@@ -37,42 +70,30 @@ Written by Starg.
3770 #else
3871 #include <strings.h>
3972 #endif
40-#include <windows.h>
41-
42-#ifdef AU_WASAPI
73+#include <process.h>
74+#include <tchar.h>
4375
76+#include <windows.h>
77+#include <initguid.h>
78+#include <objbase.h>
79+#include <mmdeviceapi.h>
80+#include <audioclient.h>
81+#include <audiopolicy.h>
82+#include <avrt.h>
4483 #include "timidity.h"
4584 #include "output.h"
4685
47-int WASAPIOpenOutputShared(void)
48-{
49- return -1;
50-}
51-
52-int WASAPIOpenOutputExclusive(void)
53-{
54- return -1;
55-}
56-
57-void WASAPICloseOutput(void)
58-{
59-
60-}
61-
62-int WASAPIOutputData(char* pData, int32 size)
63-{
64- return -1;
65-}
66-
67-int WASAPIACntl(int request, void* pArg)
68-{
69- return -1;
70-}
86+const CLSID MyCLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}};
87+const IID MyIID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}};
88+const IID MyIID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}};
89+const IID MyIID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}};
7190
72-int WASAPIDetect(void)
73-{
74- return 0;
75-}
91+int WASAPIOpenOutputShared(void);
92+int WASAPIOpenOutputExclusive(void);
93+void WASAPICloseOutput(void);
94+int WASAPIOutputData(char* pData, int32 size);
95+int WASAPIACntl(int request, void* pArg);
96+int WASAPIDetect(void);
7697
7798 PlayMode wasapi_shared_play_mode = {
7899 DEFAULT_RATE,
@@ -104,4 +125,668 @@ PlayMode wasapi_exclusive_play_mode = {
104125 &WASAPIDetect
105126 };
106127
128+PlayMode* GetWASAPIPlayModeInfo(int isExclusive)
129+{
130+ return isExclusive ? &wasapi_exclusive_play_mode : &wasapi_shared_play_mode;
131+}
132+
133+/* RenderBuffer */
134+
135+typedef struct RenderBufferNode
136+{
137+ struct RenderBufferNode* pNext;
138+ size_t CurrentSize;
139+ size_t Capacity;
140+ char Data[1];
141+} RenderBufferNode;
142+
143+typedef struct RenderBuffer
144+{
145+ RenderBufferNode* pHead;
146+ RenderBufferNode* pTail;
147+ RenderBufferNode* pFree;
148+} RenderBuffer;
149+
150+int IsRenderBufferEmpty(RenderBuffer* pRenderBuffer)
151+{
152+ return !pRenderBuffer->pHead;
153+}
154+
155+void ClearRenderBuffer(RenderBuffer* pRenderBuffer)
156+{
157+ if (pRenderBuffer->pTail)
158+ {
159+ /* prepend [pHead, ..., pTail] to [pFree, ...] */
160+ pRenderBuffer->pTail->pNext = pRenderBuffer->pFree;
161+ pRenderBuffer->pFree = pRenderBuffer->pHead;
162+ pRenderBuffer->pHead = NULL;
163+ pRenderBuffer->pTail = NULL;
164+ }
165+}
166+
167+void DeleteRenderBuffer(RenderBuffer* pRenderBuffer)
168+{
169+ ClearRenderBuffer(pRenderBuffer);
170+
171+ {
172+ RenderBufferNode* pNode = pRenderBuffer->pFree;
173+
174+ while (pNode)
175+ {
176+ RenderBufferNode* pNext = pNode->pNext;
177+ free(pNode);
178+ pNode = pNext;
179+ }
180+ }
181+
182+ pRenderBuffer->pFree = NULL;
183+}
184+
185+void PushToRenderBufferPartial(RenderBufferNode* pNode, const char** ppData, size_t* pSize)
186+{
187+ size_t copyLength = pNode->Capacity - pNode->CurrentSize;
188+
189+ if (copyLength > *pSize)
190+ {
191+ copyLength = *pSize;
192+ }
193+
194+ memcpy(pNode->Data + pNode->CurrentSize, *ppData, copyLength);
195+ *ppData += copyLength;
196+ *pSize -= copyLength;
197+ pNode->CurrentSize += copyLength;
198+}
199+
200+int PushToRenderBuffer(RenderBuffer* pRenderBuffer, const char* pData, size_t size)
201+{
202+ while (size > 0)
203+ {
204+ if (pRenderBuffer->pTail && pRenderBuffer->pTail->CurrentSize < pRenderBuffer->pTail->Capacity)
205+ {
206+ PushToRenderBufferPartial(pRenderBuffer->pTail, &pData, &size);
207+ }
208+ else if (pRenderBuffer->pFree)
209+ {
210+ RenderBufferNode* pNode = pRenderBuffer->pFree;
211+ pRenderBuffer->pFree = pRenderBuffer->pFree->pNext;
212+ pNode->CurrentSize = 0;
213+ pNode->pNext = NULL;
214+
215+ if (pRenderBuffer->pTail)
216+ {
217+ pRenderBuffer->pTail->pNext = pNode;
218+ pRenderBuffer->pTail = pNode;
219+ }
220+ else
221+ {
222+ pRenderBuffer->pHead = pNode;
223+ pRenderBuffer->pTail = pNode;
224+ }
225+
226+ PushToRenderBufferPartial(pNode, &pData, &size);
227+ }
228+ else
229+ {
230+ size_t capacity = size * 4;
231+ RenderBufferNode* pNewNode = malloc(sizeof(RenderBufferNode) + capacity);
232+
233+ if (!pNewNode)
234+ {
235+ return FALSE;
236+ }
237+
238+ if (pRenderBuffer->pTail)
239+ {
240+ pRenderBuffer->pTail->pNext = pNewNode;
241+ }
242+ else
243+ {
244+ pRenderBuffer->pHead = pNewNode;
245+ }
246+
247+ pRenderBuffer->pTail = pNewNode;
248+ pRenderBuffer->pTail->pNext = NULL;
249+ pRenderBuffer->pTail->Capacity = capacity;
250+ pRenderBuffer->pTail->CurrentSize = 0;
251+ PushToRenderBufferPartial(pRenderBuffer->pTail, &pData, &size);
252+ }
253+ }
254+
255+ return TRUE;
256+}
257+
258+size_t CalculateRenderBufferSize(RenderBuffer* pRenderBuffer, size_t maxSize)
259+{
260+ size_t size = 0;
261+ RenderBufferNode* pNode = pRenderBuffer->pHead;
262+
263+ while (size < maxSize && pNode)
264+ {
265+ size += pNode->CurrentSize;
266+ pNode = pNode->pNext;
267+ }
268+
269+ if (size > maxSize)
270+ {
271+ size = maxSize;
272+ }
273+
274+ return size;
275+}
276+
277+size_t ReadRenderBuffer(RenderBuffer* pRenderBuffer, char* pBuffer, size_t size)
278+{
279+ size_t copiedLength = 0;
280+ RenderBufferNode* pNode = pRenderBuffer->pHead;
281+
282+ while (size > 0 && pNode)
283+ {
284+ size_t copySize = pNode->CurrentSize;
285+
286+ if (copySize > size)
287+ {
288+ copySize = size;
289+ }
290+
291+ memcpy(pBuffer, pNode->Data, copySize);
292+ pBuffer += copySize;
293+ size -= copySize;
294+ copiedLength += copySize;
295+ pNode = pNode->pNext;
296+ }
297+
298+ return copiedLength;
299+}
300+
301+size_t PopRenderBuffer(RenderBuffer* pRenderBuffer, size_t size)
302+{
303+ size_t actualLength = 0;
304+
305+ while (size > 0 && pRenderBuffer->pHead)
306+ {
307+ size_t popSize = pRenderBuffer->pHead->CurrentSize;
308+
309+ if (pRenderBuffer->pHead->CurrentSize > size)
310+ {
311+ popSize = size;
312+ memmove(
313+ pRenderBuffer->pHead->Data,
314+ pRenderBuffer->pHead->Data + size,
315+ pRenderBuffer->pHead->CurrentSize - size
316+ );
317+ pRenderBuffer->pHead->CurrentSize -= size;
318+ }
319+ else
320+ {
321+ /* prepend pHead to [pFree, ...] */
322+ RenderBufferNode* pNext = pRenderBuffer->pHead->pNext;
323+ pRenderBuffer->pHead->pNext = pRenderBuffer->pFree;
324+ pRenderBuffer->pFree = pRenderBuffer->pHead;
325+ pRenderBuffer->pHead = pNext;
326+
327+ if (!pNext)
328+ {
329+ pRenderBuffer->pTail = NULL;
330+ }
331+ }
332+
333+ size -= popSize;
334+ actualLength += popSize;
335+ }
336+
337+ return actualLength;
338+}
339+
340+/* WASAPIContext */
341+
342+typedef struct WASAPIContext
343+{
344+ int IsExclusive;
345+ HANDLE hShutdownEvent;
346+ HANDLE hAudioSamplesReadyEvent;
347+ IMMDevice* pMMDevice;
348+ IAudioClient* pAudioClient;
349+ IAudioRenderClient* pAudioRenderClient;
350+ UINT32 FrameSize;
351+ UINT32 BufferSizeInFrames;
352+ RenderBuffer Buffer;
353+ HANDLE hRenderThread;
354+ int IsStarted;
355+} WASAPIContext;
356+
357+WASAPIContext g_WASAPIContext;
358+
359+void WASAPIDoStop(void)
360+{
361+ if (g_WASAPIContext.IsStarted)
362+ {
363+ if (g_WASAPIContext.pAudioClient)
364+ {
365+ IAudioClient_Stop(g_WASAPIContext.pAudioClient);
366+ }
367+
368+ g_WASAPIContext.IsStarted = FALSE;
369+ }
370+
371+ if (g_WASAPIContext.hRenderThread)
372+ {
373+ if (g_WASAPIContext.hShutdownEvent)
374+ {
375+ SetEvent(g_WASAPIContext.hShutdownEvent);
376+ WaitForSingleObject(g_WASAPIContext.hRenderThread, INFINITE);
377+ }
378+
379+ CloseHandle(g_WASAPIContext.hRenderThread);
380+ g_WASAPIContext.hRenderThread = NULL;
381+ }
382+
383+ DeleteRenderBuffer(&g_WASAPIContext.Buffer);
384+}
385+
386+void ResetWASAPIContext(void)
387+{
388+ WASAPIDoStop();
389+
390+ g_WASAPIContext.BufferSizeInFrames = 0;
391+ g_WASAPIContext.FrameSize = 0;
392+
393+ if (g_WASAPIContext.pAudioRenderClient)
394+ {
395+ IAudioRenderClient_Release(g_WASAPIContext.pAudioRenderClient);
396+ g_WASAPIContext.pAudioRenderClient = NULL;
397+ }
398+
399+ if (g_WASAPIContext.pAudioClient)
400+ {
401+ IAudioClient_Release(g_WASAPIContext.pAudioClient);
402+ g_WASAPIContext.pAudioClient = NULL;
403+ }
404+
405+ if (g_WASAPIContext.pMMDevice)
406+ {
407+ IMMDevice_Release(g_WASAPIContext.pMMDevice);
408+ g_WASAPIContext.pMMDevice = NULL;
409+ }
410+
411+ if (g_WASAPIContext.hAudioSamplesReadyEvent)
412+ {
413+ CloseHandle(g_WASAPIContext.hAudioSamplesReadyEvent);
414+ g_WASAPIContext.hAudioSamplesReadyEvent = NULL;
415+ }
416+
417+ if (g_WASAPIContext.hShutdownEvent)
418+ {
419+ CloseHandle(g_WASAPIContext.hShutdownEvent);
420+ g_WASAPIContext.hShutdownEvent = NULL;
421+ }
422+}
423+
424+/* returns TRUE on success */
425+int WASAPIGetDefaultDevice(IMMDevice** ppMMDevice)
426+{
427+ int ret = FALSE;
428+ IMMDeviceEnumerator* pEnumerator = NULL;
429+
430+ if (FAILED(CoCreateInstance(&MyCLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &MyIID_IMMDeviceEnumerator, (void**)&pEnumerator)))
431+ {
432+ goto LExit;
433+ }
434+
435+ if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, ppMMDevice)))
436+ {
437+ goto LExit;
438+ }
439+
440+ ret = TRUE;
441+
442+LExit:
443+ if (pEnumerator)
444+ {
445+ IMMDeviceEnumerator_Release(pEnumerator);
446+ }
447+
448+ return ret;
449+}
450+
451+/* return 0 on success, -1 on failure */
452+int WASAPIOpenOutput(int isExclusive)
453+{
454+ int ret = -1;
455+
456+ ResetWASAPIContext();
457+ g_WASAPIContext.IsExclusive = isExclusive;
458+
459+ g_WASAPIContext.hShutdownEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
460+
461+ if (!g_WASAPIContext.hShutdownEvent)
462+ {
463+ ResetWASAPIContext();
464+ return -1;
465+ }
466+
467+ g_WASAPIContext.hAudioSamplesReadyEvent = CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
468+
469+ if (!g_WASAPIContext.hAudioSamplesReadyEvent)
470+ {
471+ ResetWASAPIContext();
472+ return -1;
473+ }
474+
475+ if (!WASAPIGetDefaultDevice(&g_WASAPIContext.pMMDevice))
476+ {
477+ ResetWASAPIContext();
478+ return -1;
479+ }
480+
481+ if (FAILED(IMMDevice_Activate(g_WASAPIContext.pMMDevice, &MyIID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&g_WASAPIContext.pAudioClient)))
482+ {
483+ ResetWASAPIContext();
484+ return -1;
485+ }
486+
487+ {
488+ REFERENCE_TIME bufferDuration = (isExclusive ? 10 : 30) /* ms */ * 10000;
489+ PlayMode* pPlayMode = GetWASAPIPlayModeInfo(isExclusive);
490+ WAVEFORMATEX waveFormat = {0};
491+ HRESULT hr;
492+
493+ g_WASAPIContext.FrameSize = get_encoding_sample_size(pPlayMode->encoding);
494+
495+ waveFormat.wFormatTag = WAVE_FORMAT_PCM;
496+ waveFormat.nChannels = pPlayMode->encoding & PE_MONO ? 1 : 2;
497+ waveFormat.nSamplesPerSec = pPlayMode->rate;
498+ waveFormat.nBlockAlign = g_WASAPIContext.FrameSize;
499+ waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
500+ waveFormat.wBitsPerSample = pPlayMode->encoding & PE_24BIT ? 24 : pPlayMode->encoding & PE_16BIT ? 16 : 8;
501+ waveFormat.cbSize = 0;
502+
503+
504+ hr = IAudioClient_Initialize(
505+ g_WASAPIContext.pAudioClient,
506+ isExclusive ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
507+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
508+ bufferDuration,
509+ isExclusive ? bufferDuration : 0,
510+ &waveFormat,
511+ NULL
512+ );
513+
514+ if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
515+ {
516+ UINT32 bufferSize;
517+
518+ if (FAILED(IAudioClient_GetBufferSize(g_WASAPIContext.pAudioClient, &bufferSize)))
519+ {
520+ ResetWASAPIContext();
521+ return -1;
522+ }
523+
524+ IAudioClient_Release(g_WASAPIContext.pAudioClient);
525+ g_WASAPIContext.pAudioClient = NULL;
526+
527+ bufferDuration = (REFERENCE_TIME)(10000.0f * 1000 * bufferSize / waveFormat.nSamplesPerSec + 0.5);
528+
529+ if (FAILED(IMMDevice_Activate(g_WASAPIContext.pMMDevice, &MyIID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&g_WASAPIContext.pAudioClient)))
530+ {
531+ ResetWASAPIContext();
532+ return -1;
533+ }
534+
535+ hr = IAudioClient_Initialize(
536+ g_WASAPIContext.pAudioClient,
537+ isExclusive ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
538+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
539+ bufferDuration,
540+ isExclusive ? bufferDuration : 0,
541+ &waveFormat,
542+ NULL
543+ );
544+ }
545+
546+ if (FAILED(hr))
547+ {
548+ ResetWASAPIContext();
549+ return -1;
550+ }
551+ }
552+
553+ if (FAILED(IAudioClient_GetBufferSize(g_WASAPIContext.pAudioClient, &g_WASAPIContext.BufferSizeInFrames)))
554+ {
555+ ResetWASAPIContext();
556+ return -1;
557+ }
558+
559+ if (FAILED(IAudioClient_SetEventHandle(g_WASAPIContext.pAudioClient, g_WASAPIContext.hAudioSamplesReadyEvent)))
560+ {
561+ ResetWASAPIContext();
562+ return -1;
563+ }
564+
565+ if (FAILED(IAudioClient_GetService(g_WASAPIContext.pAudioClient, &MyIID_IAudioRenderClient, (void**)&g_WASAPIContext.pAudioRenderClient)))
566+ {
567+ ResetWASAPIContext();
568+ return -1;
569+ }
570+
571+ return 0;
572+}
573+
574+int WASAPIDoWriteBuffer(void)
575+{
576+ UINT32 padding = 0;
577+
578+ if (!g_WASAPIContext.IsExclusive)
579+ {
580+ if (FAILED(IAudioClient_GetCurrentPadding(g_WASAPIContext.pAudioClient, &padding)))
581+ {
582+ return FALSE;
583+ }
584+ }
585+
586+ {
587+ size_t numberOfBytesToCopy = CalculateRenderBufferSize(&g_WASAPIContext.Buffer, (g_WASAPIContext.BufferSizeInFrames - padding) * g_WASAPIContext.FrameSize);
588+ size_t numberOfFramesToCopy = numberOfBytesToCopy / g_WASAPIContext.FrameSize;
589+
590+ if (numberOfFramesToCopy > 0)
591+ {
592+ BYTE* pData;
593+ HRESULT hr = IAudioRenderClient_GetBuffer(g_WASAPIContext.pAudioRenderClient, numberOfFramesToCopy, &pData);
594+
595+ if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)
596+ {
597+ PopRenderBuffer(&g_WASAPIContext.Buffer, numberOfFramesToCopy * g_WASAPIContext.FrameSize);
598+ return TRUE;
599+ }
600+ else if (FAILED(hr))
601+ {
602+ return FALSE;
603+ }
604+
605+ PopRenderBuffer(&g_WASAPIContext.Buffer, ReadRenderBuffer(&g_WASAPIContext.Buffer, (char*)pData, numberOfFramesToCopy * g_WASAPIContext.FrameSize));
606+ IAudioRenderClient_ReleaseBuffer(g_WASAPIContext.pAudioRenderClient, numberOfFramesToCopy, 0);
607+ }
608+ else
609+ {
610+ ClearRenderBuffer(&g_WASAPIContext.Buffer);
611+ }
612+ }
613+
614+ return TRUE;
615+}
616+
617+unsigned int __stdcall WASAPIRenderThread(void* pData)
618+{
619+ int ret = 1;
620+ int stillPlaying = TRUE;
621+ HANDLE waitArray[2] = {g_WASAPIContext.hShutdownEvent, g_WASAPIContext.hAudioSamplesReadyEvent};
622+ HANDLE hMmCss = NULL;
623+ DWORD mmCssTaskIndex = 0;
624+
625+ (void)pData;
626+
627+ if (FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
628+ {
629+ return 1;
630+ }
631+
632+ hMmCss = AvSetMmThreadCharacteristics(_T("Audio"), &mmCssTaskIndex);
633+
634+ if (!hMmCss)
635+ {
636+ goto LExit;
637+ }
638+
639+ while (stillPlaying)
640+ {
641+ DWORD waitResult = WaitForMultipleObjects(_countof(waitArray), waitArray, FALSE, INFINITE);
642+
643+ switch (waitResult)
644+ {
645+ case WAIT_OBJECT_0 + 0: /* hShutdownEvent */
646+ ret = 0;
647+ stillPlaying = FALSE;
648+ break;
649+
650+ case WAIT_OBJECT_0 + 1: /* hAudioSamplesReadyEvent */
651+ WASAPIDoWriteBuffer();
652+ break;
653+ }
654+ }
655+
656+LExit:
657+ if (hMmCss)
658+ {
659+ AvRevertMmThreadCharacteristics(hMmCss);
660+ }
661+
662+ CoUninitialize();
663+ return ret;
664+}
665+
666+int WASAPIPlayStart(void)
667+{
668+ if (!g_WASAPIContext.hRenderThread)
669+ {
670+ g_WASAPIContext.hRenderThread = (HANDLE)_beginthreadex(
671+ NULL,
672+ 0,
673+ &WASAPIRenderThread,
674+ NULL,
675+ 0,
676+ NULL
677+ );
678+
679+ if (!g_WASAPIContext.hRenderThread)
680+ {
681+ ResetWASAPIContext();
682+ return -1;
683+ }
684+ }
685+
686+ if (!g_WASAPIContext.IsStarted)
687+ {
688+ if (IsRenderBufferEmpty(&g_WASAPIContext.Buffer))
689+ {
690+ BYTE* pData;
691+
692+ if (FAILED(IAudioRenderClient_GetBuffer(g_WASAPIContext.pAudioRenderClient, g_WASAPIContext.BufferSizeInFrames, &pData)))
693+ {
694+ ResetWASAPIContext();
695+ return -1;
696+ }
697+
698+ IAudioRenderClient_ReleaseBuffer(g_WASAPIContext.pAudioRenderClient, g_WASAPIContext.BufferSizeInFrames, AUDCLNT_BUFFERFLAGS_SILENT);
699+ }
700+ else
701+ {
702+ if (!WASAPIDoWriteBuffer())
703+ {
704+ ResetWASAPIContext();
705+ return -1;
706+ }
707+ }
708+
709+ if (FAILED(IAudioClient_Start(g_WASAPIContext.pAudioClient)))
710+ {
711+ ResetWASAPIContext();
712+ return -1;
713+ }
714+
715+ g_WASAPIContext.IsStarted = TRUE;
716+ }
717+
718+ return 0;
719+}
720+
721+int WASAPIOpenOutputShared(void)
722+{
723+ return WASAPIOpenOutput(FALSE);
724+}
725+
726+int WASAPIOpenOutputExclusive(void)
727+{
728+ return WASAPIOpenOutput(TRUE);
729+}
730+
731+void WASAPICloseOutput(void)
732+{
733+ ResetWASAPIContext();
734+}
735+
736+int WASAPIOutputData(char* pData, int32 size)
737+{
738+ int ret = -1;
739+
740+ if (!PushToRenderBuffer(&g_WASAPIContext.Buffer, pData, (size_t)size))
741+ {
742+ ResetWASAPIContext();
743+ return -1;
744+ }
745+ return 0;
746+}
747+
748+int WASAPIACntl(int request, void* pArg)
749+{
750+ switch (request)
751+ {
752+ case PM_REQ_GETQSIZ:
753+ *(int*)pArg = (int)(g_WASAPIContext.BufferSizeInFrames * g_WASAPIContext.FrameSize);
754+ return 0;
755+
756+ case PM_REQ_DISCARD:
757+ WASAPIDoStop();
758+ return 0;
759+
760+ case PM_REQ_PLAY_START:
761+ return WASAPIPlayStart();
762+
763+ case PM_REQ_PLAY_END:
764+ WASAPIDoStop();
765+ return 0;
766+
767+ case PM_REQ_OUTPUT_FINISH:
768+ while (!IsRenderBufferEmpty(&g_WASAPIContext.Buffer))
769+ {
770+ WaitForSingleObject(g_WASAPIContext.hRenderThread, 10);
771+ }
772+ return 0;
773+
774+ default:
775+ return -1;
776+ }
777+}
778+
779+int WASAPIDetect(void)
780+{
781+ IMMDevice* pMMDevice = NULL;
782+ int result = WASAPIGetDefaultDevice(&pMMDevice);
783+
784+ if (pMMDevice)
785+ {
786+ IMMDevice_Release(pMMDevice);
787+ }
788+
789+ return result;
790+}
791+
107792 #endif /* AU_WASAPI */