VirtualBox

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

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

Audio/DrvHostALSAAudio.cpp: Fixed recording by actually doing a snd_pcm_start() after preparing the device. This never has been the case and only worked by coincidence before (as snd_pcm_readi() internally starts the device if never done so).

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