VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostALSAAudio.cpp@ 63711

Last change on this file since 63711 was 63711, checked in by vboxsync, 8 years ago

Audio: Implemented support for audio device enumeration handling, audio device information and audio backend notifications. This will enable to let the backends tell the audio subsystem that the host audio configuration has changed and react accordingly to it. For now only the Core Audio backend supports device enumeration. Further this also will get rid of the static initialization on the device emulation side, which, if at VM startup no audio input(s) / output(s) were available, was triggering a warning. The NULL backend therefore does not need to act as a (static) fallback anymore.

Work in progress.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 54.8 KB
Line 
1/* $Id: DrvHostALSAAudio.cpp 63711 2016-09-05 12:04:01Z vboxsync $ */
2/** @file
3 * VBox audio devices: ALSA audio driver.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on: alsaaudio.c
19 *
20 * QEMU ALSA audio driver
21 *
22 * Copyright (c) 2005 Vassili Karpov (malc)
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43
44/*********************************************************************************************************************************
45* Header Files *
46*********************************************************************************************************************************/
47#define LOG_GROUP LOG_GROUP_DRV_HOST_AUDIO
48#include <VBox/log.h>
49#include <iprt/alloc.h>
50#include <iprt/uuid.h> /* For PDMIBASE_2_PDMDRV. */
51#include <VBox/vmm/pdmaudioifs.h>
52
53RT_C_DECLS_BEGIN
54 #include "alsa_stubs.h"
55 #include "alsa_mangling.h"
56RT_C_DECLS_END
57
58#include <alsa/asoundlib.h>
59#include <alsa/control.h> /* For device enumeration. */
60
61#include "DrvAudio.h"
62#include "AudioMixBuffer.h"
63
64#include "VBoxDD.h"
65
66
67/*********************************************************************************************************************************
68* Defines *
69*********************************************************************************************************************************/
70
71/** Makes DRVHOSTALSAAUDIO out of PDMIHOSTAUDIO. */
72#define PDMIHOSTAUDIO_2_DRVHOSTALSAAUDIO(pInterface) \
73 ( (PDRVHOSTALSAAUDIO)((uintptr_t)pInterface - RT_OFFSETOF(DRVHOSTALSAAUDIO, IHostAudio)) )
74
75
76/*********************************************************************************************************************************
77* Structures *
78*********************************************************************************************************************************/
79
80typedef struct ALSAAUDIOSTREAMIN
81{
82 /** Associated host input stream.
83 * Note: Always must come first! */
84 PDMAUDIOSTREAM Stream;
85 snd_pcm_t *phPCM;
86 void *pvBuf;
87 size_t cbBuf;
88} ALSAAUDIOSTREAMIN, *PALSAAUDIOSTREAMIN;
89
90typedef struct ALSAAUDIOSTREAMOUT
91{
92 /** Associated host output stream.
93 * Note: Always must come first! */
94 PDMAUDIOSTREAM Stream;
95 snd_pcm_t *phPCM;
96 void *pvBuf;
97 size_t cbBuf;
98 /** Minimum samples required for ALSA to play data. */
99 uint32_t cSamplesMin;
100} ALSAAUDIOSTREAMOUT, *PALSAAUDIOSTREAMOUT;
101
102/* latency = period_size * periods / (rate * bytes_per_frame) */
103
104typedef struct ALSAAUDIOCFG
105{
106 int size_in_usec_in;
107 int size_in_usec_out;
108 const char *pcm_name_in;
109 const char *pcm_name_out;
110 unsigned int buffer_size_in;
111 unsigned int period_size_in;
112 unsigned int buffer_size_out;
113 unsigned int period_size_out;
114 unsigned int threshold;
115
116 int buffer_size_in_overriden;
117 int period_size_in_overriden;
118
119 int buffer_size_out_overriden;
120 int period_size_out_overriden;
121
122} ALSAAUDIOCFG, *PALSAAUDIOCFG;
123
124static int alsaStreamRecover(snd_pcm_t *phPCM);
125
126static ALSAAUDIOCFG s_ALSAConf =
127{
128#ifdef HIGH_LATENCY
129 1,
130 1,
131#else
132 0,
133 0,
134#endif
135 "default",
136 "default",
137#ifdef HIGH_LATENCY
138 400000,
139 400000 / 4,
140 400000,
141 400000 / 4,
142#else
143# define DEFAULT_BUFFER_SIZE 1024
144# define DEFAULT_PERIOD_SIZE 256
145 DEFAULT_BUFFER_SIZE * 4,
146 DEFAULT_PERIOD_SIZE * 4,
147 DEFAULT_BUFFER_SIZE,
148 DEFAULT_PERIOD_SIZE,
149#endif
150 0,
151 0,
152 0,
153 0,
154 0
155};
156
157/**
158 * Host Alsa audio driver instance data.
159 * @implements PDMIAUDIOCONNECTOR
160 */
161typedef struct DRVHOSTALSAAUDIO
162{
163 /** Pointer to the driver instance structure. */
164 PPDMDRVINS pDrvIns;
165 /** Pointer to host audio interface. */
166 PDMIHOSTAUDIO IHostAudio;
167 /** Error count for not flooding the release log.
168 * UINT32_MAX for unlimited logging. */
169 uint32_t cLogErrors;
170} DRVHOSTALSAAUDIO, *PDRVHOSTALSAAUDIO;
171
172/** Maximum number of tries to recover a broken pipe. */
173#define ALSA_RECOVERY_TRIES_MAX 5
174
175typedef struct ALSAAUDIOSTREAMCFG
176{
177 unsigned int freq;
178 /** PCM sound format. */
179 snd_pcm_format_t fmt;
180 /** PCM data access type. */
181 snd_pcm_access_t access;
182 /** Whether resampling should be performed by alsalib or not. */
183 int resample;
184 int nchannels;
185 unsigned long buffer_size;
186 unsigned long period_size;
187 snd_pcm_uframes_t samples;
188} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
189
190
191
192static snd_pcm_format_t alsaAudioFmtToALSA(PDMAUDIOFMT fmt)
193{
194 switch (fmt)
195 {
196 case PDMAUDIOFMT_S8:
197 return SND_PCM_FORMAT_S8;
198
199 case PDMAUDIOFMT_U8:
200 return SND_PCM_FORMAT_U8;
201
202 case PDMAUDIOFMT_S16:
203 return SND_PCM_FORMAT_S16_LE;
204
205 case PDMAUDIOFMT_U16:
206 return SND_PCM_FORMAT_U16_LE;
207
208 case PDMAUDIOFMT_S32:
209 return SND_PCM_FORMAT_S32_LE;
210
211 case PDMAUDIOFMT_U32:
212 return SND_PCM_FORMAT_U32_LE;
213
214 default:
215 break;
216 }
217
218 AssertMsgFailed(("Format %ld not supported\n", fmt));
219 return SND_PCM_FORMAT_U8;
220}
221
222
223static int alsaALSAToAudioFmt(snd_pcm_format_t fmt,
224 PDMAUDIOFMT *pFmt, PDMAUDIOENDIANNESS *pEndianness)
225{
226 AssertPtrReturn(pFmt, VERR_INVALID_POINTER);
227 /* pEndianness is optional. */
228
229 switch (fmt)
230 {
231 case SND_PCM_FORMAT_S8:
232 *pFmt = PDMAUDIOFMT_S8;
233 if (pEndianness)
234 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
235 break;
236
237 case SND_PCM_FORMAT_U8:
238 *pFmt = PDMAUDIOFMT_U8;
239 if (pEndianness)
240 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
241 break;
242
243 case SND_PCM_FORMAT_S16_LE:
244 *pFmt = PDMAUDIOFMT_S16;
245 if (pEndianness)
246 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
247 break;
248
249 case SND_PCM_FORMAT_U16_LE:
250 *pFmt = PDMAUDIOFMT_U16;
251 if (pEndianness)
252 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
253 break;
254
255 case SND_PCM_FORMAT_S16_BE:
256 *pFmt = PDMAUDIOFMT_S16;
257 if (pEndianness)
258 *pEndianness = PDMAUDIOENDIANNESS_BIG;
259 break;
260
261 case SND_PCM_FORMAT_U16_BE:
262 *pFmt = PDMAUDIOFMT_U16;
263 if (pEndianness)
264 *pEndianness = PDMAUDIOENDIANNESS_BIG;
265 break;
266
267 case SND_PCM_FORMAT_S32_LE:
268 *pFmt = PDMAUDIOFMT_S32;
269 if (pEndianness)
270 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
271 break;
272
273 case SND_PCM_FORMAT_U32_LE:
274 *pFmt = PDMAUDIOFMT_U32;
275 if (pEndianness)
276 *pEndianness = PDMAUDIOENDIANNESS_LITTLE;
277 break;
278
279 case SND_PCM_FORMAT_S32_BE:
280 *pFmt = PDMAUDIOFMT_S32;
281 if (pEndianness)
282 *pEndianness = PDMAUDIOENDIANNESS_BIG;
283 break;
284
285 case SND_PCM_FORMAT_U32_BE:
286 *pFmt = PDMAUDIOFMT_U32;
287 if (pEndianness)
288 *pEndianness = PDMAUDIOENDIANNESS_BIG;
289 break;
290
291 default:
292 AssertMsgFailed(("Format %ld not supported\n", fmt));
293 return VERR_NOT_SUPPORTED;
294 }
295
296 return VINF_SUCCESS;
297}
298
299
300static int alsaGetSampleShift(snd_pcm_format_t fmt, unsigned *puShift)
301{
302 AssertPtrReturn(puShift, VERR_INVALID_POINTER);
303
304 switch (fmt)
305 {
306 case SND_PCM_FORMAT_S8:
307 case SND_PCM_FORMAT_U8:
308 *puShift = 0;
309 break;
310
311 case SND_PCM_FORMAT_S16_LE:
312 case SND_PCM_FORMAT_U16_LE:
313 case SND_PCM_FORMAT_S16_BE:
314 case SND_PCM_FORMAT_U16_BE:
315 *puShift = 1;
316 break;
317
318 case SND_PCM_FORMAT_S32_LE:
319 case SND_PCM_FORMAT_U32_LE:
320 case SND_PCM_FORMAT_S32_BE:
321 case SND_PCM_FORMAT_U32_BE:
322 *puShift = 2;
323 break;
324
325 default:
326 AssertMsgFailed(("Format %ld not supported\n", fmt));
327 return VERR_NOT_SUPPORTED;
328 }
329
330 return VINF_SUCCESS;
331}
332
333
334static int alsaStreamSetThreshold(snd_pcm_t *phPCM, snd_pcm_uframes_t threshold)
335{
336 snd_pcm_sw_params_t *pSWParms = NULL;
337 snd_pcm_sw_params_alloca(&pSWParms);
338 if (!pSWParms)
339 return VERR_NO_MEMORY;
340
341 int rc;
342 do
343 {
344 int err = snd_pcm_sw_params_current(phPCM, pSWParms);
345 if (err < 0)
346 {
347 LogRel(("ALSA: Failed to get current software parameters for threshold: %s\n",
348 snd_strerror(err)));
349 rc = VERR_ACCESS_DENIED;
350 break;
351 }
352
353 err = snd_pcm_sw_params_set_start_threshold(phPCM, pSWParms, threshold);
354 if (err < 0)
355 {
356 LogRel(("ALSA: Failed to set software threshold to %ld: %s\n",
357 threshold, snd_strerror(err)));
358 rc = VERR_ACCESS_DENIED;
359 break;
360 }
361
362 err = snd_pcm_sw_params_set_avail_min(phPCM, pSWParms, 512);
363 if (err < 0)
364 {
365 LogRel(("ALSA: Failed to set available minimum to %ld: %s\n",
366 threshold, snd_strerror(err)));
367 rc = VERR_ACCESS_DENIED;
368 break;
369 }
370
371 err = snd_pcm_sw_params(phPCM, pSWParms);
372 if (err < 0)
373 {
374 LogRel(("ALSA: Failed to set new software parameters for threshold: %s\n",
375 snd_strerror(err)));
376 rc = VERR_ACCESS_DENIED;
377 break;
378 }
379
380 LogFlowFunc(("Setting threshold to %RU32\n", threshold));
381 rc = VINF_SUCCESS;
382 }
383 while (0);
384
385 return rc;
386}
387
388
389static int alsaStreamClose(snd_pcm_t **pphPCM)
390{
391 if (!pphPCM || !*pphPCM)
392 return VINF_SUCCESS;
393
394 int rc;
395 int rc2 = snd_pcm_close(*pphPCM);
396 if (rc2)
397 {
398 LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2)));
399 rc = VERR_GENERAL_FAILURE; /** @todo */
400 }
401 else
402 {
403 *pphPCM = NULL;
404 rc = VINF_SUCCESS;
405 }
406
407 return rc;
408}
409
410
411#if 0 /* After Beta. */
412static int alsaSetHWParams(snd_pcm_t *phPCM, PALSAAUDIOSTREAMCFG pCfg)
413{
414 int rc;
415 snd_pcm_hw_params_t *pParams = NULL;
416
417 do
418 {
419 snd_pcm_hw_params_alloca(&pParams);
420 if (!pParams)
421 {
422 rc = VERR_NO_MEMORY;
423 break;
424 }
425
426 unsigned int rrate;
427 snd_pcm_uframes_t size;
428 int dir;
429
430 /* choose all parameters */
431 int err = snd_pcm_hw_params_any(phPCM, pParams);
432 if (err < 0)
433 {
434 LogRel(("ALSA: Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)));
435 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
436 break;
437 }
438 /* set hardware resampling */
439 err = snd_pcm_hw_params_set_rate_resample(phPCM, pParams, pCfg->resample);
440 if (err < 0)
441 {
442 LogRel(("ALSA: Resampling setup failed for playback: %s\n", snd_strerror(err)));
443 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
444 break;
445 }
446 /* set the interleaved read/write format */
447 err = snd_pcm_hw_params_set_access(phPCM, pParams, pCfg->access);
448 if (err < 0)
449 {
450 LogRel(("ALSA: Access type not available for playback: %s\n", snd_strerror(err)));
451 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
452 break;
453 }
454 /* set the sample format */
455 err = snd_pcm_hw_params_set_format(phPCM, pParams, pCfg->fmt);
456 if (err < 0)
457 {
458 LogRel(("ALSA: Sample format not available for playback: %s\n", snd_strerror(err)));
459 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
460 break;
461 }
462 /* set the count of channels */
463 err = snd_pcm_hw_params_set_channels(phPCM, pParams, pCfg->nchannels);
464 if (err < 0)
465 {
466 LogRel(("ALSA: Channels count (%d) not available for playbacks: %s\n", pCfg->nchannels, snd_strerror(err)));
467 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
468 break;
469 }
470 /* set the stream rate */
471 rrate = pCfg->freq;
472 err = snd_pcm_hw_params_set_rate_near(phPCM, pParams, &rrate, 0);
473 if (err < 0)
474 {
475 LogRel(("ALSA: Rate %uHz not available for playback: %s\n", pCfg->freq, snd_strerror(err)));
476 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
477 break;
478 }
479 if (rrate != pCfg->freq)
480 {
481 LogRel(("ALSA: Rate doesn't match (requested %iHz, get %uHz)\n", pCfg->freq, err));
482 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
483 break;
484 }
485 /* set the buffer time */
486 err = snd_pcm_hw_params_set_buffer_time_near(phPCM, pParams, &pCfg->buffer_time, &dir);
487 if (err < 0)
488 {
489 LogRel(("ALSA: Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)));
490 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
491 break;
492 }
493 err = snd_pcm_hw_params_get_buffer_size(pParams, &size);
494 if (err < 0)
495 {
496 LogRel(("ALSA: Unable to get buffer size for playback: %s\n", snd_strerror(err)));
497 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
498 break;
499 }
500 buffer_size = size;
501 /* set the period time */
502 err = snd_pcm_hw_params_set_period_time_near(phPCM, pParams, &period_time, &dir);
503 if (err < 0)
504 {
505 LogRel(("ALSA: Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)));
506 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
507 break;
508 }
509 err = snd_pcm_hw_params_get_period_size(pParams, &size, &dir);
510 if (err < 0)
511 {
512 LogRel(("ALSA: Unable to get period size for playback: %s\n", snd_strerror(err)));
513 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
514 break;
515 }
516 period_size = size;
517 /* write the parameters to device */
518 err = snd_pcm_hw_params(phPCM, pParams);
519 if (err < 0)
520 {
521 LogRel(("ALSA: Unable to set hw params for playback: %s\n", snd_strerror(err)));
522 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
523 break;
524 }
525
526 rc = VINF_SUCCESS;
527
528 } while (0);
529
530 if (pParams)
531 {
532 snd_pcm_hw_params_free(pParams);
533 pParams = NULL;
534 }
535
536 LogFlowFuncLeaveRC(rc);
537 return rc;
538}
539
540
541static int alsaSetSWParams(snd_pcm_t *phPCM, PALSAAUDIOCFG pCfg)
542{
543 int rc;
544 snd_pcm_sw_params_t *pParams = NULL;
545
546 do
547 {
548 snd_pcm_sw_params_alloca(&pParams);
549 if (!pParams)
550 {
551 rc = VERR_NO_MEMORY;
552 break;
553 }
554 /* get the current swparams */
555 int err = snd_pcm_sw_params_current(phPCM, pParams);
556 if (err < 0)
557 {
558 LogRel(("ALSA: Unable to determine current swparams for playback: %s\n", snd_strerror(err)));
559 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
560 break;
561 }
562 /* start the transfer when the buffer is almost full: */
563 /* (buffer_size / avail_min) * avail_min */
564 err = snd_pcm_sw_params_set_start_threshold(phPCM, pParams, (buffer_size / period_size) * period_size);
565 if (err < 0)
566 {
567 LogRel(("ALSA: Unable to set start threshold mode for playback: %s\n", snd_strerror(err)));
568 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
569 break;
570 }
571 /* allow the transfer when at least period_size samples can be processed */
572 /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
573 err = snd_pcm_sw_params_set_avail_min(phPCM, pParams, period_size);
574 if (err < 0)
575 {
576 LogRel(("ALSA: Unable to set avail min for playback: %s\n", snd_strerror(err)));
577 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
578 break;
579 }
580 /* write the parameters to the playback device */
581 err = snd_pcm_sw_params(phPCM, pParams);
582 if (err < 0)
583 {
584 LogRel(("ALSA: Unable to set sw params for playback: %s\n", snd_strerror(err)));
585 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
586 break;
587 }
588
589 rc = VINF_SUCCESS;
590
591 } while (0);
592
593 if (pParams)
594 {
595 snd_pcm_sw_params_free(pParams);
596 pParams = NULL;
597 }
598
599 LogFlowFuncLeaveRC(rc);
600 return rc;
601}
602#endif
603
604
605static int alsaStreamOpen(bool fIn, PALSAAUDIOSTREAMCFG pCfgReq, PALSAAUDIOSTREAMCFG pCfgObt, snd_pcm_t **pphPCM)
606{
607 snd_pcm_t *phPCM = NULL;
608 int rc;
609
610 unsigned int cChannels = pCfgReq->nchannels;
611 unsigned int uFreq = pCfgReq->freq;
612 snd_pcm_uframes_t obt_buffer_size;
613
614 do
615 {
616 const char *pszDev = fIn ? s_ALSAConf.pcm_name_in : s_ALSAConf.pcm_name_out;
617 if (!pszDev)
618 {
619 LogRel(("ALSA: Invalid or no %s device name set\n", fIn ? "input" : "output"));
620 rc = VERR_INVALID_PARAMETER;
621 break;
622 }
623
624 int err = snd_pcm_open(&phPCM, pszDev,
625 fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
626 SND_PCM_NONBLOCK);
627 if (err < 0)
628 {
629 LogRel(("ALSA: Failed to open \"%s\" as %s device: %s\n", pszDev, fIn ? "input" : "output", snd_strerror(err)));
630 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
631 break;
632 }
633
634 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
635
636 snd_pcm_hw_params_t *pHWParms;
637 snd_pcm_hw_params_alloca(&pHWParms); /** @todo Check for successful allocation? */
638 err = snd_pcm_hw_params_any(phPCM, pHWParms);
639 if (err < 0)
640 {
641 LogRel(("ALSA: Failed to initialize hardware parameters: %s\n", snd_strerror(err)));
642 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
643 break;
644 }
645
646 err = snd_pcm_hw_params_set_access(phPCM, pHWParms,
647 SND_PCM_ACCESS_RW_INTERLEAVED);
648 if (err < 0)
649 {
650 LogRel(("ALSA: Failed to set access type: %s\n", snd_strerror(err)));
651 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
652 break;
653 }
654
655 err = snd_pcm_hw_params_set_format(phPCM, pHWParms, pCfgReq->fmt);
656 if (err < 0)
657 {
658 LogRel(("ALSA: Failed to set audio format to %d: %s\n", pCfgReq->fmt, snd_strerror(err)));
659 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
660 break;
661 }
662
663 err = snd_pcm_hw_params_set_rate_near(phPCM, pHWParms, &uFreq, 0);
664 if (err < 0)
665 {
666 LogRel(("ALSA: Failed to set frequency to %uHz: %s\n", pCfgReq->freq, snd_strerror(err)));
667 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
668 break;
669 }
670
671 err = snd_pcm_hw_params_set_channels_near(phPCM, pHWParms, &cChannels);
672 if (err < 0)
673 {
674 LogRel(("ALSA: Failed to set number of channels to %d\n", pCfgReq->nchannels));
675 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
676 break;
677 }
678
679 if ( cChannels != 1
680 && cChannels != 2)
681 {
682 LogRel(("ALSA: Number of audio channels (%u) not supported\n", cChannels));
683 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
684 break;
685 }
686
687 unsigned int period_size = pCfgReq->period_size;
688 unsigned int buffer_size = pCfgReq->buffer_size;
689
690 if ( !((fIn && s_ALSAConf.size_in_usec_in)
691 || (!fIn && s_ALSAConf.size_in_usec_out)))
692 {
693 if (!buffer_size)
694 {
695 buffer_size = DEFAULT_BUFFER_SIZE;
696 period_size = DEFAULT_PERIOD_SIZE;
697 }
698 }
699
700 if (buffer_size)
701 {
702 if ( ( fIn && s_ALSAConf.size_in_usec_in)
703 || (!fIn && s_ALSAConf.size_in_usec_out))
704 {
705 if (period_size)
706 {
707 err = snd_pcm_hw_params_set_period_time_near(phPCM, pHWParms,
708 &period_size, 0);
709 if (err < 0)
710 {
711 LogRel(("ALSA: Failed to set period time %d\n", pCfgReq->period_size));
712 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
713 break;
714 }
715 }
716
717 err = snd_pcm_hw_params_set_buffer_time_near(phPCM, pHWParms,
718 &buffer_size, 0);
719 if (err < 0)
720 {
721 LogRel(("ALSA: Failed to set buffer time %d\n", pCfgReq->buffer_size));
722 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
723 break;
724 }
725 }
726 else
727 {
728 snd_pcm_uframes_t period_size_f = (snd_pcm_uframes_t)period_size;
729 snd_pcm_uframes_t buffer_size_f = (snd_pcm_uframes_t)buffer_size;
730
731 snd_pcm_uframes_t minval;
732
733 if (period_size_f)
734 {
735 minval = period_size_f;
736
737 int dir = 0;
738 err = snd_pcm_hw_params_get_period_size_min(pHWParms,
739 &minval, &dir);
740 if (err < 0)
741 {
742 LogRel(("ALSA: Could not determine minimal period size\n"));
743 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
744 break;
745 }
746 else
747 {
748 LogFunc(("Minimal period size is: %ld\n", minval));
749 if (period_size_f < minval)
750 {
751 if ( ( fIn && s_ALSAConf.period_size_in_overriden)
752 || (!fIn && s_ALSAConf.period_size_out_overriden))
753 {
754 LogFunc(("Period size %RU32 is less than minimal period size %RU32\n",
755 period_size_f, minval));
756 }
757
758 period_size_f = minval;
759 }
760 }
761
762 err = snd_pcm_hw_params_set_period_size_near(phPCM, pHWParms,
763 &period_size_f, 0);
764 LogFunc(("Period size is: %RU32\n", period_size_f));
765 if (err < 0)
766 {
767 LogRel(("ALSA: Failed to set period size %d (%s)\n",
768 period_size_f, snd_strerror(err)));
769 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
770 break;
771 }
772 }
773
774 /* Calculate default buffer size here since it might have been changed
775 * in the _near functions */
776 buffer_size_f = 4 * period_size_f;
777
778 minval = buffer_size_f;
779 err = snd_pcm_hw_params_get_buffer_size_min(pHWParms, &minval);
780 if (err < 0)
781 {
782 LogRel(("ALSA: Could not retrieve minimal buffer size\n"));
783 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
784 break;
785 }
786 else
787 {
788 LogFunc(("Minimal buffer size is: %RU32\n", minval));
789 if (buffer_size_f < minval)
790 {
791 if ( ( fIn && s_ALSAConf.buffer_size_in_overriden)
792 || (!fIn && s_ALSAConf.buffer_size_out_overriden))
793 {
794 LogFunc(("Buffer size %RU32 is less than minimal buffer size %RU32\n",
795 buffer_size_f, minval));
796 }
797
798 buffer_size_f = minval;
799 }
800 }
801
802 err = snd_pcm_hw_params_set_buffer_size_near(phPCM,
803 pHWParms, &buffer_size_f);
804 LogFunc(("Buffer size is: %RU32\n", buffer_size_f));
805 if (err < 0)
806 {
807 LogRel(("ALSA: Failed to set buffer size %d: %s\n",
808 buffer_size_f, snd_strerror(err)));
809 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
810 break;
811 }
812 }
813 }
814 else
815 LogFunc(("Warning: Buffer size is not set\n"));
816
817 err = snd_pcm_hw_params(phPCM, pHWParms);
818 if (err < 0)
819 {
820 LogRel(("ALSA: Failed to apply audio parameters\n"));
821 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
822 break;
823 }
824
825 err = snd_pcm_hw_params_get_buffer_size(pHWParms, &obt_buffer_size);
826 if (err < 0)
827 {
828 LogRel(("ALSA: Failed to get buffer size\n"));
829 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
830 break;
831 }
832
833 LogFunc(("Buffer sample size is: %RU32\n", obt_buffer_size));
834
835 snd_pcm_uframes_t obt_period_size;
836 int dir = 0;
837 err = snd_pcm_hw_params_get_period_size(pHWParms, &obt_period_size, &dir);
838 if (err < 0)
839 {
840 LogRel(("ALSA: Failed to get period size\n"));
841 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
842 break;
843 }
844
845 LogFunc(("Freq=%dHz, period size=%RU32, buffer size=%RU32\n",
846 pCfgReq->freq, obt_period_size, obt_buffer_size));
847
848 err = snd_pcm_prepare(phPCM);
849 if (err < 0)
850 {
851 LogRel(("ALSA: Could not prepare hPCM %p\n", (void *)phPCM));
852 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
853 break;
854 }
855
856 if ( !fIn
857 && s_ALSAConf.threshold)
858 {
859 unsigned uShift;
860 rc = alsaGetSampleShift(pCfgReq->fmt, &uShift);
861 if (RT_SUCCESS(rc))
862 {
863 int bytes_per_sec = uFreq
864 << (cChannels == 2)
865 << uShift;
866
867 snd_pcm_uframes_t threshold
868 = (s_ALSAConf.threshold * bytes_per_sec) / 1000;
869
870 rc = alsaStreamSetThreshold(phPCM, threshold);
871 }
872 }
873 else
874 rc = VINF_SUCCESS;
875 }
876 while (0);
877
878 if (RT_SUCCESS(rc))
879 {
880 pCfgObt->fmt = pCfgReq->fmt;
881 pCfgObt->nchannels = cChannels;
882 pCfgObt->freq = uFreq;
883 pCfgObt->samples = obt_buffer_size;
884
885 *pphPCM = phPCM;
886 }
887 else
888 alsaStreamClose(&phPCM);
889
890 LogFlowFuncLeaveRC(rc);
891 return rc;
892}
893
894
895#ifdef DEBUG
896static void alsaDbgErrorHandler(const char *file, int line, const char *function,
897 int err, const char *fmt, ...)
898{
899 /** @todo Implement me! */
900 RT_NOREF(file, line, function, err, fmt);
901}
902#endif
903
904
905static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pFramesAvail)
906{
907 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
908 AssertPtrReturn(pFramesAvail, VERR_INVALID_POINTER);
909
910 int rc;
911
912 snd_pcm_sframes_t framesAvail;
913 framesAvail = snd_pcm_avail_update(phPCM);
914 if (framesAvail < 0)
915 {
916 if (framesAvail == -EPIPE)
917 {
918 rc = alsaStreamRecover(phPCM);
919 if (RT_SUCCESS(rc))
920 framesAvail = snd_pcm_avail_update(phPCM);
921 }
922 else
923 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
924 }
925 else
926 rc = VINF_SUCCESS;
927
928 if (framesAvail >= 0)
929 *pFramesAvail = framesAvail;
930
931 return rc;
932}
933
934
935static int alsaStreamRecover(snd_pcm_t *phPCM)
936{
937 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
938
939 int err = snd_pcm_prepare(phPCM);
940 if (err < 0)
941 {
942 LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err)));
943 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
944 }
945
946 return VINF_SUCCESS;
947}
948
949
950static int alsaStreamResume(snd_pcm_t *phPCM)
951{
952 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
953
954 int err = snd_pcm_resume(phPCM);
955 if (err < 0)
956 {
957 LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err)));
958 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
959 }
960
961 return VINF_SUCCESS;
962}
963
964
965static int drvHostALSAAudioStreamCtl(snd_pcm_t *phPCM, bool fPause)
966{
967 int err;
968 if (fPause)
969 {
970 err = snd_pcm_drop(phPCM);
971 if (err < 0)
972 {
973 LogRel(("ALSA: Error stopping stream %p: %s\n", phPCM, snd_strerror(err)));
974 return VERR_ACCESS_DENIED;
975 }
976 }
977 else
978 {
979 err = snd_pcm_prepare(phPCM);
980 if (err < 0)
981 {
982 LogRel(("ALSA: Error preparing stream %p: %s\n", phPCM, snd_strerror(err)));
983 return VERR_ACCESS_DENIED;
984 }
985 }
986
987 return VINF_SUCCESS;
988}
989
990
991/**
992 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
993 */
994static DECLCALLBACK(int) drvHostALSAAudioInit(PPDMIHOSTAUDIO pInterface)
995{
996 NOREF(pInterface);
997
998 LogFlowFuncEnter();
999
1000 int rc = audioLoadAlsaLib();
1001 if (RT_FAILURE(rc))
1002 LogRel(("ALSA: Failed to load the ALSA shared library, rc=%Rrc\n", rc));
1003 else
1004 {
1005#ifdef DEBUG
1006 snd_lib_error_set_handler(alsaDbgErrorHandler);
1007#endif
1008 }
1009
1010 return rc;
1011}
1012
1013
1014/**
1015 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1016 */
1017static DECLCALLBACK(int) drvHostALSAAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1018{
1019 RT_NOREF(pvBuf, cbBuf);
1020 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1021 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1022 /* pcbRead is optional. */
1023
1024 PALSAAUDIOSTREAMIN pThisStream = (PALSAAUDIOSTREAMIN)pStream;
1025
1026 snd_pcm_sframes_t cAvail;
1027 int rc = alsaStreamGetAvail(pThisStream->phPCM, &cAvail);
1028 if (RT_FAILURE(rc))
1029 {
1030 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
1031 return rc;
1032 }
1033
1034 if (!cAvail) /* No data yet? */
1035 {
1036 snd_pcm_state_t state = snd_pcm_state(pThisStream->phPCM);
1037 switch (state)
1038 {
1039 case SND_PCM_STATE_PREPARED:
1040 cAvail = AudioMixBufFree(&pStream->MixBuf);
1041 break;
1042
1043 case SND_PCM_STATE_SUSPENDED:
1044 {
1045 rc = alsaStreamResume(pThisStream->phPCM);
1046 if (RT_FAILURE(rc))
1047 break;
1048
1049 LogFlow(("Resuming suspended input stream\n"));
1050 break;
1051 }
1052
1053 default:
1054 LogFlow(("No frames available, state=%d\n", state));
1055 break;
1056 }
1057
1058 if (!cAvail)
1059 {
1060 if (pcbRead)
1061 *pcbRead = 0;
1062 return VINF_SUCCESS;
1063 }
1064 }
1065
1066 /*
1067 * Check how much we can read from the capture device without overflowing
1068 * the mixer buffer.
1069 */
1070 Assert(cAvail);
1071 size_t cbMixFree = AudioMixBufFreeBytes(&pStream->MixBuf);
1072 size_t cbToRead = RT_MIN((size_t)AUDIOMIXBUF_S2B(&pStream->MixBuf, cAvail), cbMixFree);
1073
1074 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
1075
1076 uint32_t cWrittenTotal = 0;
1077 snd_pcm_uframes_t cToRead;
1078 snd_pcm_sframes_t cRead;
1079
1080 while ( cbToRead
1081 && RT_SUCCESS(rc))
1082 {
1083 cToRead = RT_MIN(AUDIOMIXBUF_B2S(&pStream->MixBuf, cbToRead),
1084 AUDIOMIXBUF_B2S(&pStream->MixBuf, pThisStream->cbBuf));
1085 AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
1086 cRead = snd_pcm_readi(pThisStream->phPCM, pThisStream->pvBuf, cToRead);
1087 if (cRead <= 0)
1088 {
1089 switch (cRead)
1090 {
1091 case 0:
1092 {
1093 LogFunc(("No input frames available\n"));
1094 rc = VERR_ACCESS_DENIED;
1095 break;
1096 }
1097
1098 case -EAGAIN:
1099 {
1100 /*
1101 * Don't set error here because EAGAIN means there are no further frames
1102 * available at the moment, try later. As we might have read some frames
1103 * already these need to be processed instead.
1104 */
1105 cbToRead = 0;
1106 break;
1107 }
1108
1109 case -EPIPE:
1110 {
1111 rc = alsaStreamRecover(pThisStream->phPCM);
1112 if (RT_FAILURE(rc))
1113 break;
1114
1115 LogFlowFunc(("Recovered from capturing\n"));
1116 continue;
1117 }
1118
1119 default:
1120 {
1121 LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
1122 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1123 break;
1124 }
1125 }
1126 }
1127 else
1128 {
1129 uint32_t cWritten;
1130 rc = AudioMixBufWriteCirc(&pStream->MixBuf,
1131 pThisStream->pvBuf, AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead),
1132 &cWritten);
1133 if (RT_FAILURE(rc))
1134 break;
1135
1136 /*
1137 * We should not run into a full mixer buffer or we loose samples and
1138 * run into an endless loop if ALSA keeps producing samples ("null"
1139 * capture device for example).
1140 */
1141 AssertLogRelMsgBreakStmt(cWritten > 0, ("Mixer buffer shouldn't be full at this point!\n"),
1142 rc = VERR_INTERNAL_ERROR);
1143 uint32_t cbWritten = AUDIOMIXBUF_S2B(&pStream->MixBuf, cWritten);
1144
1145 Assert(cbToRead >= cbWritten);
1146 cbToRead -= cbWritten;
1147 cWrittenTotal += cWritten;
1148 }
1149 }
1150
1151 if (RT_SUCCESS(rc))
1152 {
1153 uint32_t cProcessed = 0;
1154 if (cWrittenTotal)
1155 rc = AudioMixBufMixToParent(&pStream->MixBuf, cWrittenTotal,
1156 &cProcessed);
1157
1158 if (pcbRead)
1159 *pcbRead = cWrittenTotal;
1160
1161 LogFlowFunc(("cWrittenTotal=%RU32 (%RU32 processed), rc=%Rrc\n",
1162 cWrittenTotal, cProcessed, rc));
1163 }
1164
1165 LogFlowFuncLeaveRC(rc);
1166 return rc;
1167}
1168
1169/**
1170 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1171 */
1172static DECLCALLBACK(int) drvHostALSAAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1173{
1174 RT_NOREF(pvBuf, cbBuf);
1175 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1176 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1177 /* pcbWritten is optional. */
1178
1179 PALSAAUDIOSTREAMOUT pThisStream = (PALSAAUDIOSTREAMOUT)pStream;
1180
1181 int rc = VINF_SUCCESS;
1182 uint32_t cbReadTotal = 0;
1183
1184 do
1185 {
1186 snd_pcm_sframes_t cAvail;
1187 rc = alsaStreamGetAvail(pThisStream->phPCM, &cAvail);
1188 if (RT_FAILURE(rc))
1189 {
1190 LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
1191 break;
1192 }
1193
1194 size_t cbToRead = RT_MIN(AUDIOMIXBUF_S2B(&pStream->MixBuf,
1195 (uint32_t)cAvail), /* cAvail is always >= 0 */
1196 AUDIOMIXBUF_S2B(&pStream->MixBuf,
1197 AudioMixBufLive(&pStream->MixBuf)));
1198 LogFlowFunc(("cbToRead=%zu, cbAvail=%zu\n",
1199 cbToRead, AUDIOMIXBUF_S2B(&pStream->MixBuf, cAvail)));
1200
1201 uint32_t cRead, cbRead;
1202 snd_pcm_sframes_t cWritten;
1203 while (cbToRead)
1204 {
1205 rc = AudioMixBufReadCirc(&pStream->MixBuf, pThisStream->pvBuf, cbToRead, &cRead);
1206 if (RT_FAILURE(rc))
1207 break;
1208
1209 cbRead = AUDIOMIXBUF_S2B(&pStream->MixBuf, cRead);
1210 AssertBreak(cbRead);
1211
1212 /* Don't try infinitely on recoverable errors. */
1213 unsigned iTry;
1214 for (iTry = 0; iTry < ALSA_RECOVERY_TRIES_MAX; iTry++)
1215 {
1216 cWritten = snd_pcm_writei(pThisStream->phPCM, pThisStream->pvBuf, cRead);
1217 if (cWritten <= 0)
1218 {
1219 switch (cWritten)
1220 {
1221 case 0:
1222 {
1223 LogFunc(("Failed to write %RU32 samples\n", cRead));
1224 rc = VERR_ACCESS_DENIED;
1225 break;
1226 }
1227
1228 case -EPIPE:
1229 {
1230 rc = alsaStreamRecover(pThisStream->phPCM);
1231 if (RT_FAILURE(rc))
1232 break;
1233
1234 LogFlowFunc(("Recovered from playback\n"));
1235 continue;
1236 }
1237
1238 case -ESTRPIPE:
1239 {
1240 /* Stream was suspended and waiting for a recovery. */
1241 rc = alsaStreamResume(pThisStream->phPCM);
1242 if (RT_FAILURE(rc))
1243 {
1244 LogRel(("ALSA: Failed to resume output stream\n"));
1245 break;
1246 }
1247
1248 LogFlowFunc(("Resumed suspended output stream\n"));
1249 continue;
1250 }
1251
1252 default:
1253 LogFlowFunc(("Failed to write %RI32 output frames, rc=%Rrc\n",
1254 cRead, rc));
1255 rc = VERR_GENERAL_FAILURE; /** @todo */
1256 break;
1257 }
1258 }
1259 else
1260 break;
1261 } /* For number of tries. */
1262
1263 if ( iTry == ALSA_RECOVERY_TRIES_MAX
1264 && cWritten <= 0)
1265 rc = VERR_BROKEN_PIPE;
1266
1267 if (RT_FAILURE(rc))
1268 break;
1269
1270 Assert(cbToRead >= cbRead);
1271 cbToRead -= cbRead;
1272 cbReadTotal += cbRead;
1273 }
1274 }
1275 while (0);
1276
1277 if (RT_SUCCESS(rc))
1278 {
1279 uint32_t cReadTotal = AUDIOMIXBUF_B2S(&pStream->MixBuf, cbReadTotal);
1280 if (cReadTotal)
1281 AudioMixBufFinish(&pStream->MixBuf, cReadTotal);
1282
1283 if (pcbWritten)
1284 *pcbWritten = cReadTotal;
1285
1286 LogFlowFunc(("cReadTotal=%RU32 (%RU32 bytes), rc=%Rrc\n",
1287 cReadTotal, cbReadTotal, rc));
1288 }
1289
1290 LogFlowFuncLeaveRC(rc);
1291 return rc;
1292}
1293
1294
1295static int alsaDestroyStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1296{
1297 NOREF(pInterface);
1298 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1299
1300 PALSAAUDIOSTREAMIN pThisStream = (PALSAAUDIOSTREAMIN)pStream;
1301
1302 alsaStreamClose(&pThisStream->phPCM);
1303
1304 if (pThisStream->pvBuf)
1305 {
1306 RTMemFree(pThisStream->pvBuf);
1307 pThisStream->pvBuf = NULL;
1308 }
1309
1310 return VINF_SUCCESS;
1311}
1312
1313
1314static int alsaDestroyStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1315{
1316 NOREF(pInterface);
1317 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1318
1319 PALSAAUDIOSTREAMOUT pThisStream = (PALSAAUDIOSTREAMOUT)pStream;
1320
1321 alsaStreamClose(&pThisStream->phPCM);
1322
1323 if (pThisStream->pvBuf)
1324 {
1325 RTMemFree(pThisStream->pvBuf);
1326 pThisStream->pvBuf = NULL;
1327 }
1328
1329 return VINF_SUCCESS;
1330}
1331
1332
1333static int alsaCreateStreamOut(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1334{
1335 PALSAAUDIOSTREAMOUT pThisStream = (PALSAAUDIOSTREAMOUT)pStream;
1336
1337 snd_pcm_t *phPCM = NULL;
1338
1339 int rc;
1340
1341 do
1342 {
1343 ALSAAUDIOSTREAMCFG req;
1344 req.fmt = alsaAudioFmtToALSA(pCfgReq->enmFormat);
1345 req.freq = pCfgReq->uHz;
1346 req.nchannels = pCfgReq->cChannels;
1347 req.period_size = s_ALSAConf.period_size_out;
1348 req.buffer_size = s_ALSAConf.buffer_size_out;
1349
1350 ALSAAUDIOSTREAMCFG obt;
1351 rc = alsaStreamOpen(false /* fIn */, &req, &obt, &phPCM);
1352 if (RT_FAILURE(rc))
1353 break;
1354
1355 PDMAUDIOFMT enmFormat;
1356 PDMAUDIOENDIANNESS enmEnd;
1357 rc = alsaALSAToAudioFmt(obt.fmt, &enmFormat, &enmEnd);
1358 if (RT_FAILURE(rc))
1359 break;
1360
1361 pCfgAcq->uHz = obt.freq;
1362 pCfgAcq->cChannels = obt.nchannels;
1363 pCfgAcq->enmFormat = enmFormat;
1364 pCfgAcq->enmEndianness = enmEnd;
1365 pCfgAcq->cSampleBufferSize = obt.samples * 4;
1366
1367 PDMAUDIOPCMPROPS Props;
1368 rc = DrvAudioHlpStreamCfgToProps(pCfgAcq, &Props);
1369 if (RT_FAILURE(rc))
1370 break;
1371
1372 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
1373 size_t cbBuf = obt.samples * (1 << Props.cShift); /** @todo Get rid of using Props! */
1374 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
1375 pThisStream->pvBuf = RTMemAlloc(cbBuf);
1376 if (!pThisStream->pvBuf)
1377 {
1378 LogRel(("ALSA: Not enough memory for output DAC buffer (%RU32 samples, each %d bytes)\n",
1379 obt.samples, 1 << Props.cShift));
1380 rc = VERR_NO_MEMORY;
1381 break;
1382 }
1383
1384 pThisStream->cbBuf = cbBuf;
1385 pThisStream->phPCM = phPCM;
1386 }
1387 while (0);
1388
1389 if (RT_FAILURE(rc))
1390 alsaStreamClose(&phPCM);
1391
1392 LogFlowFuncLeaveRC(rc);
1393 return rc;
1394}
1395
1396
1397static int alsaCreateStreamIn(PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1398{
1399 int rc;
1400
1401 PALSAAUDIOSTREAMIN pThisStream = (PALSAAUDIOSTREAMIN)pStream;
1402 snd_pcm_t *phPCM = NULL;
1403
1404 do
1405 {
1406 ALSAAUDIOSTREAMCFG req;
1407 req.fmt = alsaAudioFmtToALSA(pCfgReq->enmFormat);
1408 req.freq = pCfgReq->uHz;
1409 req.nchannels = pCfgReq->cChannels;
1410 req.period_size = s_ALSAConf.period_size_in;
1411 req.buffer_size = s_ALSAConf.buffer_size_in;
1412
1413 ALSAAUDIOSTREAMCFG obt;
1414 rc = alsaStreamOpen(true /* fIn */, &req, &obt, &phPCM);
1415 if (RT_FAILURE(rc))
1416 break;
1417
1418 PDMAUDIOFMT enmFormat;
1419 PDMAUDIOENDIANNESS enmEnd;
1420 rc = alsaALSAToAudioFmt(obt.fmt, &enmFormat, &enmEnd);
1421 if (RT_FAILURE(rc))
1422 break;
1423
1424 pCfgAcq->uHz = obt.freq;
1425 pCfgAcq->cChannels = obt.nchannels;
1426 pCfgAcq->enmFormat = enmFormat;
1427 pCfgAcq->enmEndianness = enmEnd;
1428 pCfgAcq->cSampleBufferSize = obt.samples;
1429
1430 PDMAUDIOPCMPROPS Props;
1431 rc = DrvAudioHlpStreamCfgToProps(pCfgAcq, &Props);
1432 if (RT_FAILURE(rc))
1433 break;
1434
1435 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
1436 size_t cbBuf = obt.samples * (1 << Props.cShift); /** @todo Get rid of using Props! */
1437 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
1438 pThisStream->pvBuf = RTMemAlloc(cbBuf);
1439 if (!pThisStream->pvBuf)
1440 {
1441 LogRel(("ALSA: Not enough memory for input ADC buffer (%RU32 samples, each %d bytes)\n",
1442 obt.samples, 1 << Props.cShift));
1443 rc = VERR_NO_MEMORY;
1444 break;
1445 }
1446
1447 pThisStream->cbBuf = cbBuf;
1448 pThisStream->phPCM = phPCM;
1449 }
1450 while (0);
1451
1452 if (RT_FAILURE(rc))
1453 alsaStreamClose(&phPCM);
1454
1455 LogFlowFuncLeaveRC(rc);
1456 return rc;
1457}
1458
1459
1460static int alsaControlStreamIn(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1461 PDMAUDIOSTREAMCMD enmStreamCmd)
1462{
1463 NOREF(pInterface);
1464 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1465 PALSAAUDIOSTREAMIN pThisStream = (PALSAAUDIOSTREAMIN)pStream;
1466
1467 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1468
1469 int rc;
1470 switch (enmStreamCmd)
1471 {
1472 case PDMAUDIOSTREAMCMD_ENABLE:
1473 case PDMAUDIOSTREAMCMD_RESUME:
1474 rc = drvHostALSAAudioStreamCtl(pThisStream->phPCM, false /* fStop */);
1475 break;
1476
1477 case PDMAUDIOSTREAMCMD_DISABLE:
1478 case PDMAUDIOSTREAMCMD_PAUSE:
1479 rc = drvHostALSAAudioStreamCtl(pThisStream->phPCM, true /* fStop */);
1480 break;
1481
1482 default:
1483 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1484 rc = VERR_INVALID_PARAMETER;
1485 break;
1486 }
1487
1488 return rc;
1489}
1490
1491
1492static int alsaControlStreamOut(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream,
1493 PDMAUDIOSTREAMCMD enmStreamCmd)
1494{
1495 NOREF(pInterface);
1496 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1497 PALSAAUDIOSTREAMOUT pThisStream = (PALSAAUDIOSTREAMOUT)pStream;
1498
1499 LogFlowFunc(("enmStreamCmd=%ld\n", enmStreamCmd));
1500
1501 int rc;
1502 switch (enmStreamCmd)
1503 {
1504 case PDMAUDIOSTREAMCMD_ENABLE:
1505 case PDMAUDIOSTREAMCMD_RESUME:
1506 rc = drvHostALSAAudioStreamCtl(pThisStream->phPCM, false /* fStop */);
1507 break;
1508
1509 case PDMAUDIOSTREAMCMD_DISABLE:
1510 case PDMAUDIOSTREAMCMD_PAUSE:
1511 rc = drvHostALSAAudioStreamCtl(pThisStream->phPCM, true /* fStop */);
1512 break;
1513
1514 default:
1515 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1516 rc = VERR_INVALID_PARAMETER;
1517 break;
1518 }
1519
1520 return rc;
1521}
1522
1523
1524/**
1525 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
1526 */
1527static DECLCALLBACK(int) drvHostALSAAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
1528{
1529 NOREF(pInterface);
1530 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
1531
1532 pBackendCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAMIN);
1533 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAMOUT);
1534
1535 /* Enumerate sound devices. */
1536 char **pszHints;
1537 int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);
1538 if (err == 0)
1539 {
1540 char** pszHintCur = pszHints;
1541 while (*pszHintCur != NULL)
1542 {
1543 char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");
1544 bool fSkip = !pszDev
1545 || !RTStrICmp("null", pszDev);
1546 if (fSkip)
1547 {
1548 if (pszDev)
1549 free(pszDev);
1550 pszHintCur++;
1551 continue;
1552 }
1553
1554 char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");
1555 if (pszIOID)
1556 {
1557#if 0
1558 if (!RTStrICmp("input", pszIOID))
1559
1560 else if (!RTStrICmp("output", pszIOID))
1561#endif
1562 }
1563 else /* NULL means bidirectional, input + output. */
1564 {
1565 }
1566
1567 LogRel2(("ALSA: Found %s device: %s\n", pszIOID ? RTStrToLower(pszIOID) : "bidirectional", pszDev));
1568
1569 /* Special case for ALSAAudio. */
1570 if ( pszDev
1571 && RTStrIStr("pulse", pszDev) != NULL)
1572 LogRel2(("ALSA: ALSAAudio plugin in use\n"));
1573
1574 if (pszIOID)
1575 free(pszIOID);
1576
1577 if (pszDev)
1578 free(pszDev);
1579
1580 pszHintCur++;
1581 }
1582
1583 snd_device_name_free_hint((void **)pszHints);
1584 pszHints = NULL;
1585 }
1586 else
1587 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));
1588
1589 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
1590 pBackendCfg->cMaxStreamsIn = 1;
1591 pBackendCfg->cMaxStreamsOut = 1;
1592
1593 return VINF_SUCCESS;
1594}
1595
1596
1597/**
1598 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
1599 */
1600static DECLCALLBACK(void) drvHostALSAAudioShutdown(PPDMIHOSTAUDIO pInterface)
1601{
1602 NOREF(pInterface);
1603}
1604
1605
1606/**
1607 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1608 */
1609static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostALSAAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1610{
1611 RT_NOREF(enmDir);
1612 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1613
1614 return PDMAUDIOBACKENDSTS_RUNNING;
1615}
1616
1617
1618/**
1619 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1620 */
1621static DECLCALLBACK(int) drvHostALSAAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1622{
1623 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1624 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1625 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1626 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1627
1628 int rc;
1629 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1630 rc = alsaCreateStreamIn(pStream, pCfgReq, pCfgAcq);
1631 else
1632 rc = alsaCreateStreamOut(pStream, pCfgReq, pCfgAcq);
1633
1634 LogFlowFunc(("%s: rc=%Rrc\n", pStream->szName, rc));
1635 return rc;
1636}
1637
1638
1639/**
1640 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1641 */
1642static DECLCALLBACK(int) drvHostALSAAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1643{
1644 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1645 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1646
1647 int rc;
1648 if (pStream->enmDir == PDMAUDIODIR_IN)
1649 rc = alsaDestroyStreamIn(pInterface, pStream);
1650 else
1651 rc = alsaDestroyStreamOut(pInterface, pStream);
1652
1653 return rc;
1654}
1655
1656
1657/**
1658 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1659 */
1660static DECLCALLBACK(int) drvHostALSAAudioStreamControl(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1661{
1662 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1663 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1664
1665 Assert(pStream->enmCtx == PDMAUDIOSTREAMCTX_HOST);
1666
1667 int rc;
1668 if (pStream->enmDir == PDMAUDIODIR_IN)
1669 rc = alsaControlStreamIn(pInterface, pStream, enmStreamCmd);
1670 else
1671 rc = alsaControlStreamOut(pInterface, pStream, enmStreamCmd);
1672
1673 return rc;
1674}
1675
1676
1677/**
1678 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1679 */
1680static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostALSAAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1681{
1682 NOREF(pInterface);
1683 NOREF(pStream);
1684
1685 PDMAUDIOSTRMSTS strmSts = PDMAUDIOSTRMSTS_FLAG_INITIALIZED
1686 | PDMAUDIOSTRMSTS_FLAG_ENABLED;
1687
1688 if (pStream->enmDir == PDMAUDIODIR_IN)
1689 {
1690
1691 }
1692 else
1693 {
1694 PALSAAUDIOSTREAMOUT pStreamOut = (PALSAAUDIOSTREAMOUT)pStream;
1695
1696 snd_pcm_sframes_t cAvail;
1697 int rc2 = alsaStreamGetAvail(pStreamOut->phPCM, &cAvail);
1698 if (RT_SUCCESS(rc2))
1699 {
1700 LogFlowFunc(("cAvail=%ld\n", cAvail));
1701 if (cAvail >= (snd_pcm_sframes_t)pStreamOut->cSamplesMin)
1702 strmSts |= PDMAUDIOSTRMSTS_FLAG_DATA_WRITABLE;
1703 }
1704 }
1705
1706 return strmSts;
1707}
1708
1709
1710/**
1711 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1712 */
1713static DECLCALLBACK(int) drvHostALSAAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOSTREAM pStream)
1714{
1715 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1716 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1717
1718 LogFlowFuncEnter();
1719
1720 /* Nothing to do here for ALSA. */
1721 return VINF_SUCCESS;
1722}
1723
1724
1725/**
1726 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1727 */
1728static DECLCALLBACK(void *) drvHostALSAAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1729{
1730 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1731 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1732 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1733 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1734
1735 return NULL;
1736}
1737
1738
1739/**
1740 * Construct a DirectSound Audio driver instance.
1741 *
1742 * @copydoc FNPDMDRVCONSTRUCT
1743 */
1744static DECLCALLBACK(int) drvHostAlsaAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1745{
1746 RT_NOREF(pCfg, fFlags);
1747 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1748 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1749 LogRel(("Audio: Initializing ALSA driver\n"));
1750
1751 /*
1752 * Init the static parts.
1753 */
1754 pThis->pDrvIns = pDrvIns;
1755 /* IBase */
1756 pDrvIns->IBase.pfnQueryInterface = drvHostALSAAudioQueryInterface;
1757 /* IHostAudio */
1758 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio);
1759
1760 return VINF_SUCCESS;
1761}
1762
1763
1764/**
1765 * Char driver registration record.
1766 */
1767const PDMDRVREG g_DrvHostALSAAudio =
1768{
1769 /* u32Version */
1770 PDM_DRVREG_VERSION,
1771 /* szName */
1772 "ALSAAudio",
1773 /* szRCMod */
1774 "",
1775 /* szR0Mod */
1776 "",
1777 /* pszDescription */
1778 "ALSA host audio driver",
1779 /* fFlags */
1780 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1781 /* fClass. */
1782 PDM_DRVREG_CLASS_AUDIO,
1783 /* cMaxInstances */
1784 ~0U,
1785 /* cbInstance */
1786 sizeof(DRVHOSTALSAAUDIO),
1787 /* pfnConstruct */
1788 drvHostAlsaAudioConstruct,
1789 /* pfnDestruct */
1790 NULL,
1791 /* pfnRelocate */
1792 NULL,
1793 /* pfnIOCtl */
1794 NULL,
1795 /* pfnPowerOn */
1796 NULL,
1797 /* pfnReset */
1798 NULL,
1799 /* pfnSuspend */
1800 NULL,
1801 /* pfnResume */
1802 NULL,
1803 /* pfnAttach */
1804 NULL,
1805 /* pfnDetach */
1806 NULL,
1807 /* pfnPowerOff */
1808 NULL,
1809 /* pfnSoftReset */
1810 NULL,
1811 /* u32EndVersion */
1812 PDM_DRVREG_VERSION
1813};
1814
1815#if 0 /* unused */
1816static struct audio_option alsa_options[] =
1817{
1818 {"DACSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_out,
1819 "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1820 {"DACPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_out,
1821 "DAC period size", &s_ALSAConf.period_size_out_overriden, 0},
1822 {"DACBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_out,
1823 "DAC buffer size", &s_ALSAConf.buffer_size_out_overriden, 0},
1824
1825 {"ADCSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_in,
1826 "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1827 {"ADCPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_in,
1828 "ADC period size", &s_ALSAConf.period_size_in_overriden, 0},
1829 {"ADCBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_in,
1830 "ADC buffer size", &s_ALSAConf.buffer_size_in_overriden, 0},
1831
1832 {"Threshold", AUD_OPT_INT, &s_ALSAConf.threshold,
1833 "(undocumented)", NULL, 0},
1834
1835 {"DACDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_out,
1836 "DAC device name (for instance dmix)", NULL, 0},
1837
1838 {"ADCDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_in,
1839 "ADC device name", NULL, 0}
1840};
1841#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette