VirtualBox

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

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

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.7 KB
Line 
1/* $Id: DrvHostALSAAudio.cpp 65696 2017-02-09 11:23:35Z 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->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 return rc;
924}
925
926
927static int alsaStreamRecover(snd_pcm_t *phPCM)
928{
929 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
930
931 int err = snd_pcm_prepare(phPCM);
932 if (err < 0)
933 {
934 LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err)));
935 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
936 }
937
938 return VINF_SUCCESS;
939}
940
941
942static int alsaStreamResume(snd_pcm_t *phPCM)
943{
944 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
945
946 int err = snd_pcm_resume(phPCM);
947 if (err < 0)
948 {
949 LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err)));
950 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
951 }
952
953 return VINF_SUCCESS;
954}
955
956
957static int drvHostALSAAudioStreamCtl(snd_pcm_t *phPCM, bool fPause)
958{
959 int err;
960 if (fPause)
961 {
962 err = snd_pcm_drop(phPCM);
963 if (err < 0)
964 {
965 LogRel(("ALSA: Error stopping stream %p: %s\n", phPCM, snd_strerror(err)));
966 return VERR_ACCESS_DENIED;
967 }
968 }
969 else
970 {
971 err = snd_pcm_prepare(phPCM);
972 if (err < 0)
973 {
974 LogRel(("ALSA: Error preparing stream %p: %s\n", phPCM, snd_strerror(err)));
975 return VERR_ACCESS_DENIED;
976 }
977 }
978
979 return VINF_SUCCESS;
980}
981
982
983/**
984 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
985 */
986static DECLCALLBACK(int) drvHostALSAAudioInit(PPDMIHOSTAUDIO pInterface)
987{
988 RT_NOREF(pInterface);
989
990 LogFlowFuncEnter();
991
992 int rc = audioLoadAlsaLib();
993 if (RT_FAILURE(rc))
994 LogRel(("ALSA: Failed to load the ALSA shared library, rc=%Rrc\n", rc));
995 else
996 {
997#ifdef DEBUG
998 snd_lib_error_set_handler(alsaDbgErrorHandler);
999#endif
1000 }
1001
1002 return rc;
1003}
1004
1005
1006/**
1007 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
1008 */
1009static DECLCALLBACK(int) drvHostALSAAudioStreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1010 void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
1011{
1012 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1013 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1014 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1015 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1016 /* pcbRead is optional. */
1017
1018 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1019
1020 snd_pcm_sframes_t cAvail;
1021 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cAvail);
1022 if (RT_FAILURE(rc))
1023 {
1024 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
1025 return rc;
1026 }
1027
1028 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg;
1029 AssertPtr(pCfg);
1030
1031 if (!cAvail) /* No data yet? */
1032 {
1033 snd_pcm_state_t state = snd_pcm_state(pStreamALSA->phPCM);
1034 switch (state)
1035 {
1036 case SND_PCM_STATE_PREPARED:
1037 cAvail = PDMAUDIOSTREAMCFG_B2S(pCfg, cbBuf);
1038 break;
1039
1040 case SND_PCM_STATE_SUSPENDED:
1041 {
1042 rc = alsaStreamResume(pStreamALSA->phPCM);
1043 if (RT_FAILURE(rc))
1044 break;
1045
1046 LogFlow(("Resuming suspended input stream\n"));
1047 break;
1048 }
1049
1050 default:
1051 LogFlow(("No frames available, state=%d\n", state));
1052 break;
1053 }
1054
1055 if (!cAvail)
1056 {
1057 if (pcbRead)
1058 *pcbRead = 0;
1059 return VINF_SUCCESS;
1060 }
1061 }
1062
1063 /*
1064 * Check how much we can read from the capture device without overflowing
1065 * the mixer buffer.
1066 */
1067 size_t cbToRead = RT_MIN((size_t)PDMAUDIOSTREAMCFG_S2B(pCfg, cAvail), cbBuf);
1068
1069 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
1070
1071 uint32_t cbReadTotal = 0;
1072
1073 snd_pcm_uframes_t cToRead;
1074 snd_pcm_sframes_t cRead;
1075
1076 while ( cbToRead
1077 && RT_SUCCESS(rc))
1078 {
1079 cToRead = RT_MIN(PDMAUDIOSTREAMCFG_B2S(pCfg, cbToRead),
1080 PDMAUDIOSTREAMCFG_B2S(pCfg, pStreamALSA->cbBuf));
1081 AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
1082 cRead = snd_pcm_readi(pStreamALSA->phPCM, pStreamALSA->pvBuf, cToRead);
1083 if (cRead <= 0)
1084 {
1085 switch (cRead)
1086 {
1087 case 0:
1088 {
1089 LogFunc(("No input frames available\n"));
1090 rc = VERR_ACCESS_DENIED;
1091 break;
1092 }
1093
1094 case -EAGAIN:
1095 {
1096 /*
1097 * Don't set error here because EAGAIN means there are no further frames
1098 * available at the moment, try later. As we might have read some frames
1099 * already these need to be processed instead.
1100 */
1101 cbToRead = 0;
1102 break;
1103 }
1104
1105 case -EPIPE:
1106 {
1107 rc = alsaStreamRecover(pStreamALSA->phPCM);
1108 if (RT_FAILURE(rc))
1109 break;
1110
1111 LogFlowFunc(("Recovered from capturing\n"));
1112 continue;
1113 }
1114
1115 default:
1116 {
1117 LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
1118 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
1119 break;
1120 }
1121 }
1122 }
1123 else
1124 {
1125 /*
1126 * We should not run into a full mixer buffer or we loose samples and
1127 * run into an endless loop if ALSA keeps producing samples ("null"
1128 * capture device for example).
1129 */
1130 uint32_t cbRead = PDMAUDIOSTREAMCFG_S2B(pCfg, cRead);
1131
1132 memcpy(pvBuf, pStreamALSA->pvBuf, cbRead);
1133
1134 Assert(cbToRead >= cbRead);
1135 cbToRead -= cbRead;
1136 cbReadTotal += cbRead;
1137 }
1138 }
1139
1140 if (RT_SUCCESS(rc))
1141 {
1142 if (pcbRead)
1143 *pcbRead = cbReadTotal;
1144 }
1145
1146 return rc;
1147}
1148
1149/**
1150 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
1151 */
1152static DECLCALLBACK(int) drvHostALSAAudioStreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1153 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten)
1154{
1155 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1156 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1157 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1158 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1159 /* pcbWritten is optional. */
1160
1161 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1162
1163 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg;
1164 AssertPtr(pCfg);
1165
1166 int rc;
1167
1168 uint32_t cbWrittenTotal = 0;
1169
1170 do
1171 {
1172 snd_pcm_sframes_t csAvail;
1173 rc = alsaStreamGetAvail(pStreamALSA->phPCM, &csAvail);
1174 if (RT_FAILURE(rc))
1175 {
1176 LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
1177 break;
1178 }
1179
1180 if (!csAvail)
1181 break;
1182
1183 size_t cbToWrite = RT_MIN((unsigned)PDMAUDIOSTREAMCFG_S2B(pCfg, csAvail), pStreamALSA->cbBuf);
1184 if (!cbToWrite)
1185 break;
1186
1187 /* Do not write more than available. */
1188 if (cbToWrite > cbBuf)
1189 cbToWrite = cbBuf;
1190
1191 Assert(cbToWrite % 2 == 0);
1192 memcpy(pStreamALSA->pvBuf, pvBuf, cbToWrite);
1193
1194 snd_pcm_sframes_t csWritten = 0;
1195
1196 /* Don't try infinitely on recoverable errors. */
1197 unsigned iTry;
1198 for (iTry = 0; iTry < ALSA_RECOVERY_TRIES_MAX; iTry++)
1199 {
1200 csWritten = snd_pcm_writei(pStreamALSA->phPCM, pStreamALSA->pvBuf,
1201 PDMAUDIOSTREAMCFG_B2S(pCfg, cbToWrite));
1202 if (csWritten <= 0)
1203 {
1204 switch (csWritten)
1205 {
1206 case 0:
1207 {
1208 LogFunc(("Failed to write %zu bytes\n", cbToWrite));
1209 rc = VERR_ACCESS_DENIED;
1210 break;
1211 }
1212
1213 case -EPIPE:
1214 {
1215 rc = alsaStreamRecover(pStreamALSA->phPCM);
1216 if (RT_FAILURE(rc))
1217 break;
1218
1219 LogFlowFunc(("Recovered from playback\n"));
1220 continue;
1221 }
1222
1223 case -ESTRPIPE:
1224 {
1225 /* Stream was suspended and waiting for a recovery. */
1226 rc = alsaStreamResume(pStreamALSA->phPCM);
1227 if (RT_FAILURE(rc))
1228 {
1229 LogRel(("ALSA: Failed to resume output stream\n"));
1230 break;
1231 }
1232
1233 LogFlowFunc(("Resumed suspended output stream\n"));
1234 continue;
1235 }
1236
1237 default:
1238 LogFlowFunc(("Failed to write %RU32 bytes, error unknown\n", cbToWrite));
1239 rc = VERR_GENERAL_FAILURE; /** @todo */
1240 break;
1241 }
1242 }
1243 else
1244 break;
1245 } /* For number of tries. */
1246
1247 if ( iTry == ALSA_RECOVERY_TRIES_MAX
1248 && csWritten <= 0)
1249 rc = VERR_BROKEN_PIPE;
1250
1251 if (RT_FAILURE(rc))
1252 break;
1253
1254 cbWrittenTotal = PDMAUDIOSTREAMCFG_S2B(pCfg, csWritten);
1255
1256 } while (0);
1257
1258 if (RT_SUCCESS(rc))
1259 {
1260 if (pcbWritten)
1261 *pcbWritten = cbWrittenTotal;
1262 }
1263
1264 return rc;
1265}
1266
1267
1268static int alsaDestroyStreamIn(PALSAAUDIOSTREAM pStreamALSA)
1269{
1270 alsaStreamClose(&pStreamALSA->phPCM);
1271
1272 if (pStreamALSA->pvBuf)
1273 {
1274 RTMemFree(pStreamALSA->pvBuf);
1275 pStreamALSA->pvBuf = NULL;
1276 }
1277
1278 return VINF_SUCCESS;
1279}
1280
1281
1282static int alsaDestroyStreamOut(PALSAAUDIOSTREAM pStreamALSA)
1283{
1284 alsaStreamClose(&pStreamALSA->phPCM);
1285
1286 if (pStreamALSA->pvBuf)
1287 {
1288 RTMemFree(pStreamALSA->pvBuf);
1289 pStreamALSA->pvBuf = NULL;
1290 }
1291
1292 return VINF_SUCCESS;
1293}
1294
1295
1296static int alsaCreateStreamOut(PALSAAUDIOSTREAM pStreamALSA, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1297{
1298 snd_pcm_t *phPCM = NULL;
1299
1300 int rc;
1301
1302 do
1303 {
1304 ALSAAUDIOSTREAMCFG req;
1305 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);
1306 req.freq = pCfgReq->Props.uHz;
1307 req.nchannels = pCfgReq->Props.cChannels;
1308 req.period_size = s_ALSAConf.period_size_out; /** @todo Make this configurable. */
1309 req.buffer_size = s_ALSAConf.buffer_size_out; /** @todo Make this configurable. */
1310
1311 ALSAAUDIOSTREAMCFG obt;
1312 rc = alsaStreamOpen(false /* fIn */, &req, &obt, &phPCM);
1313 if (RT_FAILURE(rc))
1314 break;
1315
1316 pCfgAcq->Props.uHz = obt.freq;
1317 pCfgAcq->Props.cChannels = obt.nchannels;
1318
1319 rc = alsaALSAToAudioProps(obt.fmt, &pCfgAcq->Props);
1320 if (RT_FAILURE(rc))
1321 break;
1322
1323 pCfgAcq->cSampleBufferHint = obt.samples * 4;
1324
1325 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
1326
1327 size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_S2B(pCfgAcq, 1);
1328 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
1329
1330 pStreamALSA->pvBuf = RTMemAllocZ(cbBuf);
1331 if (!pStreamALSA->pvBuf)
1332 {
1333 LogRel(("ALSA: Not enough memory for output DAC buffer (%RU32 samples, %zu bytes)\n", obt.samples, cbBuf));
1334 rc = VERR_NO_MEMORY;
1335 break;
1336 }
1337
1338 pStreamALSA->cbBuf = cbBuf;
1339 pStreamALSA->phPCM = phPCM;
1340 }
1341 while (0);
1342
1343 if (RT_FAILURE(rc))
1344 alsaStreamClose(&phPCM);
1345
1346 LogFlowFuncLeaveRC(rc);
1347 return rc;
1348}
1349
1350
1351static int alsaCreateStreamIn(PALSAAUDIOSTREAM pStreamALSA, PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1352{
1353 int rc;
1354
1355 snd_pcm_t *phPCM = NULL;
1356
1357 do
1358 {
1359 ALSAAUDIOSTREAMCFG req;
1360 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);
1361 req.freq = pCfgReq->Props.uHz;
1362 req.nchannels = pCfgReq->Props.cChannels;
1363 req.period_size = s_ALSAConf.period_size_in; /** @todo Make this configurable. */
1364 req.buffer_size = s_ALSAConf.buffer_size_in; /** @todo Make this configurable. */
1365
1366 ALSAAUDIOSTREAMCFG obt;
1367 rc = alsaStreamOpen(true /* fIn */, &req, &obt, &phPCM);
1368 if (RT_FAILURE(rc))
1369 break;
1370
1371 pCfgAcq->Props.uHz = obt.freq;
1372 pCfgAcq->Props.cChannels = obt.nchannels;
1373
1374 rc = alsaALSAToAudioProps(obt.fmt, &pCfgAcq->Props);
1375 if (RT_FAILURE(rc))
1376 break;
1377
1378 pCfgAcq->cSampleBufferHint = obt.samples;
1379
1380 AssertBreakStmt(obt.samples, rc = VERR_INVALID_PARAMETER);
1381
1382 size_t cbBuf = obt.samples * PDMAUDIOSTREAMCFG_S2B(pCfgAcq, 1);
1383 AssertBreakStmt(cbBuf, rc = VERR_INVALID_PARAMETER);
1384
1385 pStreamALSA->pvBuf = RTMemAlloc(cbBuf);
1386 if (!pStreamALSA->pvBuf)
1387 {
1388 LogRel(("ALSA: Not enough memory for input ADC buffer (%RU32 samples, %zu bytes)\n", obt.samples, cbBuf));
1389 rc = VERR_NO_MEMORY;
1390 break;
1391 }
1392
1393 pStreamALSA->cbBuf = cbBuf;
1394 pStreamALSA->phPCM = phPCM;
1395 }
1396 while (0);
1397
1398 if (RT_FAILURE(rc))
1399 alsaStreamClose(&phPCM);
1400
1401 LogFlowFuncLeaveRC(rc);
1402 return rc;
1403}
1404
1405
1406static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
1407{
1408 int rc;
1409 switch (enmStreamCmd)
1410 {
1411 case PDMAUDIOSTREAMCMD_ENABLE:
1412 case PDMAUDIOSTREAMCMD_RESUME:
1413 rc = drvHostALSAAudioStreamCtl(pStreamALSA->phPCM, false /* fStop */);
1414 break;
1415
1416 case PDMAUDIOSTREAMCMD_DISABLE:
1417 case PDMAUDIOSTREAMCMD_PAUSE:
1418 rc = drvHostALSAAudioStreamCtl(pStreamALSA->phPCM, true /* fStop */);
1419 break;
1420
1421 default:
1422 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1423 rc = VERR_INVALID_PARAMETER;
1424 break;
1425 }
1426
1427 return rc;
1428}
1429
1430
1431static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
1432{
1433 int rc;
1434 switch (enmStreamCmd)
1435 {
1436 case PDMAUDIOSTREAMCMD_ENABLE:
1437 case PDMAUDIOSTREAMCMD_RESUME:
1438 rc = drvHostALSAAudioStreamCtl(pStreamALSA->phPCM, false /* fStop */);
1439 break;
1440
1441 case PDMAUDIOSTREAMCMD_DISABLE:
1442 case PDMAUDIOSTREAMCMD_PAUSE:
1443 rc = drvHostALSAAudioStreamCtl(pStreamALSA->phPCM, true /* fStop */);
1444 break;
1445
1446 default:
1447 AssertMsgFailed(("Invalid command %ld\n", enmStreamCmd));
1448 rc = VERR_INVALID_PARAMETER;
1449 break;
1450 }
1451
1452 return rc;
1453}
1454
1455
1456/**
1457 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
1458 */
1459static DECLCALLBACK(int) drvHostALSAAudioGetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
1460{
1461 RT_NOREF(pInterface);
1462 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
1463
1464 pBackendCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAM);
1465 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM);
1466
1467 /* Enumerate sound devices. */
1468 char **pszHints;
1469 int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);
1470 if (err == 0)
1471 {
1472 char** pszHintCur = pszHints;
1473 while (*pszHintCur != NULL)
1474 {
1475 char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");
1476 bool fSkip = !pszDev
1477 || !RTStrICmp("null", pszDev);
1478 if (fSkip)
1479 {
1480 if (pszDev)
1481 free(pszDev);
1482 pszHintCur++;
1483 continue;
1484 }
1485
1486 char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");
1487 if (pszIOID)
1488 {
1489#if 0
1490 if (!RTStrICmp("input", pszIOID))
1491
1492 else if (!RTStrICmp("output", pszIOID))
1493#endif
1494 }
1495 else /* NULL means bidirectional, input + output. */
1496 {
1497 }
1498
1499 LogRel2(("ALSA: Found %s device: %s\n", pszIOID ? RTStrToLower(pszIOID) : "bidirectional", pszDev));
1500
1501 /* Special case for ALSAAudio. */
1502 if ( pszDev
1503 && RTStrIStr("pulse", pszDev) != NULL)
1504 LogRel2(("ALSA: ALSAAudio plugin in use\n"));
1505
1506 if (pszIOID)
1507 free(pszIOID);
1508
1509 if (pszDev)
1510 free(pszDev);
1511
1512 pszHintCur++;
1513 }
1514
1515 snd_device_name_free_hint((void **)pszHints);
1516 pszHints = NULL;
1517 }
1518 else
1519 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));
1520
1521 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
1522 pBackendCfg->cMaxStreamsIn = 1;
1523 pBackendCfg->cMaxStreamsOut = 1;
1524
1525 return VINF_SUCCESS;
1526}
1527
1528
1529/**
1530 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
1531 */
1532static DECLCALLBACK(void) drvHostALSAAudioShutdown(PPDMIHOSTAUDIO pInterface)
1533{
1534 RT_NOREF(pInterface);
1535}
1536
1537
1538/**
1539 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1540 */
1541static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostALSAAudioGetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1542{
1543 RT_NOREF(enmDir);
1544 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1545
1546 return PDMAUDIOBACKENDSTS_RUNNING;
1547}
1548
1549
1550/**
1551 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1552 */
1553static DECLCALLBACK(int) drvHostALSAAudioStreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1554 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1555{
1556 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1557 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1558 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1559 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1560
1561 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1562
1563 int rc;
1564 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1565 rc = alsaCreateStreamIn (pStreamALSA, pCfgReq, pCfgAcq);
1566 else
1567 rc = alsaCreateStreamOut(pStreamALSA, pCfgReq, pCfgAcq);
1568
1569 if (RT_SUCCESS(rc))
1570 {
1571 pStreamALSA->pCfg = DrvAudioHlpStreamCfgDup(pCfgAcq);
1572 if (!pStreamALSA->pCfg)
1573 rc = VERR_NO_MEMORY;
1574 }
1575
1576 return rc;
1577}
1578
1579
1580/**
1581 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1582 */
1583static DECLCALLBACK(int) drvHostALSAAudioStreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1584{
1585 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1586 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1587
1588 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1589
1590 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
1591 return VINF_SUCCESS;
1592
1593 int rc;
1594 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
1595 rc = alsaDestroyStreamIn(pStreamALSA);
1596 else
1597 rc = alsaDestroyStreamOut(pStreamALSA);
1598
1599 if (RT_SUCCESS(rc))
1600 {
1601 DrvAudioHlpStreamCfgFree(pStreamALSA->pCfg);
1602 pStreamALSA->pCfg = NULL;
1603 }
1604
1605 return rc;
1606}
1607
1608
1609/**
1610 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1611 */
1612static DECLCALLBACK(int) drvHostALSAAudioStreamControl(PPDMIHOSTAUDIO pInterface,
1613 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1614{
1615 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1616 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1617
1618 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1619
1620 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
1621 return VINF_SUCCESS;
1622
1623 int rc;
1624 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
1625 rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd);
1626 else
1627 rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd);
1628
1629 return rc;
1630}
1631
1632
1633/**
1634 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1635 */
1636static DECLCALLBACK(uint32_t) drvHostALSAAudioStreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1637{
1638 RT_NOREF(pInterface);
1639
1640 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1641
1642 uint32_t cbAvail = 0;
1643
1644 snd_pcm_sframes_t cFramesAvail;
1645 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
1646 if (RT_SUCCESS(rc))
1647 cbAvail = PDMAUDIOSTREAMCFG_S2B(pStreamALSA->pCfg, cFramesAvail);
1648
1649 return cbAvail;
1650}
1651
1652
1653/**
1654 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1655 */
1656static DECLCALLBACK(uint32_t) drvHostALSAAudioStreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1657{
1658 RT_NOREF(pInterface);
1659
1660 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1661
1662 uint32_t cbAvail = 0;
1663
1664 snd_pcm_sframes_t cFramesAvail;
1665 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
1666 if ( RT_SUCCESS(rc)
1667 && (uint32_t)cFramesAvail >= pStreamALSA->Out.cSamplesMin)
1668 {
1669 cbAvail = PDMAUDIOSTREAMCFG_S2B(pStreamALSA->pCfg, cFramesAvail);
1670 }
1671
1672 return cbAvail;
1673}
1674
1675
1676/**
1677 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1678 */
1679static DECLCALLBACK(PDMAUDIOSTRMSTS) drvHostALSAAudioStreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1680{
1681 RT_NOREF(pInterface, pStream);
1682
1683 return (PDMAUDIOSTRMSTS_FLAG_INITIALIZED | PDMAUDIOSTRMSTS_FLAG_ENABLED);
1684}
1685
1686
1687/**
1688 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1689 */
1690static DECLCALLBACK(int) drvHostALSAAudioStreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1691{
1692 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1693 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1694
1695 LogFlowFuncEnter();
1696
1697 /* Nothing to do here for ALSA. */
1698 return VINF_SUCCESS;
1699}
1700
1701
1702/**
1703 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1704 */
1705static DECLCALLBACK(void *) drvHostALSAAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1706{
1707 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1708 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1709 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1710 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1711
1712 return NULL;
1713}
1714
1715
1716/**
1717 * Construct a DirectSound Audio driver instance.
1718 *
1719 * @copydoc FNPDMDRVCONSTRUCT
1720 */
1721static DECLCALLBACK(int) drvHostAlsaAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1722{
1723 RT_NOREF(pCfg, fFlags);
1724 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1725 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1726 LogRel(("Audio: Initializing ALSA driver\n"));
1727
1728 /*
1729 * Init the static parts.
1730 */
1731 pThis->pDrvIns = pDrvIns;
1732 /* IBase */
1733 pDrvIns->IBase.pfnQueryInterface = drvHostALSAAudioQueryInterface;
1734 /* IHostAudio */
1735 PDMAUDIO_IHOSTAUDIO_CALLBACKS(drvHostALSAAudio);
1736
1737 return VINF_SUCCESS;
1738}
1739
1740
1741/**
1742 * Char driver registration record.
1743 */
1744const PDMDRVREG g_DrvHostALSAAudio =
1745{
1746 /* u32Version */
1747 PDM_DRVREG_VERSION,
1748 /* szName */
1749 "ALSAAudio",
1750 /* szRCMod */
1751 "",
1752 /* szR0Mod */
1753 "",
1754 /* pszDescription */
1755 "ALSA host audio driver",
1756 /* fFlags */
1757 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1758 /* fClass. */
1759 PDM_DRVREG_CLASS_AUDIO,
1760 /* cMaxInstances */
1761 ~0U,
1762 /* cbInstance */
1763 sizeof(DRVHOSTALSAAUDIO),
1764 /* pfnConstruct */
1765 drvHostAlsaAudioConstruct,
1766 /* pfnDestruct */
1767 NULL,
1768 /* pfnRelocate */
1769 NULL,
1770 /* pfnIOCtl */
1771 NULL,
1772 /* pfnPowerOn */
1773 NULL,
1774 /* pfnReset */
1775 NULL,
1776 /* pfnSuspend */
1777 NULL,
1778 /* pfnResume */
1779 NULL,
1780 /* pfnAttach */
1781 NULL,
1782 /* pfnDetach */
1783 NULL,
1784 /* pfnPowerOff */
1785 NULL,
1786 /* pfnSoftReset */
1787 NULL,
1788 /* u32EndVersion */
1789 PDM_DRVREG_VERSION
1790};
1791
1792#if 0 /* unused */
1793static struct audio_option alsa_options[] =
1794{
1795 {"DACSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_out,
1796 "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1797 {"DACPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_out,
1798 "DAC period size", &s_ALSAConf.period_size_out_overriden, 0},
1799 {"DACBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_out,
1800 "DAC buffer size", &s_ALSAConf.buffer_size_out_overriden, 0},
1801
1802 {"ADCSizeInUsec", AUD_OPT_BOOL, &s_ALSAConf.size_in_usec_in,
1803 "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
1804 {"ADCPeriodSize", AUD_OPT_INT, &s_ALSAConf.period_size_in,
1805 "ADC period size", &s_ALSAConf.period_size_in_overriden, 0},
1806 {"ADCBufferSize", AUD_OPT_INT, &s_ALSAConf.buffer_size_in,
1807 "ADC buffer size", &s_ALSAConf.buffer_size_in_overriden, 0},
1808
1809 {"Threshold", AUD_OPT_INT, &s_ALSAConf.threshold,
1810 "(undocumented)", NULL, 0},
1811
1812 {"DACDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_out,
1813 "DAC device name (for instance dmix)", NULL, 0},
1814
1815 {"ADCDev", AUD_OPT_STR, &s_ALSAConf.pcm_name_in,
1816 "ADC device name", NULL, 0}
1817};
1818#endif
Note: See TracBrowser for help on using the repository browser.

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