2 TiMidity++ -- MIDI to WAVE converter and player
3 Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
4 Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 Functions to play sound using WASAPI (Windows Vista).
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:
30 Copyright (c) Microsoft Corporation
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:
39 The above copyright notice and this permission notice shall be included in
40 all copies or substantial portions of the Software.
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
50 Portions of this repo are provided under the SIL Open Font License.
51 See the LICENSE file in individual samples for additional details
61 #endif /* HAVE_CONFIG_H */
68 #include "interface.h"
86 int opt_wasapi_device_id = -1; // default render
87 int opt_wasapi_latency = 10; // ms
88 int opt_wasapi_format_ext = 1;
89 int opt_wasapi_exclusive = 0; // shared
90 int opt_wasapi_polling = 0; // 0:event 1:polling
91 int opt_wasapi_priority = 0; // auto
92 int opt_wasapi_stream_category = 0;
93 int opt_wasapi_stream_option = 0;
95 /*****************************************************************************************************************************/
97 static int open_output (void); /* 0=success, 1=warning, -1=fatal error */
98 static void close_output (void);
99 static int output_data (const uint8 *buf, size_t nbytes);
100 static int acntl (int request, void *arg);
101 static int detect (void);
103 /*****************************************************************************************************************************/
105 #define dpm wasapi_play_mode
109 PE_16BIT | PE_SIGNED,
110 PF_PCM_STREAM | PF_CAN_TRACE | PF_BUFF_FRAGM_OPT,
122 /*****************************************************************************************************************************/
129 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
131 #include <Audioclient.h>
132 #include <audiopolicy.h>
134 #include <mmdeviceapi.h>
135 #include <functiondiscoverykeys.h>
139 const CLSID tim_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}};
140 const IID tim_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}};
141 const IID tim_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}};
142 const IID tim_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}};
144 #define SPEAKER_FRONT_LEFT 0x1
145 #define SPEAKER_FRONT_RIGHT 0x2
146 #define SPEAKER_FRONT_CENTER 0x4
147 #define SPEAKER_MONO (SPEAKER_FRONT_CENTER)
148 #define SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
150 const char *ThreadPriorityName[] =
162 /*****************************************************************************************************************************/
164 typedef struct WABufferBlock_t {
165 struct WABufferBlock_t *pNext;
171 typedef struct WABuffer_t {
172 WABufferBlock *pHead;
173 WABufferBlock *pTail;
174 WABufferBlock *pFree;
178 static WABuffer Buffers = {NULL, NULL, NULL, 0};
180 static int get_filled_byte(void)
182 return Buffers.FilledByte;
185 static int is_buffer_empty(void)
187 return !Buffers.pHead;
190 static size_t calc_output_bytes(size_t max_bytes)
193 WABufferBlock *block = Buffers.pHead;
195 while(bytes < max_bytes && block){
196 bytes += block->CurrentSize;
197 block = block->pNext;
199 return bytes > max_bytes ? max_bytes : bytes;
202 static void clear_buffer(void)
205 Buffers.pTail->pNext = Buffers.pFree;
206 Buffers.pFree = Buffers.pHead;
207 Buffers.pHead = NULL;
208 Buffers.pTail = NULL;
212 static void free_buffer(void)
214 WABufferBlock *block = NULL;
217 block = Buffers.pFree;
219 WABufferBlock* pNext = block->pNext;
223 Buffers.pFree = NULL;
224 Buffers.FilledByte = 0;
227 /* if *pbuf == NULL, appends `size` count of zeros */
228 static void input_buffer_partial(WABufferBlock *block, const uint8 **pbuf, size_t *bytes)
230 size_t pushLength = block->Capacity - block->CurrentSize;
232 if(pushLength > *bytes)
235 memcpy(block->Data + block->CurrentSize, *pbuf, pushLength);
238 memset(block->Data + block->CurrentSize, 0, pushLength);
240 *bytes -= pushLength;
241 block->CurrentSize += pushLength;
244 /* if buf == NULL, appends `size` count of zeros */
245 static int input_buffer(const uint8 *buf, size_t bytes)
247 size_t pbytes = bytes;
250 if(Buffers.pTail && Buffers.pTail->CurrentSize < Buffers.pTail->Capacity){
251 input_buffer_partial(Buffers.pTail, &buf, &bytes);
252 }else if(Buffers.pFree){
253 WABufferBlock *block = Buffers.pFree;
255 Buffers.pFree = Buffers.pFree->pNext;
256 block->CurrentSize = 0;
259 Buffers.pTail->pNext = block;
260 Buffers.pTail = block;
262 Buffers.pHead = block;
263 Buffers.pTail = block;
265 input_buffer_partial(block, &buf, &bytes);
267 size_t capacity = bytes * 4;
268 WABufferBlock *new_block = (WABufferBlock *)safe_malloc(sizeof(WABufferBlock) + capacity);
273 Buffers.pTail->pNext = new_block;
275 Buffers.pHead = new_block;
276 Buffers.pTail = new_block;
277 Buffers.pTail->pNext = NULL;
278 Buffers.pTail->Capacity = capacity;
279 Buffers.pTail->CurrentSize = 0;
280 input_buffer_partial(Buffers.pTail, &buf, &bytes);
283 Buffers.FilledByte += pbytes;
287 static void output_buffer(uint8 *buff, size_t bytes)
289 size_t pbytes = bytes, tmp_bytes, out_bytes = 0;
290 WABufferBlock *block = Buffers.pHead;
292 while(bytes > 0 && block){
293 tmp_bytes = block->CurrentSize;
294 if(tmp_bytes > bytes)
296 memcpy(buff, block->Data, tmp_bytes);
299 out_bytes += tmp_bytes;
300 block = block->pNext;
302 while (out_bytes > 0 && Buffers.pHead){
303 tmp_bytes = Buffers.pHead->CurrentSize;
304 if(Buffers.pHead->CurrentSize > out_bytes){
305 tmp_bytes = out_bytes;
306 memmove(Buffers.pHead->Data, Buffers.pHead->Data + out_bytes, Buffers.pHead->CurrentSize - out_bytes);
307 Buffers.pHead->CurrentSize -= out_bytes;
309 WABufferBlock *pNext = Buffers.pHead->pNext;
310 Buffers.pHead->pNext = Buffers.pFree;
311 Buffers.pFree = Buffers.pHead;
312 Buffers.pHead = pNext;
314 Buffers.pTail = NULL;
316 out_bytes -= tmp_bytes;
318 Buffers.FilledByte -= pbytes;
321 /*****************************************************************************************************************************/
323 typedef HANDLE (WINAPI *fAvSetMmThreadCharacteristics)(LPCSTR,LPDWORD);
324 typedef BOOL (WINAPI *fAvRevertMmThreadCharacteristics)(HANDLE);
326 static HINSTANCE hDll = NULL;
327 static fAvSetMmThreadCharacteristics pAvSetMmThreadCharacteristics = NULL;
328 static fAvRevertMmThreadCharacteristics pAvRevertMmThreadCharacteristics = NULL;
330 static int load_avrt(void)
332 hDll = LoadLibrary("avrt.dll");
336 pAvSetMmThreadCharacteristics = (fAvSetMmThreadCharacteristics)GetProcAddress(hDll, "AvSetMmThreadCharacteristicsW");
338 pAvSetMmThreadCharacteristics = (fAvSetMmThreadCharacteristics)GetProcAddress(hDll, "AvSetMmThreadCharacteristicsA");
340 pAvRevertMmThreadCharacteristics = (fAvRevertMmThreadCharacteristics)GetProcAddress(hDll, "AvRevertMmThreadCharacteristics");
341 return (int)pAvSetMmThreadCharacteristics && pAvRevertMmThreadCharacteristics;
344 static void free_avrt(void)
349 pAvSetMmThreadCharacteristics = NULL;
350 pAvRevertMmThreadCharacteristics = NULL;
353 static int get_winver(void)
355 DWORD winver, major, minor;
358 winver = GetVersion();
359 major = (DWORD)(LOBYTE(LOWORD(winver)));
360 minor = (DWORD)(HIBYTE(LOWORD(winver)));
372 case 0: ver = 1; break; // vista
373 case 1: ver = 2; break; // 7
374 case 2: ver = 3; break; // 8
375 case 3: ver = 4; break; // 8.1
376 default: ver = 5; break; // 8.2?
381 case 0: ver = 6; break; // 10
382 default: ver = 7; break; // 10.1?
392 /*****************************************************************************************************************************/
394 static HANDLE hEventTcv = NULL;
395 static HANDLE hRenderThread = NULL;
396 static IMMDevice* pMMDevice = NULL;
397 static IAudioClient* pAudioClient = NULL;
398 static IAudioRenderClient* pAudioRenderClient = NULL;
399 static REFERENCE_TIME BufferDuration = 100000;
400 static UINT32 FrameBytes = 0;
401 static UINT32 BufferFrames = 0;
402 static int ThreadPriorityNum = 0;
403 static int IsExclusive = 0;
404 static int IsPolling = 0;
405 static int IsStarted = 0;
406 static int IsOpened = 0;
407 static int IsThreadStart = 0;
408 static int IsThreadExit = 0;
409 static int IsCoInit = 0;
410 static int CoInitThreadId = 0;
411 static int CvtMode = 0; // 0:no convert 1:convert 24bit(3byte->4byte)
412 static uint32 QueueSize = sizeof(int16) * 2 * (1L << DEFAULT_AUDIO_BUFFER_BITS) * 2;
413 static uint32 WaitTime = 1;
414 static uint32 ThreadWaitTime = 1;
416 static int write_buffer_event(void)
419 size_t out_bytes, out_frames;
423 if(FAILED(IAudioClient_GetCurrentPadding(pAudioClient, &padding)))
425 out_bytes = calc_output_bytes((BufferFrames - padding) * FrameBytes);
426 out_frames = out_bytes / FrameBytes;
427 if(IsExclusive && out_frames < BufferFrames){
428 if(!input_buffer(NULL, BufferFrames * FrameBytes - out_bytes))
430 out_bytes = BufferFrames * FrameBytes;
431 out_frames = out_bytes / FrameBytes;
432 }else if(out_frames == 0){
433 if(!input_buffer(NULL, FrameBytes - out_bytes))
435 out_bytes = FrameBytes;
438 if(FAILED(IAudioRenderClient_GetBuffer(pAudioRenderClient, out_frames, &buf)))
440 output_buffer((uint8 *)buf, out_bytes);
441 IAudioRenderClient_ReleaseBuffer(pAudioRenderClient, out_frames, 0);
445 static int write_buffer_polling(void)
448 size_t out_bytes, out_frames;
451 if(FAILED(IAudioClient_GetCurrentPadding(pAudioClient, &padding)))
453 out_bytes = calc_output_bytes((BufferFrames - padding) * FrameBytes);
454 out_frames = out_bytes / FrameBytes;
455 if(FAILED(IAudioRenderClient_GetBuffer(pAudioRenderClient, out_frames, &buf)))
457 output_buffer((uint8 *)buf, out_bytes);
458 IAudioRenderClient_ReleaseBuffer(pAudioRenderClient, out_frames, 0);
462 static unsigned int WINAPI render_thread(void *arglist)
465 HANDLE hMmCss = NULL;
466 DWORD mmCssTaskIndex = 0;
468 if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
470 hMmCss = (pAvSetMmThreadCharacteristics)(ThreadPriorityName[ThreadPriorityNum], &mmCssTaskIndex);
474 WaitForSingleObject(hEventTcv, INFINITE); // wait initialize open_output
475 ResetEvent(hEventTcv);
478 WaitForSingleObject(hEventTcv, INFINITE);
479 ResetEvent(hEventTcv);
480 if(IsThreadExit) break;
481 EnterCriticalSection(&critSect);
482 write_buffer_event();
483 LeaveCriticalSection(&critSect);
487 WaitForSingleObject(hEventTcv, ThreadWaitTime);
488 ResetEvent(hEventTcv);
489 if(IsThreadExit) break;
490 EnterCriticalSection(&critSect);
491 write_buffer_polling();
492 LeaveCriticalSection(&critSect);
498 (pAvRevertMmThreadCharacteristics)(hMmCss);
504 /*****************************************************************************************************************************/
506 /* returns TRUE on success */
507 static int get_default_device(IMMDevice** ppMMDevice)
509 IMMDeviceEnumerator *pEnumerator = NULL;
511 if(FAILED(CoCreateInstance(&tim_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &tim_IID_IMMDeviceEnumerator, (void**)&pEnumerator)))
513 if(FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, ppMMDevice)))
516 IMMDeviceEnumerator_Release(pEnumerator);
520 IMMDeviceEnumerator_Release(pEnumerator);
524 static int get_device(IMMDevice **ppMMDevice, int devnum)
529 IMMDeviceEnumerator *pde = NULL;
530 IMMDeviceCollection *pdc = NULL;
531 IMMDevice *pdev = NULL;
532 WCHAR *pszDeviceId = NULL;
534 if(devnum > WASAPI_DEVLIST_MAX - 2)
537 return get_default_device(ppMMDevice);
538 if(FAILED(CoCreateInstance(&tim_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &tim_IID_IMMDeviceEnumerator, (void **)&pde)))
540 if(FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(pde, eRender, DEVICE_STATE_ACTIVE, &pdc)))
542 if(FAILED(IMMDeviceCollection_GetCount(pdc, &num)))
546 if(num > WASAPI_DEVLIST_MAX - 2)
547 num = WASAPI_DEVLIST_MAX - 2;
550 if(FAILED(IMMDeviceCollection_Item(pdc, devnum, &pdev)))
552 if(FAILED(IMMDevice_GetId(pdev, &pszDeviceId)))
554 if(FAILED(IMMDeviceEnumerator_GetDevice(pde, pszDeviceId, ppMMDevice)))
557 CoTaskMemFree(pszDeviceId);
559 IMMDeviceEnumerator_Release(pde);
564 CoTaskMemFree(pszDeviceId);
566 IMMDeviceEnumerator_Release(pde);
571 /*****************************************************************************************************************************/
572 /* interface function */
574 void close_output(void)
578 IAudioClient_Stop(pAudioClient);
586 WaitForSingleObject(hRenderThread, INFINITE);
588 CloseHandle(hRenderThread);
589 hRenderThread = NULL;
593 if(pAudioRenderClient){
594 IAudioRenderClient_Release(pAudioRenderClient);
595 pAudioRenderClient = NULL;
598 IAudioClient_Release(pAudioClient);
602 IMMDevice_Release(pMMDevice);
606 CloseHandle(hEventTcv);
609 if(IsCoInit && CoInitThreadId == GetCurrentThreadId()){
615 #ifdef CNV_USE_TEMP_ENCODE
616 reset_temporary_encoding();
621 /* 0=success, 1=warning, -1=fatal error */
622 int open_output(void)
625 int include_enc, exclude_enc;
626 WAVEFORMATEXTENSIBLE wfe = {0};
627 WAVEFORMATEX *pwf = NULL;
628 AUDCLNT_SHAREMODE ShareMode;
630 REFERENCE_TIME Periodicity, LatencyMax, LatencyMin;
631 GUID guid_WAVE_FORMAT = {WAVE_FORMAT_UNKNOWN,0x0000,0x0010,{0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
636 ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "ERROR! WASAPI require Windows Vista and later.");
640 ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "ERROR! AVRT.DLL function failed.");
643 IsExclusive = opt_wasapi_exclusive;
644 IsPolling = opt_wasapi_polling;
645 hEventTcv = CreateEvent(NULL,FALSE,FALSE,NULL); // reset manual
648 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
649 if(FAILED(hr) && hr != RPC_E_CHANGED_MODE)
651 if(hr != RPC_E_CHANGED_MODE){
653 CoInitThreadId = GetCurrentThreadId();
655 if(!get_device(&pMMDevice, opt_output_device_id == -3 ? opt_wasapi_device_id : opt_output_device_id))
657 if(FAILED(IMMDevice_Activate(pMMDevice, &tim_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&pAudioClient)))
662 IPropertyStore *pps = NULL;
666 if(FAILED(IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pps)))
668 PropVariantInit(&value);
669 if(FAILED(IPropertyStore_GetValue(pps, &PKEY_AudioEngine_DeviceFormat, &value)))
671 cpsz = min(sizeof(WAVEFORMATEXTENSIBLE), value.blob.cbSize);
672 memcpy(&wfe, value.blob.pBlobData, cpsz);
673 PropVariantClear(&value);
675 pps->lpVtbl->Release(pps);
678 if(cpsz == sizeof(WAVEFORMATEX)){
679 pwf = (WAVEFORMATEX *)&wfe;
680 opt_wasapi_format_ext = 0;
683 opt_wasapi_format_ext = 1;
688 include_enc = PE_SIGNED;
689 exclude_enc = PE_BYTESWAP | PE_ULAW | PE_ALAW;
690 if(!(dpm.encoding & (PE_F64BIT | PE_64BIT | PE_F32BIT | PE_32BIT | PE_24BIT | PE_16BIT))) // 8bit
691 include_enc |= PE_16BIT;
692 dpm.encoding = validate_encoding(dpm.encoding, include_enc, exclude_enc);
694 if(opt_wasapi_format_ext){
696 memcpy(&wfe.SubFormat, &guid_WAVE_FORMAT, sizeof(GUID));
697 pwf->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
698 pwf->cbSize = (WORD)22;
700 pwf = (WAVEFORMATEX *)&wfe;
701 pwf->cbSize = (WORD)0;
704 if(dpm.encoding & PE_16BIT){
705 if(opt_wasapi_format_ext){
706 wfe.SubFormat.Data1 = WAVE_FORMAT_PCM;
707 wfe.Samples.wValidBitsPerSample = 16;
709 pwf->wBitsPerSample = (WORD) 16;
711 if(opt_wasapi_exclusive){
712 pwf->wBitsPerSample = (WORD) 32;
715 pwf->wBitsPerSample = (WORD) 16;
719 pwf->wFormatTag = WAVE_FORMAT_PCM;
720 pwf->wBitsPerSample = (WORD) 16;
722 }else if(dpm.encoding & PE_24BIT){
723 if(opt_wasapi_format_ext){
724 wfe.SubFormat.Data1 = WAVE_FORMAT_PCM;
725 wfe.Samples.wValidBitsPerSample = 24;
727 pwf->wBitsPerSample = (WORD) 32;
730 pwf->wBitsPerSample = (WORD) 24;
733 pwf->wFormatTag = WAVE_FORMAT_PCM;
734 pwf->wBitsPerSample = (WORD) 24;
736 }else if(dpm.encoding & PE_32BIT){
737 if(opt_wasapi_format_ext){
738 wfe.SubFormat.Data1 = WAVE_FORMAT_PCM;
739 wfe.Samples.wValidBitsPerSample = 32;
740 pwf->wBitsPerSample = (WORD) 32;
742 pwf->wFormatTag = WAVE_FORMAT_PCM;
743 pwf->wBitsPerSample = (WORD) 32;
745 }else if(dpm.encoding & PE_F32BIT){
746 if(opt_wasapi_format_ext){
747 wfe.SubFormat.Data1 = WAVE_FORMAT_IEEE_FLOAT;
748 wfe.Samples.wValidBitsPerSample = 32;
749 pwf->wBitsPerSample = (WORD) 32;
751 pwf->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
752 pwf->wBitsPerSample = (WORD) 32;
754 }else if(dpm.encoding & PE_64BIT){
755 if(opt_wasapi_format_ext){
756 wfe.SubFormat.Data1 = WAVE_FORMAT_PCM;
757 wfe.Samples.wValidBitsPerSample = 64;
758 pwf->wBitsPerSample = (WORD) 64;
760 pwf->wFormatTag = WAVE_FORMAT_PCM;
761 pwf->wBitsPerSample = (WORD) 64;
763 }else if(dpm.encoding & PE_F64BIT){
764 if(opt_wasapi_format_ext){
765 wfe.SubFormat.Data1 = WAVE_FORMAT_IEEE_FLOAT;
766 wfe.Samples.wValidBitsPerSample = 64;
767 pwf->wBitsPerSample = (WORD) 64;
769 pwf->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
770 pwf->wBitsPerSample = (WORD) 64;
772 }else{ // 8bit // error
773 if(opt_wasapi_format_ext){
774 wfe.SubFormat.Data1 = WAVE_FORMAT_PCM;
775 wfe.Samples.wValidBitsPerSample = 8;
776 pwf->wBitsPerSample = (WORD) 8;
778 pwf->wFormatTag = WAVE_FORMAT_PCM;
779 pwf->wBitsPerSample = (WORD) 8;
782 pwf->nChannels = (WORD)dpm.encoding & PE_MONO ? 1 : 2;
783 pwf->nSamplesPerSec = (DWORD)dpm.rate;
784 pwf->nBlockAlign = (WORD)(pwf->nChannels * pwf->wBitsPerSample / 8);
785 pwf->nAvgBytesPerSec = (DWORD)pwf->nSamplesPerSec * pwf->nBlockAlign;
786 wfe.dwChannelMask = pwf->nChannels==1 ? SPEAKER_MONO : SPEAKER_STEREO;
788 #ifdef CNV_USE_TEMP_ENCODE
790 int tmp_enc = dpm.encoding;
791 tmp_enc &= ~PE_24BIT;
793 set_temporary_encoding(tmp_enc);
794 }else if(CvtMode == 1){
795 int tmp_enc = dpm.encoding;
796 tmp_enc &= ~PE_16BIT;
798 set_temporary_encoding(tmp_enc);
800 reset_temporary_encoding();
804 FrameBytes = pwf->nBlockAlign;
806 #ifdef __IAudioClient2_INTERFACE_DEFINED__
808 int ver = get_winver();
810 if(ver >= 3) // win8
\88È
\8fã
812 AudioClientProperties acp = {0};
813 acp.cbSize = sizeof(AudioClientProperties);
814 acp.bIsOffload = FALSE;
815 acp.eCategory = opt_wasapi_stream_category;
817 if(opt_wasapi_stream_option >= 2){
818 if(ver >= 6) // win10
\88È
\8fã
819 acp.Options = AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
820 }else if(opt_wasapi_stream_option == 1){
821 if(ver >= 6) // win8.1
\88È
\8fã
822 acp.Options = AUDCLNT_STREAMOPTIONS_RAW;
824 hr = IAudioClient2_SetClientProperties((IAudioClient2 *)pAudioClient, (AudioClientProperties *)&acp);
830 if(opt_wasapi_priority <= 0 || opt_wasapi_priority > 7)
831 ThreadPriorityNum = IsExclusive ? 6 : 1;
833 ThreadPriorityNum = opt_wasapi_priority;
834 ShareMode = IsExclusive ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
835 StreamFlags = IsPolling ? 0x0 : AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
836 StreamFlags |= AUDCLNT_STREAMFLAGS_NOPERSIST;
840 if(FAILED(IAudioClient_GetDevicePeriod(pAudioClient, &LatencyMax, &LatencyMin))){
844 if(LatencyMax > 10000000) // 1000ms
845 LatencyMax = 10000000;
846 else if(LatencyMin < 10000)
847 LatencyMin = 10000; // 1ms
848 BufferDuration = opt_wasapi_latency * 10000; // ms to 100ns
849 if(BufferDuration > LatencyMax)
850 BufferDuration = LatencyMax;
851 if(BufferDuration < LatencyMin)
852 BufferDuration = LatencyMin;
854 BufferDuration = (IsExclusive ? 10 : 30) * 10000; // 10ms,30ms
856 Periodicity = IsExclusive ? BufferDuration : 0;
858 hr = IAudioClient_Initialize(pAudioClient, ShareMode, StreamFlags, BufferDuration, Periodicity, (WAVEFORMATEX *)&wfe, NULL);
859 if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED){
862 if(FAILED(IAudioClient_GetBufferSize(pAudioClient, &bufferSize)))
864 IAudioClient_Release(pAudioClient);
866 BufferDuration = (REFERENCE_TIME)(10000.0f * 1000 * bufferSize / pwf->nSamplesPerSec + 0.5);
867 if(FAILED(IMMDevice_Activate(pMMDevice, &tim_IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&pAudioClient)))
869 hr = IAudioClient_Initialize(pAudioClient, ShareMode, StreamFlags, BufferDuration, Periodicity, (WAVEFORMATEX *)&wfe, NULL);
873 if(FAILED(IAudioClient_GetBufferSize(pAudioClient, &BufferFrames)))
876 if(FAILED(IAudioClient_SetEventHandle(pAudioClient, hEventTcv)))
878 if(FAILED(IAudioClient_GetService(pAudioClient, &tim_IID_IAudioRenderClient, (void**)&pAudioRenderClient)))
881 if(dpm.extra_param[0] < 2){
882 ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Too small -B option: %d,X", dpm.extra_param[0]);
883 dpm.extra_param[0] = 2;
885 if(audio_buffer_size < 5){
886 ctl->cmsg(CMSG_WARNING, VERB_NORMAL, "Too small -B option: X,%d", audio_buffer_bits);
887 audio_buffer_bits = 5;
889 QueueSize = audio_buffer_size * FrameBytes * dpm.extra_param[0];
890 WaitTime = (double)audio_buffer_size * div_playmode_rate * 1000.0 * DIV_4; // blocktime/4
891 if(IsPolling){ // for polling
892 DWORD sleep_ms_in = (double)audio_buffer_size * div_playmode_rate * 1000.0 * DIV_2;
893 DWORD sleep_ms_out = (double)BufferDuration * DIV_10000 * DIV_2; // 100ns to ms
894 ThreadWaitTime = sleep_ms_in < sleep_ms_out ? sleep_ms_in : sleep_ms_out;
900 hRenderThread = (HANDLE)_beginthreadex(NULL, 0, &render_thread, NULL, 0, NULL);
905 int count = 20; // 200ms
906 if(FAILED(IAudioRenderClient_GetBuffer(pAudioRenderClient, BufferFrames, &buf)))
908 IAudioRenderClient_ReleaseBuffer(pAudioRenderClient, BufferFrames, AUDCLNT_BUFFERFLAGS_SILENT);
909 while(!IsThreadStart && count > 0){ //
913 if(count <= 0) // time out
915 if(FAILED(IAudioClient_Start(pAudioClient)))
917 SetEvent(hEventTcv); // start process
926 int output_data(const uint8 *buf, size_t nbytes)
929 int32 max_count = 64; // wait = blocktime/4 * max_count
930 #ifndef CNV_USE_TEMP_ENCODE
931 uint8 tbuff[2 * (1L << DEFAULT_AUDIO_BUFFER_BITS) * sizeof(int16) * 2] = {0};
936 #ifndef CNV_USE_TEMP_ENCODE
937 if(CvtMode == 2){ // 24bit 3byte->4byte
938 int samples = nbytes / 3;
939 uint8 *in = (uint8 *)buf, *out = tbuff;
940 for(i = 0; i < samples; i++){
946 buf = (const uint8 *)&tbuff;
947 nbytes = samples * 4;
948 }else if(CvtMode == 1){ // 16bit 2byte->4byte
949 int samples = nbytes / 2;
950 uint8 *in = (uint8 *)buf, *out = tbuff;
951 for(i = 0; i < samples; i++){
957 buf = (const uint8 *)&tbuff;
958 nbytes = samples * 4;
964 if(get_filled_byte() < QueueSize)
967 ctl->cmsg(CMSG_ERROR, VERB_NORMAL, "WASAPI error: timeout output_data().");
973 EnterCriticalSection(&critSect);
974 flg = input_buffer(buf, nbytes);
975 LeaveCriticalSection(&critSect);
982 int acntl(int request, void *arg)
985 //case PM_REQ_GETFRAGSIZ:
988 *(int *)arg = QueueSize;
990 case PM_REQ_GETFILLED:
991 *(int *)arg = get_filled_byte();
993 case PM_REQ_FLUSH: // thru
994 while (!is_buffer_empty())
995 WaitForSingleObject(hRenderThread, 10);
1000 case PM_REQ_PLAY_START:
1002 case PM_REQ_PLAY_END:
1011 IMMDevice *pMMDevice = NULL;
1012 int result = get_default_device(&pMMDevice);
1014 IMMDevice_Release(pMMDevice);
1018 /*****************************************************************************************************************************/
1020 int wasapi_device_list(WASAPI_DEVICELIST *device)
1025 IMMDeviceEnumerator *pde = NULL;
1026 IMMDeviceCollection *pdc = NULL;
1027 IPropertyStore *pps = NULL;
1028 IAudioClient *tmpClient = NULL;
1029 REFERENCE_TIME LatencyMax, LatencyMin;
1030 IMMDevice *defdev = NULL;
1032 device[0].deviceID = -1;
1033 device[0].LatencyMax = 10;
1034 device[0].LatencyMin = 3;
1035 strcpy(device[0].name, "Default Render Device");
1039 if(detect() == FALSE)
1041 if(!get_default_device(&defdev))
1043 if(FAILED(CoCreateInstance(&tim_CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &tim_IID_IMMDeviceEnumerator, (void **)&pde)))
1045 if(FAILED(IMMDeviceEnumerator_EnumAudioEndpoints(pde, eRender, DEVICE_STATE_ACTIVE, &pdc)))
1047 LatencyMax = 100000;
1049 if(FAILED(IMMDevice_Activate(defdev, &tim_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&tmpClient))){
1050 LatencyMin =LatencyMin;
1051 }else if(FAILED(IAudioClient_GetDevicePeriod(tmpClient, &LatencyMax, &LatencyMin))){
1052 LatencyMin =LatencyMin;
1054 LatencyMax /= 10000; // hns to ms
1055 LatencyMin /= 10000; // hns to ms
1056 if(LatencyMax > 1000)
1060 device[0].LatencyMax = LatencyMax;
1061 device[0].LatencyMin = LatencyMin;
1063 tmpClient->lpVtbl->Release(tmpClient);
1067 IMMDevice_Release(defdev);
1070 if(FAILED(IMMDeviceCollection_GetCount(pdc, &num)))
1074 if(num > WASAPI_DEVLIST_MAX - 2)
1075 num = WASAPI_DEVLIST_MAX - 2;
1076 for(i = 0; i < num; i++){ // -1, 0
1077 IMMDevice *dev = NULL;
1079 IAudioClient *tmpClient = NULL;
1081 if(FAILED(IMMDeviceCollection_Item(pdc, i, &dev)))
1083 device[i+1].deviceID = i;
1084 if(FAILED(IMMDevice_OpenPropertyStore(dev, STGM_READ, &pps)))
1086 PropVariantInit(&value);
1087 if(FAILED(IPropertyStore_GetValue(pps, &PKEY_Device_FriendlyName, &value))){
1088 PropVariantClear(&value);
1092 WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (int)wcslen(value.pwszVal), device[i+1].name, WASAPI_DEVLIST_LEN - 1, 0, 0);
1094 WideCharToMultiByte(CP_ACP, 0, value.pwszVal, (int)wcslen(value.pwszVal), device[i+1].name, WASAPI_DEVLIST_LEN - 1, 0, 0);
1097 _snprintf(device[i+1].name, WASAPI_DEVLIST_LEN - 1, "Device Error %d", i);
1099 PropVariantClear(&value);
1100 LatencyMax = 100000;
1102 if(FAILED(IMMDevice_Activate(dev, &tim_IID_IAudioClient, CLSCTX_ALL, NULL, (void **)&tmpClient))){
1103 }else if(FAILED(IAudioClient_GetDevicePeriod(tmpClient, &LatencyMax, &LatencyMin))){
1105 LatencyMax /= 10000; // hns to ms
1106 LatencyMin /= 10000; // hns to ms
1107 if(LatencyMax > 1000)
1111 device[i+1].LatencyMax = LatencyMax;
1112 device[i+1].LatencyMin = LatencyMin;
1114 tmpClient->lpVtbl->Release(tmpClient);
1118 IMMDevice_Release(dev);
1122 pps->lpVtbl->Release(pps);
1127 pdc->lpVtbl->Release(pdc);
1129 IMMDeviceEnumerator_Release(pde);
1130 return num + 1; // +1 def dev
1134 tmpClient->lpVtbl->Release(tmpClient);
1136 pdc->lpVtbl->Release(pdc);
1139 IMMDeviceEnumerator_Release(pde);
1146 #endif /* AU_WASAPI */