VirtualBox

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

Last change on this file since 71930 was 69857, checked in by vboxsync, 7 years ago

Audio/ALSA: Only do pcm_start() for input streams in drvHostALSAAudioStreamCtl().

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

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