xref: /illumos-kvm-cmd/audio/winwaveaudio.c (revision 68396ea9)
1 /* public domain */
2 
3 #include "qemu-common.h"
4 #include "sysemu.h"
5 #include "audio.h"
6 
7 #define AUDIO_CAP "winwave"
8 #include "audio_int.h"
9 
10 #include <windows.h>
11 #include <mmsystem.h>
12 
13 #include "audio_win_int.h"
14 
15 static struct {
16     int dac_headers;
17     int dac_samples;
18     int adc_headers;
19     int adc_samples;
20 } conf = {
21     .dac_headers = 4,
22     .dac_samples = 1024,
23     .adc_headers = 4,
24     .adc_samples = 1024
25 };
26 
27 typedef struct {
28     HWVoiceOut hw;
29     HWAVEOUT hwo;
30     WAVEHDR *hdrs;
31     HANDLE event;
32     void *pcm_buf;
33     int avail;
34     int pending;
35     int curhdr;
36     int paused;
37     CRITICAL_SECTION crit_sect;
38 } WaveVoiceOut;
39 
40 typedef struct {
41     HWVoiceIn hw;
42     HWAVEIN hwi;
43     WAVEHDR *hdrs;
44     HANDLE event;
45     void *pcm_buf;
46     int curhdr;
47     int paused;
48     int rpos;
49     int avail;
50     CRITICAL_SECTION crit_sect;
51 } WaveVoiceIn;
52 
winwave_log_mmresult(MMRESULT mr)53 static void winwave_log_mmresult (MMRESULT mr)
54 {
55     const char *str = "BUG";
56 
57     switch (mr) {
58     case MMSYSERR_NOERROR:
59         str = "Success";
60         break;
61 
62     case MMSYSERR_INVALHANDLE:
63         str = "Specified device handle is invalid";
64         break;
65 
66     case MMSYSERR_BADDEVICEID:
67         str = "Specified device id is out of range";
68         break;
69 
70     case MMSYSERR_NODRIVER:
71         str = "No device driver is present";
72         break;
73 
74     case MMSYSERR_NOMEM:
75         str = "Unable to allocate or locl memory";
76         break;
77 
78     case WAVERR_SYNC:
79         str = "Device is synchronous but waveOutOpen was called "
80             "without using the WINWAVE_ALLOWSYNC flag";
81         break;
82 
83     case WAVERR_UNPREPARED:
84         str = "The data block pointed to by the pwh parameter "
85             "hasn't been prepared";
86         break;
87 
88     case WAVERR_STILLPLAYING:
89         str = "There are still buffers in the queue";
90         break;
91 
92     default:
93         dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
94         return;
95     }
96 
97     dolog ("Reason: %s\n", str);
98 }
99 
winwave_logerr(MMRESULT mr,const char * fmt,...)100 static void GCC_FMT_ATTR (2, 3) winwave_logerr (
101     MMRESULT mr,
102     const char *fmt,
103     ...
104     )
105 {
106     va_list ap;
107 
108     va_start (ap, fmt);
109     AUD_vlog (AUDIO_CAP, fmt, ap);
110     va_end (ap);
111 
112     AUD_log (NULL, " failed\n");
113     winwave_log_mmresult (mr);
114 }
115 
winwave_anal_close_out(WaveVoiceOut * wave)116 static void winwave_anal_close_out (WaveVoiceOut *wave)
117 {
118     MMRESULT mr;
119 
120     mr = waveOutClose (wave->hwo);
121     if (mr != MMSYSERR_NOERROR) {
122         winwave_logerr (mr, "waveOutClose");
123     }
124     wave->hwo = NULL;
125 }
126 
winwave_callback_out(HWAVEOUT hwo,UINT msg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)127 static void CALLBACK winwave_callback_out (
128     HWAVEOUT hwo,
129     UINT msg,
130     DWORD_PTR dwInstance,
131     DWORD_PTR dwParam1,
132     DWORD_PTR dwParam2
133     )
134 {
135     WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance;
136 
137     switch (msg) {
138     case WOM_DONE:
139         {
140             WAVEHDR *h = (WAVEHDR *) dwParam1;
141             if (!h->dwUser) {
142                 h->dwUser = 1;
143                 EnterCriticalSection (&wave->crit_sect);
144                 {
145                     wave->avail += conf.dac_samples;
146                 }
147                 LeaveCriticalSection (&wave->crit_sect);
148                 if (wave->hw.poll_mode) {
149                     if (!SetEvent (wave->event)) {
150                         dolog ("DAC SetEvent failed %lx\n", GetLastError ());
151                     }
152                 }
153             }
154         }
155         break;
156 
157     case WOM_CLOSE:
158     case WOM_OPEN:
159         break;
160 
161     default:
162         dolog ("unknown wave out callback msg %x\n", msg);
163     }
164 }
165 
winwave_init_out(HWVoiceOut * hw,struct audsettings * as)166 static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
167 {
168     int i;
169     int err;
170     MMRESULT mr;
171     WAVEFORMATEX wfx;
172     WaveVoiceOut *wave;
173 
174     wave = (WaveVoiceOut *) hw;
175 
176     InitializeCriticalSection (&wave->crit_sect);
177 
178     err = waveformat_from_audio_settings (&wfx, as);
179     if (err) {
180         goto err0;
181     }
182 
183     mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
184                       (DWORD_PTR) winwave_callback_out,
185                       (DWORD_PTR) wave, CALLBACK_FUNCTION);
186     if (mr != MMSYSERR_NOERROR) {
187         winwave_logerr (mr, "waveOutOpen");
188         goto err1;
189     }
190 
191     wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
192                                sizeof (*wave->hdrs));
193     if (!wave->hdrs) {
194         goto err2;
195     }
196 
197     audio_pcm_init_info (&hw->info, as);
198     hw->samples = conf.dac_samples * conf.dac_headers;
199     wave->avail = hw->samples;
200 
201     wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples,
202                                   conf.dac_headers << hw->info.shift);
203     if (!wave->pcm_buf) {
204         goto err3;
205     }
206 
207     for (i = 0; i < conf.dac_headers; ++i) {
208         WAVEHDR *h = &wave->hdrs[i];
209 
210         h->dwUser = 0;
211         h->dwBufferLength = conf.dac_samples << hw->info.shift;
212         h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
213         h->dwFlags = 0;
214 
215         mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
216         if (mr != MMSYSERR_NOERROR) {
217             winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
218             goto err4;
219         }
220     }
221 
222     return 0;
223 
224  err4:
225     qemu_free (wave->pcm_buf);
226  err3:
227     qemu_free (wave->hdrs);
228  err2:
229     winwave_anal_close_out (wave);
230  err1:
231  err0:
232     return -1;
233 }
234 
winwave_write(SWVoiceOut * sw,void * buf,int len)235 static int winwave_write (SWVoiceOut *sw, void *buf, int len)
236 {
237     return audio_pcm_sw_write (sw, buf, len);
238 }
239 
winwave_run_out(HWVoiceOut * hw,int live)240 static int winwave_run_out (HWVoiceOut *hw, int live)
241 {
242     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
243     int decr;
244     int doreset;
245 
246     EnterCriticalSection (&wave->crit_sect);
247     {
248         decr = audio_MIN (live, wave->avail);
249         decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending);
250         wave->pending += decr;
251         wave->avail -= decr;
252     }
253     LeaveCriticalSection (&wave->crit_sect);
254 
255     doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
256     if (doreset && !ResetEvent (wave->event)) {
257         dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
258     }
259 
260     while (wave->pending >= conf.dac_samples) {
261         MMRESULT mr;
262         WAVEHDR *h = &wave->hdrs[wave->curhdr];
263 
264         h->dwUser = 0;
265         mr = waveOutWrite (wave->hwo, h, sizeof (*h));
266         if (mr != MMSYSERR_NOERROR) {
267             winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
268             break;
269         }
270 
271         wave->pending -= conf.dac_samples;
272         wave->curhdr = (wave->curhdr + 1) % conf.dac_headers;
273     }
274 
275     return decr;
276 }
277 
winwave_poll(void * opaque)278 static void winwave_poll (void *opaque)
279 {
280     (void) opaque;
281     audio_run ("winwave_poll");
282 }
283 
winwave_fini_out(HWVoiceOut * hw)284 static void winwave_fini_out (HWVoiceOut *hw)
285 {
286     int i;
287     MMRESULT mr;
288     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
289 
290     mr = waveOutReset (wave->hwo);
291     if (mr != MMSYSERR_NOERROR) {
292         winwave_logerr (mr, "waveOutReset");
293     }
294 
295     for (i = 0; i < conf.dac_headers; ++i) {
296         mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
297                                      sizeof (wave->hdrs[i]));
298         if (mr != MMSYSERR_NOERROR) {
299             winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
300         }
301     }
302 
303     winwave_anal_close_out (wave);
304 
305     if (wave->event) {
306         qemu_del_wait_object (wave->event, winwave_poll, wave);
307         if (!CloseHandle (wave->event)) {
308             dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
309         }
310         wave->event = NULL;
311     }
312 
313     qemu_free (wave->pcm_buf);
314     wave->pcm_buf = NULL;
315 
316     qemu_free (wave->hdrs);
317     wave->hdrs = NULL;
318 }
319 
winwave_ctl_out(HWVoiceOut * hw,int cmd,...)320 static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
321 {
322     MMRESULT mr;
323     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
324 
325     switch (cmd) {
326     case VOICE_ENABLE:
327         {
328             va_list ap;
329             int poll_mode;
330 
331             va_start (ap, cmd);
332             poll_mode = va_arg (ap, int);
333             va_end (ap);
334 
335             if (poll_mode && !wave->event) {
336                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
337                 if (!wave->event) {
338                     dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
339                            GetLastError ());
340                 }
341             }
342 
343             if (wave->event) {
344                 int ret;
345 
346                 ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
347                 hw->poll_mode = (ret == 0);
348             }
349             else {
350                 hw->poll_mode = 0;
351             }
352             if (wave->paused) {
353                 mr = waveOutRestart (wave->hwo);
354                 if (mr != MMSYSERR_NOERROR) {
355                     winwave_logerr (mr, "waveOutRestart");
356                 }
357                 wave->paused = 0;
358             }
359         }
360         return 0;
361 
362     case VOICE_DISABLE:
363         if (!wave->paused) {
364             mr = waveOutPause (wave->hwo);
365             if (mr != MMSYSERR_NOERROR) {
366                 winwave_logerr (mr, "waveOutPause");
367             }
368             else {
369                 wave->paused = 1;
370             }
371         }
372         if (wave->event) {
373             qemu_del_wait_object (wave->event, winwave_poll, wave);
374         }
375         return 0;
376     }
377     return -1;
378 }
379 
winwave_anal_close_in(WaveVoiceIn * wave)380 static void winwave_anal_close_in (WaveVoiceIn *wave)
381 {
382     MMRESULT mr;
383 
384     mr = waveInClose (wave->hwi);
385     if (mr != MMSYSERR_NOERROR) {
386         winwave_logerr (mr, "waveInClose");
387     }
388     wave->hwi = NULL;
389 }
390 
winwave_callback_in(HWAVEIN * hwi,UINT msg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)391 static void CALLBACK winwave_callback_in (
392     HWAVEIN *hwi,
393     UINT msg,
394     DWORD_PTR dwInstance,
395     DWORD_PTR dwParam1,
396     DWORD_PTR dwParam2
397     )
398 {
399     WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
400 
401     switch (msg) {
402     case WIM_DATA:
403         {
404             WAVEHDR *h = (WAVEHDR *) dwParam1;
405             if (!h->dwUser) {
406                 h->dwUser = 1;
407                 EnterCriticalSection (&wave->crit_sect);
408                 {
409                     wave->avail += conf.adc_samples;
410                 }
411                 LeaveCriticalSection (&wave->crit_sect);
412                 if (wave->hw.poll_mode) {
413                     if (!SetEvent (wave->event)) {
414                         dolog ("ADC SetEvent failed %lx\n", GetLastError ());
415                     }
416                 }
417             }
418         }
419         break;
420 
421     case WIM_CLOSE:
422     case WIM_OPEN:
423         break;
424 
425     default:
426         dolog ("unknown wave in callback msg %x\n", msg);
427     }
428 }
429 
winwave_add_buffers(WaveVoiceIn * wave,int samples)430 static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
431 {
432     int doreset;
433 
434     doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
435     if (doreset && !ResetEvent (wave->event)) {
436         dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
437     }
438 
439     while (samples >= conf.adc_samples) {
440         MMRESULT mr;
441         WAVEHDR *h = &wave->hdrs[wave->curhdr];
442 
443         h->dwUser = 0;
444         mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
445         if (mr != MMSYSERR_NOERROR) {
446             winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
447         }
448         wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
449         samples -= conf.adc_samples;
450     }
451 }
452 
winwave_init_in(HWVoiceIn * hw,struct audsettings * as)453 static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
454 {
455     int i;
456     int err;
457     MMRESULT mr;
458     WAVEFORMATEX wfx;
459     WaveVoiceIn *wave;
460 
461     wave = (WaveVoiceIn *) hw;
462 
463     InitializeCriticalSection (&wave->crit_sect);
464 
465     err = waveformat_from_audio_settings (&wfx, as);
466     if (err) {
467         goto err0;
468     }
469 
470     mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
471                      (DWORD_PTR) winwave_callback_in,
472                      (DWORD_PTR) wave, CALLBACK_FUNCTION);
473     if (mr != MMSYSERR_NOERROR) {
474         winwave_logerr (mr, "waveInOpen");
475         goto err1;
476     }
477 
478     wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
479                                sizeof (*wave->hdrs));
480     if (!wave->hdrs) {
481         goto err2;
482     }
483 
484     audio_pcm_init_info (&hw->info, as);
485     hw->samples = conf.adc_samples * conf.adc_headers;
486     wave->avail = 0;
487 
488     wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
489                                   conf.adc_headers << hw->info.shift);
490     if (!wave->pcm_buf) {
491         goto err3;
492     }
493 
494     for (i = 0; i < conf.adc_headers; ++i) {
495         WAVEHDR *h = &wave->hdrs[i];
496 
497         h->dwUser = 0;
498         h->dwBufferLength = conf.adc_samples << hw->info.shift;
499         h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
500         h->dwFlags = 0;
501 
502         mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
503         if (mr != MMSYSERR_NOERROR) {
504             winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
505             goto err4;
506         }
507     }
508 
509     wave->paused = 1;
510     winwave_add_buffers (wave, hw->samples);
511     return 0;
512 
513  err4:
514     qemu_free (wave->pcm_buf);
515  err3:
516     qemu_free (wave->hdrs);
517  err2:
518     winwave_anal_close_in (wave);
519  err1:
520  err0:
521     return -1;
522 }
523 
winwave_fini_in(HWVoiceIn * hw)524 static void winwave_fini_in (HWVoiceIn *hw)
525 {
526     int i;
527     MMRESULT mr;
528     WaveVoiceIn *wave = (WaveVoiceIn *) hw;
529 
530     mr = waveInReset (wave->hwi);
531     if (mr != MMSYSERR_NOERROR) {
532         winwave_logerr (mr, "waveInReset");
533     }
534 
535     for (i = 0; i < conf.adc_headers; ++i) {
536         mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
537                                      sizeof (wave->hdrs[i]));
538         if (mr != MMSYSERR_NOERROR) {
539             winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
540         }
541     }
542 
543     winwave_anal_close_in (wave);
544 
545     if (wave->event) {
546         qemu_del_wait_object (wave->event, winwave_poll, wave);
547         if (!CloseHandle (wave->event)) {
548             dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
549         }
550         wave->event = NULL;
551     }
552 
553     qemu_free (wave->pcm_buf);
554     wave->pcm_buf = NULL;
555 
556     qemu_free (wave->hdrs);
557     wave->hdrs = NULL;
558 }
559 
winwave_run_in(HWVoiceIn * hw)560 static int winwave_run_in (HWVoiceIn *hw)
561 {
562     WaveVoiceIn *wave = (WaveVoiceIn *) hw;
563     int live = audio_pcm_hw_get_live_in (hw);
564     int dead = hw->samples - live;
565     int decr, ret;
566 
567     if (!dead) {
568         return 0;
569     }
570 
571     EnterCriticalSection (&wave->crit_sect);
572     {
573         decr = audio_MIN (dead, wave->avail);
574         wave->avail -= decr;
575     }
576     LeaveCriticalSection (&wave->crit_sect);
577 
578     ret = decr;
579     while (decr) {
580         int left = hw->samples - hw->wpos;
581         int conv = audio_MIN (left, decr);
582         hw->conv (hw->conv_buf + hw->wpos,
583                   advance (wave->pcm_buf, wave->rpos << hw->info.shift),
584                   conv);
585 
586         wave->rpos = (wave->rpos + conv) % hw->samples;
587         hw->wpos = (hw->wpos + conv) % hw->samples;
588         decr -= conv;
589     }
590 
591     winwave_add_buffers (wave, ret);
592     return ret;
593 }
594 
winwave_read(SWVoiceIn * sw,void * buf,int size)595 static int winwave_read (SWVoiceIn *sw, void *buf, int size)
596 {
597     return audio_pcm_sw_read (sw, buf, size);
598 }
599 
winwave_ctl_in(HWVoiceIn * hw,int cmd,...)600 static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
601 {
602     MMRESULT mr;
603     WaveVoiceIn *wave = (WaveVoiceIn *) hw;
604 
605     switch (cmd) {
606     case VOICE_ENABLE:
607         {
608             va_list ap;
609             int poll_mode;
610 
611             va_start (ap, cmd);
612             poll_mode = va_arg (ap, int);
613             va_end (ap);
614 
615             if (poll_mode && !wave->event) {
616                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
617                 if (!wave->event) {
618                     dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
619                            GetLastError ());
620                 }
621             }
622 
623             if (wave->event) {
624                 int ret;
625 
626                 ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
627                 hw->poll_mode = (ret == 0);
628             }
629             else {
630                 hw->poll_mode = 0;
631             }
632             if (wave->paused) {
633                 mr = waveInStart (wave->hwi);
634                 if (mr != MMSYSERR_NOERROR) {
635                     winwave_logerr (mr, "waveInStart");
636                 }
637                 wave->paused = 0;
638             }
639         }
640         return 0;
641 
642     case VOICE_DISABLE:
643         if (!wave->paused) {
644             mr = waveInStop (wave->hwi);
645             if (mr != MMSYSERR_NOERROR) {
646                 winwave_logerr (mr, "waveInStop");
647             }
648             else {
649                 wave->paused = 1;
650             }
651         }
652         if (wave->event) {
653             qemu_del_wait_object (wave->event, winwave_poll, wave);
654         }
655         return 0;
656     }
657     return 0;
658 }
659 
winwave_audio_init(void)660 static void *winwave_audio_init (void)
661 {
662     return &conf;
663 }
664 
winwave_audio_fini(void * opaque)665 static void winwave_audio_fini (void *opaque)
666 {
667     (void) opaque;
668 }
669 
670 static struct audio_option winwave_options[] = {
671     {
672         .name        = "DAC_HEADERS",
673         .tag         = AUD_OPT_INT,
674         .valp        = &conf.dac_headers,
675         .descr       = "DAC number of headers",
676     },
677     {
678         .name        = "DAC_SAMPLES",
679         .tag         = AUD_OPT_INT,
680         .valp        = &conf.dac_samples,
681         .descr       = "DAC number of samples per header",
682     },
683     {
684         .name        = "ADC_HEADERS",
685         .tag         = AUD_OPT_INT,
686         .valp        = &conf.adc_headers,
687         .descr       = "ADC number of headers",
688     },
689     {
690         .name        = "ADC_SAMPLES",
691         .tag         = AUD_OPT_INT,
692         .valp        = &conf.adc_samples,
693         .descr       = "ADC number of samples per header",
694     },
695     { /* End of list */ }
696 };
697 
698 static struct audio_pcm_ops winwave_pcm_ops = {
699     .init_out = winwave_init_out,
700     .fini_out = winwave_fini_out,
701     .run_out  = winwave_run_out,
702     .write    = winwave_write,
703     .ctl_out  = winwave_ctl_out,
704     .init_in  = winwave_init_in,
705     .fini_in  = winwave_fini_in,
706     .run_in   = winwave_run_in,
707     .read     = winwave_read,
708     .ctl_in   = winwave_ctl_in
709 };
710 
711 struct audio_driver winwave_audio_driver = {
712     .name           = "winwave",
713     .descr          = "Windows Waveform Audio http://msdn.microsoft.com",
714     .options        = winwave_options,
715     .init           = winwave_audio_init,
716     .fini           = winwave_audio_fini,
717     .pcm_ops        = &winwave_pcm_ops,
718     .can_be_default = 1,
719     .max_voices_out = INT_MAX,
720     .max_voices_in  = INT_MAX,
721     .voice_size_out = sizeof (WaveVoiceOut),
722     .voice_size_in  = sizeof (WaveVoiceIn)
723 };
724