1 /* FluidSynth - A Software Synthesizer
2  *
3  * Copyright (C) 2003  Peter Hanappe and others.
4  * Copyright (C) 2021  Chris Xiong
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA
20  */
21 
22 #include "fluid_synth.h"
23 #include "fluid_adriver.h"
24 #include "fluid_settings.h"
25 
26 #if WASAPI_SUPPORT
27 
28 #define CINTERFACE
29 #define COBJMACROS
30 
31 #include <objbase.h>
32 #include <windows.h>
33 #include <initguid.h>
34 #include <mmdeviceapi.h>
35 #include <audioclient.h>
36 #include <audiosessiontypes.h>
37 #include <functiondiscoverykeys_devpkey.h>
38 #include <mmreg.h>
39 #include <oaidl.h>
40 #include <ksguid.h>
41 #include <ksmedia.h>
42 
43 // these symbols are either never found in headers, or
44 // only defined but there are no library containing the actual symbol...
45 
46 #ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
47 #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
48 #endif
49 
50 #ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
51 #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
52 #endif
53 
54 static const CLSID _CLSID_MMDeviceEnumerator =
55 {
56     0xbcde0395, 0xe52f, 0x467c, {0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e}
57 };
58 
59 static const IID   _IID_IMMDeviceEnumerator =
60 {
61     0xa95664d2, 0x9614, 0x4f35, {0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6}
62 };
63 static const IID   _IID_IAudioClient =
64 {
65     0x1cb9ad4c, 0xdbfa, 0x4c32, {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2}
66 };
67 static const IID   _IID_IAudioRenderClient =
68 {
69     0xf294acfc, 0x3146, 0x4483, {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2}
70 };
71 /*
72  * WASAPI Driver for fluidsynth
73  *
74  * Current limitations:
75  *  - Only one stereo audio output.
76  *  - If audio.sample-format is "16bits", a conversion from float
77  *    without dithering is used.
78  *
79  * Available settings:
80  *  - audio.wasapi.exclusive-mode
81  *    0 = shared mode, 1 = exclusive mode
82  *  - audio.wasapi.device
83  *    Used for device selection. Leave unchanged (or use the value "default")
84  *    to use the default Windows audio device for media playback.
85  *
86  * Notes:
87  *  - If exclusive mode is selected, audio.period-size is used as the periodicity
88  *    of the IAudioClient stream, which is the sole factor of audio latency.
89  *    audio.periods still determines the buffer size, but has no direct impact on
90  *    the latency (at least according to Microsoft). The valid range for
91  *    audio.period-size may vary depending on the driver and sample rate.
92  *  - In shared mode, audio.period-size is completely ignored. Instead, a value
93  *    provided by the audio driver is used. In theory this means the latency in
94  *    shared mode is out of fluidsynth's control, but you may still increase
95  *    audio.periods for a larger buffer to fix buffer underruns in case there
96  *    are any.
97  *  - The sample rate and sample format of fluidsynth must be supported by the
98  *    audio device in exclusive mode. Otherwise driver creation will fail. Use
99  *    `fluidsynth ---query-audio-devices` to find out the modes supported by
100  *    the soundcards installed on the system.
101  *  - In shared mode, if the sample rate of the synth doesn't match what is
102  *    configured in the 'advanced' tab of the audio device properties dialog,
103  *    Windows will automatically resample the output (obviously). Windows
104  *    Vista doesn't seem to support the resampling method with better quality.
105  *  - Under Windows 10, this driver may report a latency of 0ms in shared mode.
106  *    This is nonsensical and should be disregarded.
107  *
108  */
109 
110 #define FLUID_WASAPI_MAX_OUTPUTS 1
111 
112 typedef void(*fluid_wasapi_devenum_callback_t)(IMMDevice *, void *);
113 
114 static DWORD WINAPI fluid_wasapi_audio_run(void *p);
115 static int fluid_wasapi_write_processed_channels(void *data, int len,
116         int channels_count,
117         void *channels_out[], int channels_off[],
118         int channels_incr[]);
119 static void fluid_wasapi_foreach_device(fluid_wasapi_devenum_callback_t callback, void *data);
120 static void fluid_wasapi_register_callback(IMMDevice *dev, void *data);
121 static void fluid_wasapi_finddev_callback(IMMDevice *dev, void *data);
122 static IMMDevice *fluid_wasapi_find_device(IMMDeviceEnumerator *denum, const char *name);
123 static FLUID_INLINE int16_t round_clip_to_i16(float x);
124 
125 typedef struct
126 {
127     const char *name;
128     wchar_t *id;
129 } fluid_wasapi_finddev_data_t;
130 
131 typedef struct
132 {
133     fluid_audio_driver_t driver;
134 
135     void *user_pointer;
136     fluid_audio_func_t func;
137     fluid_audio_channels_callback_t write;
138     float **drybuf;
139 
140     UINT32 nframes;
141     double buffer_duration;
142     int channels_count;
143     int float_samples;
144 
145     HANDLE start_ev;
146     HANDLE thread;
147     DWORD thread_id;
148     HANDLE quit_ev;
149 
150     IAudioClient *aucl;
151     IAudioRenderClient *arcl;
152 
153     double sample_rate;
154     int periods, period_size;
155     fluid_long_long_t buffer_duration_reftime;
156     fluid_long_long_t periods_reftime;
157     fluid_long_long_t latency_reftime;
158     int audio_channels;
159     int sample_size;
160     char *dname;
161     int exclusive;
162     unsigned short sample_format;
163 
164 } fluid_wasapi_audio_driver_t;
165 
new_fluid_wasapi_audio_driver(fluid_settings_t * settings,fluid_synth_t * synth)166 fluid_audio_driver_t *new_fluid_wasapi_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
167 {
168     return new_fluid_wasapi_audio_driver2(settings, (fluid_audio_func_t)fluid_synth_process, synth);
169 }
170 
new_fluid_wasapi_audio_driver2(fluid_settings_t * settings,fluid_audio_func_t func,void * data)171 fluid_audio_driver_t *new_fluid_wasapi_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data)
172 {
173     DWORD ret;
174     HANDLE wait_handles[2];
175     fluid_wasapi_audio_driver_t *dev = NULL;
176     OSVERSIONINFOEXW vi = {sizeof(vi), 6, 0, 0, 0, {0}, 0, 0, 0, 0, 0};
177 
178     if(!VerifyVersionInfoW(&vi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
179                            VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0,
180                                    VER_MAJORVERSION, VER_GREATER_EQUAL),
181                                    VER_MINORVERSION, VER_GREATER_EQUAL),
182                                    VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)))
183     {
184         FLUID_LOG(FLUID_ERR, "wasapi: this driver requires Windows Vista or newer.");
185         return NULL;
186     }
187 
188     dev = FLUID_NEW(fluid_wasapi_audio_driver_t);
189 
190     if(dev == NULL)
191     {
192         FLUID_LOG(FLUID_ERR, "wasapi: out of memory.");
193         return NULL;
194     }
195 
196     FLUID_MEMSET(dev, 0, sizeof(fluid_wasapi_audio_driver_t));
197 
198     /* Retrieve the settings */
199 
200     fluid_settings_getnum(settings, "synth.sample-rate", &dev->sample_rate);
201     fluid_settings_getint(settings, "audio.periods", &dev->periods);
202     fluid_settings_getint(settings, "audio.period-size", &dev->period_size);
203     fluid_settings_getint(settings, "synth.audio-channels", &dev->audio_channels);
204     fluid_settings_getint(settings, "audio.wasapi.exclusive-mode", &dev->exclusive);
205 
206     if(dev->audio_channels > FLUID_WASAPI_MAX_OUTPUTS)
207     {
208         FLUID_LOG(FLUID_ERR, "wasapi: channel configuration with more than one stereo pair is not supported.");
209         goto cleanup;
210     }
211 
212     if(fluid_settings_str_equal(settings, "audio.sample-format", "16bits"))
213     {
214         dev->sample_size = sizeof(int16_t);
215         dev->sample_format = WAVE_FORMAT_PCM;
216     }
217     else
218     {
219         dev->sample_size = sizeof(float);
220         dev->sample_format = WAVE_FORMAT_IEEE_FLOAT;
221     }
222 
223     if(fluid_settings_dupstr(settings, "audio.wasapi.device", &dev->dname) != FLUID_OK)
224     {
225         FLUID_LOG(FLUID_ERR, "wasapi: out of memory.");
226         goto cleanup;
227     }
228 
229     dev->func = func;
230     dev->user_pointer = data;
231     dev->buffer_duration = dev->periods * dev->period_size / dev->sample_rate;
232     dev->channels_count = dev->audio_channels * 2;
233     dev->float_samples = (dev->sample_format == WAVE_FORMAT_IEEE_FLOAT);
234     dev->buffer_duration_reftime = (fluid_long_long_t)(dev->buffer_duration * 1e7 + .5);
235     dev->periods_reftime = (fluid_long_long_t)(dev->period_size / dev->sample_rate * 1e7 + .5);
236 
237     dev->quit_ev = CreateEvent(NULL, FALSE, FALSE, NULL);
238 
239     if(dev->quit_ev == NULL)
240     {
241         FLUID_LOG(FLUID_ERR, "wasapi: failed to create quit event: '%s'", fluid_get_windows_error());
242         goto cleanup;
243     }
244 
245     dev->start_ev = CreateEvent(NULL, FALSE, FALSE, NULL);
246 
247     if(dev->start_ev == NULL)
248     {
249         FLUID_LOG(FLUID_ERR, "wasapi: failed to create start event: '%s'", fluid_get_windows_error());
250         goto cleanup;
251     }
252 
253     dev->thread = CreateThread(NULL, 0, fluid_wasapi_audio_run, dev, 0, &dev->thread_id);
254 
255     if(dev->thread == NULL)
256     {
257         FLUID_LOG(FLUID_ERR, "wasapi: failed to create audio thread: '%s'", fluid_get_windows_error());
258         goto cleanup;
259     }
260 
261     /* start event must be first */
262     wait_handles[0] = dev->start_ev;
263     wait_handles[1] = dev->thread;
264     ret = WaitForMultipleObjects(FLUID_N_ELEMENTS(wait_handles), wait_handles, FALSE, 2000);
265 
266     switch(ret)
267     {
268     case WAIT_OBJECT_0:
269         return &dev->driver;
270 
271     case WAIT_TIMEOUT:
272         FLUID_LOG(FLUID_WARN, "wasapi: initialization timeout!");
273         break;
274 
275     default:
276         break;
277     }
278 
279 cleanup:
280 
281     delete_fluid_wasapi_audio_driver(&dev->driver);
282     return NULL;
283 }
284 
delete_fluid_wasapi_audio_driver(fluid_audio_driver_t * p)285 void delete_fluid_wasapi_audio_driver(fluid_audio_driver_t *p)
286 {
287     fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *) p;
288     int i;
289 
290     fluid_return_if_fail(dev != NULL);
291 
292     if(dev->thread != NULL)
293     {
294         SetEvent(dev->quit_ev);
295 
296         if(WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0)
297         {
298             FLUID_LOG(FLUID_WARN, "wasapi: couldn't join the audio thread. killing it.");
299             TerminateThread(dev->thread, 0);
300         }
301 
302         CloseHandle(dev->thread);
303     }
304 
305     if(dev->quit_ev != NULL)
306     {
307         CloseHandle(dev->quit_ev);
308     }
309 
310     if(dev->start_ev != NULL)
311     {
312         CloseHandle(dev->start_ev);
313     }
314 
315     if(dev->drybuf)
316     {
317         for(i = 0; i < dev->channels_count; ++i)
318         {
319             FLUID_FREE(dev->drybuf[i]);
320         }
321     }
322 
323     FLUID_FREE(dev->dname);
324 
325     FLUID_FREE(dev->drybuf);
326 
327     FLUID_FREE(dev);
328 }
329 
fluid_wasapi_audio_driver_settings(fluid_settings_t * settings)330 void fluid_wasapi_audio_driver_settings(fluid_settings_t *settings)
331 {
332     fluid_settings_register_int(settings, "audio.wasapi.exclusive-mode", 0, 0, 1, FLUID_HINT_TOGGLED);
333     fluid_settings_register_str(settings, "audio.wasapi.device", "default", 0);
334     fluid_settings_add_option(settings, "audio.wasapi.device", "default");
335     fluid_wasapi_foreach_device(fluid_wasapi_register_callback, settings);
336 }
337 
fluid_wasapi_audio_run(void * p)338 static DWORD WINAPI fluid_wasapi_audio_run(void *p)
339 {
340     fluid_wasapi_audio_driver_t *dev = (fluid_wasapi_audio_driver_t *)p;
341     DWORD time_to_sleep;
342     UINT32 pos;
343     DWORD len;
344     void *channels_out[2];
345     int channels_off[2] = {0, 1};
346     int channels_incr[2] = {2, 2};
347     BYTE *pbuf;
348     HRESULT ret;
349     IMMDeviceEnumerator *denum = NULL;
350     IMMDevice *mmdev = NULL;
351     DWORD flags = 0;
352     WAVEFORMATEXTENSIBLE wfx;
353     WAVEFORMATEXTENSIBLE *rwfx = NULL;
354     AUDCLNT_SHAREMODE share_mode;
355     OSVERSIONINFOEXW vi = {sizeof(vi), 6, 0, 0, 0, {0}, 0, 0, 0, 0, 0};
356     int needs_com_uninit = FALSE;
357     int i;
358 
359     /* Clear format structure */
360     ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
361 
362     wfx.Format.nChannels  = 2;
363     wfx.Format.wBitsPerSample  = dev->sample_size * 8;
364     wfx.Format.nBlockAlign     = dev->sample_size * wfx.Format.nChannels;
365     wfx.Format.nSamplesPerSec  = (DWORD) dev->sample_rate;
366     wfx.Format.nAvgBytesPerSec = (DWORD) dev->sample_rate * wfx.Format.nBlockAlign;
367     wfx.Format.wFormatTag      = dev->sample_format;
368     //wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
369     //wfx.Format.cbSize = 22;
370     //wfx.SubFormat = guid_float;
371     //wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
372     //wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
373 
374     /* initialize COM in a worker thread to avoid a potential double initialization in the callers thread */
375     ret = CoInitializeEx(NULL, COINIT_MULTITHREADED);
376 
377     if(FAILED(ret))
378     {
379         FLUID_LOG(FLUID_ERR, "wasapi: cannot initialize COM. 0x%x", (unsigned)ret);
380         goto cleanup;
381     }
382 
383     needs_com_uninit = TRUE;
384 
385     ret = CoCreateInstance(
386               &_CLSID_MMDeviceEnumerator, NULL,
387               CLSCTX_ALL, &_IID_IMMDeviceEnumerator,
388               (void **)&denum);
389 
390     if(FAILED(ret))
391     {
392         FLUID_LOG(FLUID_ERR, "wasapi: cannot create device enumerator. 0x%x", (unsigned)ret);
393         goto cleanup;
394     }
395 
396     mmdev = fluid_wasapi_find_device(denum, dev->dname);
397 
398     if(mmdev == NULL)
399     {
400         goto cleanup;
401     }
402 
403     ret = IMMDevice_Activate(mmdev,
404                              &_IID_IAudioClient,
405                              CLSCTX_ALL, NULL,
406                              (void **)&dev->aucl);
407 
408     if(FAILED(ret))
409     {
410         FLUID_LOG(FLUID_ERR, "wasapi: cannot activate audio client. 0x%x", (unsigned)ret);
411         goto cleanup;
412     }
413 
414 
415     if(dev->exclusive)
416     {
417         share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
418         FLUID_LOG(FLUID_DBG, "wasapi: using exclusive mode.");
419     }
420     else
421     {
422         fluid_long_long_t defp;
423         share_mode = AUDCLNT_SHAREMODE_SHARED;
424         FLUID_LOG(FLUID_DBG, "wasapi: using shared mode.");
425         dev->periods_reftime = 0;
426 
427         //use default period size of the device
428         if(SUCCEEDED(IAudioClient_GetDevicePeriod(dev->aucl, &defp, NULL)))
429         {
430             dev->period_size = (int)(defp / 1e7 * dev->sample_rate);
431             dev->buffer_duration = dev->periods * dev->period_size / dev->sample_rate;
432             dev->buffer_duration_reftime = (fluid_long_long_t)(dev->buffer_duration * 1e7 + .5);
433             FLUID_LOG(FLUID_DBG, "wasapi: using device period size: %d", dev->period_size);
434         }
435     }
436 
437     ret = IAudioClient_IsFormatSupported(dev->aucl, share_mode, (const WAVEFORMATEX *)&wfx, (WAVEFORMATEX **)&rwfx);
438 
439     if(FAILED(ret))
440     {
441         FLUID_LOG(FLUID_ERR, "wasapi: device doesn't support the mode we want. 0x%x", (unsigned)ret);
442         goto cleanup;
443     }
444     else if(ret == S_FALSE)
445     {
446         //rwfx is non-null only in this case
447         FLUID_LOG(FLUID_INFO, "wasapi: requested mode cannot be fully satisfied.");
448 
449         if(rwfx->Format.nSamplesPerSec != wfx.Format.nSamplesPerSec) // needs resampling
450         {
451             flags = AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
452             vi.dwMinorVersion = 1;
453 
454             if(VerifyVersionInfoW(&vi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
455                                   VerSetConditionMask(VerSetConditionMask(VerSetConditionMask(0,
456                                           VER_MAJORVERSION, VER_GREATER_EQUAL),
457                                           VER_MINORVERSION, VER_GREATER_EQUAL),
458                                           VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL)))
459                 //IAudioClient::Initialize in Vista fails with E_INVALIDARG if this flag is set
460             {
461                 flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
462             }
463         }
464 
465         CoTaskMemFree(rwfx);
466     }
467 
468     ret = IAudioClient_Initialize(dev->aucl, share_mode, flags,
469                                   dev->buffer_duration_reftime, dev->periods_reftime, (WAVEFORMATEX *)&wfx, &GUID_NULL);
470 
471     if(FAILED(ret))
472     {
473         FLUID_LOG(FLUID_ERR, "wasapi: failed to initialize audio client. 0x%x", (unsigned)ret);
474 
475         if(ret == AUDCLNT_E_INVALID_DEVICE_PERIOD)
476         {
477             fluid_long_long_t defp, minp;
478             FLUID_LOG(FLUID_ERR, "wasapi: the device period size is invalid.");
479 
480             if(SUCCEEDED(IAudioClient_GetDevicePeriod(dev->aucl, &defp, &minp)))
481             {
482                 int defpf = (int)(defp / 1e7 * dev->sample_rate);
483                 int minpf = (int)(minp / 1e7 * dev->sample_rate);
484                 FLUID_LOG(FLUID_ERR, "wasapi: minimum period is %d, default period is %d. selected %d.", minpf, defpf, dev->period_size);
485             }
486         }
487 
488         goto cleanup;
489     }
490 
491     ret = IAudioClient_GetBufferSize(dev->aucl, &dev->nframes);
492 
493     if(FAILED(ret))
494     {
495         FLUID_LOG(FLUID_ERR, "wasapi: cannot get audio buffer size. 0x%x", (unsigned)ret);
496         goto cleanup;
497     }
498 
499     FLUID_LOG(FLUID_DBG, "wasapi: requested %d frames of buffers, got %u.", dev->periods * dev->period_size, dev->nframes);
500     dev->buffer_duration = dev->nframes / dev->sample_rate;
501     time_to_sleep = dev->buffer_duration * 1000 / 2;
502     if(time_to_sleep < 1)
503     {
504         time_to_sleep = 1;
505     }
506 
507     dev->drybuf = FLUID_ARRAY(float *, dev->audio_channels * 2);
508 
509     if(dev->drybuf == NULL)
510     {
511         FLUID_LOG(FLUID_ERR, "wasapi: out of memory");
512         goto cleanup;
513     }
514 
515     FLUID_MEMSET(dev->drybuf, 0, sizeof(float *) * dev->audio_channels * 2);
516 
517     for(i = 0; i < dev->audio_channels * 2; ++i)
518     {
519         dev->drybuf[i] = FLUID_ARRAY(float, dev->nframes);
520 
521         if(dev->drybuf[i] == NULL)
522         {
523             FLUID_LOG(FLUID_ERR, "wasapi: out of memory");
524             goto cleanup;
525         }
526     }
527 
528     ret = IAudioClient_GetService(dev->aucl, &_IID_IAudioRenderClient, (void **)&dev->arcl);
529 
530     if(FAILED(ret))
531     {
532         FLUID_LOG(FLUID_ERR, "wasapi: cannot get audio render device. 0x%x", (unsigned)ret);
533         goto cleanup;
534     }
535 
536     if(SUCCEEDED(IAudioClient_GetStreamLatency(dev->aucl, &dev->latency_reftime)))
537     {
538         FLUID_LOG(FLUID_DBG, "wasapi: latency: %fms.", dev->latency_reftime / 1e4);
539     }
540 
541     ret = IAudioClient_Start(dev->aucl);
542 
543     if(FAILED(ret))
544     {
545         FLUID_LOG(FLUID_ERR, "wasapi: failed to start audio client. 0x%x", (unsigned)ret);
546         goto cleanup;
547     }
548 
549     /* Signal the success of the driver initialization */
550     SetEvent(dev->start_ev);
551 
552     for(;;)
553     {
554         ret = IAudioClient_GetCurrentPadding(dev->aucl, &pos);
555 
556         if(FAILED(ret))
557         {
558             FLUID_LOG(FLUID_ERR, "wasapi: cannot get buffer padding. 0x%x", (unsigned)ret);
559             goto cleanup;
560         }
561 
562         len = dev->nframes - pos;
563 
564         if(len == 0)
565         {
566             Sleep(0);
567             continue;
568         }
569 
570         ret = IAudioRenderClient_GetBuffer(dev->arcl, len, &pbuf);
571 
572         if(FAILED(ret))
573         {
574             FLUID_LOG(FLUID_ERR, "wasapi: cannot get buffer. 0x%x", (unsigned)ret);
575             goto cleanup;
576         }
577 
578         channels_out[0] = channels_out[1] = (void *)pbuf;
579 
580         fluid_wasapi_write_processed_channels(dev, len, 2,
581                                               channels_out, channels_off, channels_incr);
582 
583         ret = IAudioRenderClient_ReleaseBuffer(dev->arcl, len, 0);
584 
585         if(FAILED(ret))
586         {
587             FLUID_LOG(FLUID_ERR, "wasapi: failed to release buffer. 0x%x", (unsigned)ret);
588             goto cleanup;
589         }
590 
591         if(WaitForSingleObject(dev->quit_ev, time_to_sleep) == WAIT_OBJECT_0)
592         {
593             break;
594         }
595     }
596 
597 cleanup:
598 
599     if(dev->aucl != NULL)
600     {
601         IAudioClient_Stop(dev->aucl);
602         IAudioClient_Release(dev->aucl);
603     }
604 
605     if(dev->arcl != NULL)
606     {
607         IAudioRenderClient_Release(dev->arcl);
608     }
609 
610     if(mmdev != NULL)
611     {
612         IMMDevice_Release(mmdev);
613     }
614 
615     if(denum != NULL)
616     {
617         IMMDeviceEnumerator_Release(denum);
618     }
619 
620     if(needs_com_uninit)
621     {
622         CoUninitialize();
623     }
624 
625     return 0;
626 }
627 
fluid_wasapi_write_processed_channels(void * data,int len,int channels_count,void * channels_out[],int channels_off[],int channels_incr[])628 static int fluid_wasapi_write_processed_channels(void *data, int len,
629         int channels_count,
630         void *channels_out[], int channels_off[],
631         int channels_incr[])
632 {
633     int i, ch;
634     int ret;
635     fluid_wasapi_audio_driver_t *drv = (fluid_wasapi_audio_driver_t *) data;
636     float *optr[FLUID_WASAPI_MAX_OUTPUTS * 2];
637     int16_t *ioptr[FLUID_WASAPI_MAX_OUTPUTS * 2];
638     int efx_nch = 0;
639     float **efx_buf = NULL;
640 
641     for(ch = 0; ch < drv->channels_count; ++ch)
642     {
643         FLUID_MEMSET(drv->drybuf[ch], 0, len * sizeof(float));
644         optr[ch] = (float *)channels_out[ch] + channels_off[ch];
645         ioptr[ch] = (int16_t *)channels_out[ch] + channels_off[ch];
646     }
647 
648     if(drv->func == (fluid_audio_func_t)fluid_synth_process)
649     {
650         efx_nch = drv->channels_count;
651         efx_buf = drv->drybuf;
652     }
653     ret = drv->func(drv->user_pointer, len, efx_nch, efx_buf, drv->channels_count, drv->drybuf);
654 
655     for(ch = 0; ch < drv->channels_count; ++ch)
656     {
657         for(i = 0; i < len; ++i)
658         {
659             if(drv->float_samples)
660             {
661                 *optr[ch] = drv->drybuf[ch][i];
662                 optr[ch] += channels_incr[ch];
663             }
664             else //This code is taken from fluid_synth.c. No dithering yet.
665             {
666                 *ioptr[ch] = round_clip_to_i16(drv->drybuf[ch][i] * 32766.0f);
667                 ioptr[ch] += channels_incr[ch];
668             }
669         }
670     }
671 
672     return ret;
673 }
674 
fluid_wasapi_foreach_device(fluid_wasapi_devenum_callback_t callback,void * data)675 static void fluid_wasapi_foreach_device(fluid_wasapi_devenum_callback_t callback, void *data)
676 {
677     IMMDeviceEnumerator *denum = NULL;
678     IMMDeviceCollection *dcoll = NULL;
679     UINT cnt, i;
680     HRESULT ret;
681     int com_was_initialized = FALSE;
682 
683     ret = CoInitializeEx(NULL, COINIT_MULTITHREADED);
684 
685     if(FAILED(ret))
686     {
687         if(ret == RPC_E_CHANGED_MODE)
688         {
689             com_was_initialized = TRUE;
690             FLUID_LOG(FLUID_DBG, "wasapi: COM was already initialized");
691         }
692         else
693         {
694             FLUID_LOG(FLUID_ERR, "wasapi: cannot initialize COM. 0x%x", (unsigned)ret);
695             return;
696         }
697     }
698 
699     ret = CoCreateInstance(
700               &_CLSID_MMDeviceEnumerator, NULL,
701               CLSCTX_ALL, &_IID_IMMDeviceEnumerator,
702               (void **)&denum);
703 
704     if(FAILED(ret))
705     {
706         FLUID_LOG(FLUID_ERR, "wasapi: cannot create device enumerator. 0x%x", (unsigned)ret);
707         goto cleanup;
708     }
709 
710     ret = IMMDeviceEnumerator_EnumAudioEndpoints(
711               denum, eRender,
712               DEVICE_STATE_ACTIVE, &dcoll);
713 
714     if(FAILED(ret))
715     {
716         FLUID_LOG(FLUID_ERR, "wasapi: cannot enumerate audio devices. 0x%x", (unsigned)ret);
717         goto cleanup;
718     }
719 
720     ret = IMMDeviceCollection_GetCount(dcoll, &cnt);
721 
722     if(FAILED(ret))
723     {
724         FLUID_LOG(FLUID_ERR, "wasapi: cannot get device count. 0x%x", (unsigned)ret);
725         goto cleanup;
726     }
727 
728     for(i = 0; i < cnt; ++i)
729     {
730         IMMDevice *dev = NULL;
731 
732         ret = IMMDeviceCollection_Item(dcoll, i, &dev);
733 
734         if(FAILED(ret))
735         {
736             FLUID_LOG(FLUID_ERR, "wasapi: cannot get device #%u. 0x%x", i, (unsigned)ret);
737             continue;
738         }
739 
740         callback(dev, data);
741 
742         IMMDevice_Release(dev);
743     }
744 
745 cleanup:
746 
747     if(dcoll != NULL)
748     {
749         IMMDeviceCollection_Release(dcoll);
750     }
751 
752     if(denum != NULL)
753     {
754         IMMDeviceEnumerator_Release(denum);
755     }
756 
757     if(!com_was_initialized)
758     {
759         CoUninitialize();
760     }
761 }
762 
fluid_wasapi_register_callback(IMMDevice * dev,void * data)763 static void fluid_wasapi_register_callback(IMMDevice *dev, void *data)
764 {
765     fluid_settings_t *settings = (fluid_settings_t *)data;
766     IPropertyStore *prop = NULL;
767     PROPVARIANT var;
768     int ret;
769 
770     ret = IMMDevice_OpenPropertyStore(dev, STGM_READ, &prop);
771 
772     if(FAILED(ret))
773     {
774         FLUID_LOG(FLUID_ERR, "wasapi: cannot get properties of device. 0x%x", (unsigned)ret);
775         return;
776     }
777 
778     PropVariantInit(&var);
779 
780     ret = IPropertyStore_GetValue(prop, &PKEY_Device_FriendlyName, &var);
781 
782     if(FAILED(ret))
783     {
784         FLUID_LOG(FLUID_ERR, "wasapi: cannot get friendly name of device. 0x%x", (unsigned)ret);
785     }
786     else
787     {
788         int nsz;
789         char *name;
790 
791         nsz = WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, 0, 0, 0, 0);
792         name = FLUID_ARRAY(char, nsz + 1);
793         WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, name, nsz, 0, 0);
794         fluid_settings_add_option(settings, "audio.wasapi.device", name);
795         FLUID_FREE(name);
796     }
797 
798     IPropertyStore_Release(prop);
799     PropVariantClear(&var);
800 }
801 
fluid_wasapi_finddev_callback(IMMDevice * dev,void * data)802 static void fluid_wasapi_finddev_callback(IMMDevice *dev, void *data)
803 {
804     fluid_wasapi_finddev_data_t *d = (fluid_wasapi_finddev_data_t *)data;
805     int nsz;
806     char *name = NULL;
807     wchar_t *id = NULL;
808     IPropertyStore *prop = NULL;
809     PROPVARIANT var;
810     HRESULT ret;
811 
812     ret = IMMDevice_OpenPropertyStore(dev, STGM_READ, &prop);
813 
814     if(FAILED(ret))
815     {
816         FLUID_LOG(FLUID_ERR, "wasapi: cannot get properties of device. 0x%x", (unsigned)ret);
817         return;
818     }
819 
820     PropVariantInit(&var);
821 
822     ret = IPropertyStore_GetValue(prop, &PKEY_Device_FriendlyName, &var);
823 
824     if(FAILED(ret))
825     {
826         FLUID_LOG(FLUID_ERR, "wasapi: cannot get friendly name of device. 0x%x", (unsigned)ret);
827         goto cleanup;
828     }
829 
830     nsz = WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, 0, 0, 0, 0);
831     name = FLUID_ARRAY(char, nsz + 1);
832     WideCharToMultiByte(CP_ACP, 0, var.pwszVal, -1, name, nsz, 0, 0);
833 
834     if(!FLUID_STRCASECMP(name, d->name))
835     {
836         ret = IMMDevice_GetId(dev, &id);
837 
838         if(FAILED(ret))
839         {
840             FLUID_LOG(FLUID_ERR, "wasapi: cannot get id of device. 0x%x", (unsigned)ret);
841             goto cleanup;
842         }
843 
844         nsz = wcslen(id);
845         d->id = FLUID_ARRAY(wchar_t, nsz + 1);
846         FLUID_MEMCPY(d->id, id, sizeof(wchar_t) * (nsz + 1));
847     }
848 
849 cleanup:
850     PropVariantClear(&var);
851     IPropertyStore_Release(prop);
852     CoTaskMemFree(id);
853     FLUID_FREE(name);
854 }
855 
fluid_wasapi_find_device(IMMDeviceEnumerator * denum,const char * name)856 static IMMDevice *fluid_wasapi_find_device(IMMDeviceEnumerator *denum, const char *name)
857 {
858     fluid_wasapi_finddev_data_t d;
859     IMMDevice *dev;
860     HRESULT ret;
861     d.name = name;
862     d.id = NULL;
863 
864     if(!FLUID_STRCASECMP(name, "default"))
865     {
866         ret = IMMDeviceEnumerator_GetDefaultAudioEndpoint(
867                   denum,
868                   eRender,
869                   eMultimedia,
870                   &dev);
871 
872         if(FAILED(ret))
873         {
874             FLUID_LOG(FLUID_ERR, "wasapi: cannot get default audio device. 0x%x", (unsigned)ret);
875             return NULL;
876         }
877         else
878         {
879             return dev;
880         }
881     }
882 
883     fluid_wasapi_foreach_device(fluid_wasapi_finddev_callback, &d);
884 
885     if(d.id != NULL)
886     {
887         ret = IMMDeviceEnumerator_GetDevice(denum, d.id, &dev);
888         FLUID_FREE(d.id);
889 
890         if(FAILED(ret))
891         {
892             FLUID_LOG(FLUID_ERR, "wasapi: cannot find device with id. 0x%x", (unsigned)ret);
893             return NULL;
894         }
895 
896         return dev;
897     }
898     else
899     {
900         FLUID_LOG(FLUID_ERR, "wasapi: cannot find device \"%s\".", name);
901         return NULL;
902     }
903 }
904 
905 static FLUID_INLINE int16_t
round_clip_to_i16(float x)906 round_clip_to_i16(float x)
907 {
908     long i;
909 
910     if(x >= 0.0f)
911     {
912         i = (long)(x + 0.5f);
913 
914         if(FLUID_UNLIKELY(i > 32767))
915         {
916             i = 32767;
917         }
918     }
919     else
920     {
921         i = (long)(x - 0.5f);
922 
923         if(FLUID_UNLIKELY(i < -32768))
924         {
925             i = -32768;
926         }
927     }
928 
929     return (int16_t)i;
930 }
931 #endif /* WASAPI_SUPPORT */
932 
933