VirtualBox

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

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

Build fix.

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

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