VirtualBox

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

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

Audio: More abstraction for the backends: Now the backend stream's data is completely separate from the audio connector interface. That way the backends cannot mess with the audio connector's data (e.g. mixing buffers and friends) anymore, and those are forced to use the audio connector API as meant now. Needs more testing, partly work in progress.

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