VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DrvHostAudioAlsa.cpp@ 88253

最後變更 在這個檔案從88253是 88235,由 vboxsync 提交於 4 年 前

Audio: File header adjustments. bugref:9890

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 51.4 KB
 
1/* $Id: DrvHostAudioAlsa.cpp 88235 2021-03-22 10:44:43Z vboxsync $ */
2/** @file
3 * Host audio driver - Advanced Linux Sound Architecture (ALSA).
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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#include <VBox/vmm/pdmaudioinline.h>
53
54RT_C_DECLS_BEGIN
55#include "DrvHostAudioAlsaStubs.h"
56#include "DrvHostAudioAlsaStubsMangling.h"
57RT_C_DECLS_END
58
59#include <alsa/asoundlib.h>
60#include <alsa/control.h> /* For device enumeration. */
61
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_UOFFSETOF(DRVHOSTALSAAUDIO, IHostAudio)) )
72
73
74/*********************************************************************************************************************************
75* Structures *
76*********************************************************************************************************************************/
77
78/**
79 * Structure for maintaining an ALSA audio stream.
80 */
81typedef struct ALSAAUDIOSTREAM
82{
83 /** The stream's acquired configuration. */
84 PPDMAUDIOSTREAMCFG pCfg;
85 /** Pointer to allocated ALSA PCM configuration to use. */
86 snd_pcm_t *phPCM;
87 /** Scratch buffer. */
88 void *pvBuf;
89 /** Size (in bytes) of allocated scratch buffer. */
90 size_t cbBuf;
91} ALSAAUDIOSTREAM, *PALSAAUDIOSTREAM;
92
93/* latency = period_size * periods / (rate * bytes_per_frame) */
94
95static int alsaStreamRecover(snd_pcm_t *phPCM);
96
97/**
98 * Host Alsa audio driver instance data.
99 * @implements PDMIAUDIOCONNECTOR
100 */
101typedef struct DRVHOSTALSAAUDIO
102{
103 /** Pointer to the driver instance structure. */
104 PPDMDRVINS pDrvIns;
105 /** Pointer to host audio interface. */
106 PDMIHOSTAUDIO IHostAudio;
107 /** Error count for not flooding the release log.
108 * UINT32_MAX for unlimited logging. */
109 uint32_t cLogErrors;
110 /** Default input device name. */
111 char szDefaultIn[256];
112 /** Default output device name. */
113 char szDefaultOut[256];
114} DRVHOSTALSAAUDIO, *PDRVHOSTALSAAUDIO;
115
116/** Maximum number of tries to recover a broken pipe. */
117#define ALSA_RECOVERY_TRIES_MAX 5
118
119/**
120 * Structure for maintaining an ALSA audio stream configuration.
121 */
122typedef struct ALSAAUDIOSTREAMCFG
123{
124 unsigned int freq;
125 /** PCM sound format. */
126 snd_pcm_format_t fmt;
127 /** PCM data access type. */
128 snd_pcm_access_t access;
129 /** Whether resampling should be performed by alsalib or not. */
130 int resample;
131 /** Number of audio channels. */
132 int nchannels;
133 /** Buffer size (in audio frames). */
134 unsigned long buffer_size;
135 /** Periods (in audio frames). */
136 unsigned long period_size;
137 /** For playback: Starting to play threshold (in audio frames).
138 * For Capturing: Starting to capture threshold (in audio frames). */
139 unsigned long threshold;
140} ALSAAUDIOSTREAMCFG, *PALSAAUDIOSTREAMCFG;
141
142
143/**
144 * Converts internal audio PCM properties to an ALSA PCM format.
145 *
146 * @returns Converted ALSA PCM format.
147 * @param pProps Internal audio PCM configuration to convert.
148 */
149static snd_pcm_format_t alsaAudioPropsToALSA(PPDMAUDIOPCMPROPS pProps)
150{
151 switch (pProps->cbSample)
152 {
153 case 1:
154 return pProps->fSigned ? SND_PCM_FORMAT_S8 : SND_PCM_FORMAT_U8;
155
156 case 2:
157 return pProps->fSigned ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U16_LE;
158
159 case 4:
160 return pProps->fSigned ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_U32_LE;
161
162 default:
163 break;
164 }
165
166 AssertMsgFailed(("%RU8 bytes not supported\n", pProps->cbSample));
167 return SND_PCM_FORMAT_U8;
168}
169
170
171/**
172 * Converts an ALSA PCM format to internal PCM properties.
173 *
174 * @returns VBox status code.
175 * @param fmt ALSA PCM format to convert.
176 * @param pProps Where to store the converted PCM properties on success.
177 */
178static int alsaALSAToAudioProps(snd_pcm_format_t fmt, PPDMAUDIOPCMPROPS pProps)
179{
180 switch (fmt)
181 {
182 case SND_PCM_FORMAT_S8:
183 pProps->cbSample = 1;
184 pProps->fSigned = true;
185 pProps->fSwapEndian = false;
186 break;
187
188 case SND_PCM_FORMAT_U8:
189 pProps->cbSample = 1;
190 pProps->fSigned = false;
191 pProps->fSwapEndian = false;
192 break;
193
194 case SND_PCM_FORMAT_S16_LE:
195 pProps->cbSample = 2;
196 pProps->fSigned = true;
197 pProps->fSwapEndian = false;
198 break;
199
200 case SND_PCM_FORMAT_U16_LE:
201 pProps->cbSample = 2;
202 pProps->fSigned = false;
203 pProps->fSwapEndian = false;
204 break;
205
206 case SND_PCM_FORMAT_S16_BE:
207 pProps->cbSample = 2;
208 pProps->fSigned = true;
209#ifdef RT_LITTLE_ENDIAN
210 pProps->fSwapEndian = true;
211#endif
212 break;
213
214 case SND_PCM_FORMAT_U16_BE:
215 pProps->cbSample = 2;
216 pProps->fSigned = false;
217#ifdef RT_LITTLE_ENDIAN
218 pProps->fSwapEndian = true;
219#endif
220 break;
221
222 case SND_PCM_FORMAT_S32_LE:
223 pProps->cbSample = 4;
224 pProps->fSigned = true;
225 pProps->fSwapEndian = false;
226 break;
227
228 case SND_PCM_FORMAT_U32_LE:
229 pProps->cbSample = 4;
230 pProps->fSigned = false;
231 pProps->fSwapEndian = false;
232 break;
233
234 case SND_PCM_FORMAT_S32_BE:
235 pProps->cbSample = 4;
236 pProps->fSigned = true;
237#ifdef RT_LITTLE_ENDIAN
238 pProps->fSwapEndian = true;
239#endif
240 break;
241
242 case SND_PCM_FORMAT_U32_BE:
243 pProps->cbSample = 4;
244 pProps->fSigned = false;
245#ifdef RT_LITTLE_ENDIAN
246 pProps->fSwapEndian = true;
247#endif
248 break;
249
250 default:
251 AssertMsgFailedReturn(("Format %d not supported\n", fmt), VERR_NOT_SUPPORTED);
252 }
253
254 AssertReturn(pProps->cbSample > 0, VERR_NOT_SUPPORTED);
255 AssertReturn(pProps->cChannels > 0, VERR_INVALID_PARAMETER);
256
257 pProps->cShift = PDMAUDIOPCMPROPS_MAKE_SHIFT_PARMS(pProps->cbSample, pProps->cChannels);
258
259 return VINF_SUCCESS;
260}
261
262
263/**
264 * Closes an ALSA stream
265 *
266 * @returns VBox status code.
267 * @param pphPCM ALSA stream to close.
268 */
269static int alsaStreamClose(snd_pcm_t **pphPCM)
270{
271 if (!pphPCM || !*pphPCM)
272 return VINF_SUCCESS;
273
274 int rc;
275 int rc2 = snd_pcm_close(*pphPCM);
276 if (rc2)
277 {
278 LogRel(("ALSA: Closing PCM descriptor failed: %s\n", snd_strerror(rc2)));
279 rc = VERR_GENERAL_FAILURE; /** @todo */
280 }
281 else
282 {
283 *pphPCM = NULL;
284 rc = VINF_SUCCESS;
285 }
286
287 LogFlowFuncLeaveRC(rc);
288 return rc;
289}
290
291
292/**
293 * Sets the software parameters of an ALSA stream.
294 *
295 * @returns 0 on success, negative errno on failure.
296 * @param phPCM ALSA stream to set software parameters for.
297 * @param fIn Whether this is an input stream or not.
298 * @param pCfgReq Requested configuration to set.
299 * @param pCfgObt Obtained configuration on success. Might differ from requested configuration.
300 */
301static int alsaStreamSetSWParams(snd_pcm_t *phPCM, bool fIn, PALSAAUDIOSTREAMCFG pCfgReq, PALSAAUDIOSTREAMCFG pCfgObt)
302{
303 if (fIn) /* For input streams there's nothing to do in here right now. */
304 return VINF_SUCCESS;
305
306 snd_pcm_sw_params_t *pSWParms = NULL;
307 snd_pcm_sw_params_alloca(&pSWParms);
308 AssertReturn(pSWParms, -ENOMEM);
309
310 int err = snd_pcm_sw_params_current(phPCM, pSWParms);
311 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to get current software parameters: %s\n", snd_strerror(err)), err);
312
313 /* Must make sure we don't require ALSA to prebuffer more than
314 it has buffer space for, because that means output will
315 never start. */
316 unsigned long cFramesPreBuffer = pCfgReq->threshold;
317 if (cFramesPreBuffer >= pCfgObt->buffer_size - pCfgObt->buffer_size / 16)
318 {
319 cFramesPreBuffer = pCfgObt->buffer_size - pCfgObt->buffer_size / 16;
320 LogRel2(("ALSA: Reducing threshold from %lu to %lu due to buffer size of %lu.\n",
321 pCfgReq->threshold, cFramesPreBuffer, pCfgObt->buffer_size));
322 }
323 err = snd_pcm_sw_params_set_start_threshold(phPCM, pSWParms, cFramesPreBuffer);
324 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set software threshold to %lu: %s\n", cFramesPreBuffer, snd_strerror(err)), err);
325
326 err = snd_pcm_sw_params_set_avail_min(phPCM, pSWParms, pCfgReq->period_size);
327 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set available minimum to %lu: %s\n", pCfgReq->period_size, snd_strerror(err)), err);
328
329 /* Commit the software parameters: */
330 err = snd_pcm_sw_params(phPCM, pSWParms);
331 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set new software parameters: %s\n", snd_strerror(err)), err);
332
333 /* Get the actual parameters: */
334 err = snd_pcm_sw_params_get_start_threshold(pSWParms, &pCfgObt->threshold);
335 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to get start threshold: %s\n", snd_strerror(err)), err);
336
337 LogRel2(("ALSA: SW params: %ul frames threshold, %ul frame avail minimum\n",
338 pCfgObt->threshold, pCfgReq->period_size));
339 return 0;
340}
341
342
343/**
344 * Sets the hardware parameters of an ALSA stream.
345 *
346 * @returns 0 on success, negative errno on failure.
347 * @param phPCM ALSA stream to set software parameters for.
348 * @param pCfgReq Requested configuration to set.
349 * @param pCfgObt Obtained configuration on success. Might differ from
350 * requested configuration.
351 */
352static int alsaStreamSetHwParams(snd_pcm_t *phPCM, PALSAAUDIOSTREAMCFG pCfgReq, PALSAAUDIOSTREAMCFG pCfgObt)
353{
354 /*
355 * Get the current hardware parameters.
356 */
357 snd_pcm_hw_params_t *pHWParms = NULL;
358 snd_pcm_hw_params_alloca(&pHWParms);
359 AssertReturn(pHWParms, -ENOMEM);
360
361 int err = snd_pcm_hw_params_any(phPCM, pHWParms);
362 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to initialize hardware parameters: %s\n", snd_strerror(err)), err);
363
364 /*
365 * Modify them according to pCfgReq.
366 * We update pCfgObt as we go for parameters set by "near" methods.
367 */
368 /* We'll use snd_pcm_writei/snd_pcm_readi: */
369 err = snd_pcm_hw_params_set_access(phPCM, pHWParms, SND_PCM_ACCESS_RW_INTERLEAVED);
370 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set access type: %s\n", snd_strerror(err)), err);
371
372 /* Set the format, frequency and channel count. */
373 err = snd_pcm_hw_params_set_format(phPCM, pHWParms, pCfgReq->fmt);
374 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set audio format to %d: %s\n", pCfgReq->fmt, snd_strerror(err)), err);
375
376 unsigned int uFreq = pCfgReq->freq;
377 err = snd_pcm_hw_params_set_rate_near(phPCM, pHWParms, &uFreq, NULL /*dir*/);
378 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set frequency to %uHz: %s\n", pCfgReq->freq, snd_strerror(err)), err);
379 pCfgObt->freq = uFreq;
380
381 unsigned int cChannels = pCfgReq->nchannels;
382 err = snd_pcm_hw_params_set_channels_near(phPCM, pHWParms, &cChannels);
383 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set number of channels to %d\n", pCfgReq->nchannels), err);
384 AssertLogRelMsgReturn(cChannels == 1 || cChannels == 2, ("ALSA: Number of audio channels (%u) not supported\n", cChannels), -1);
385 pCfgObt->nchannels = cChannels;
386
387 /* The period size (reportedly frame count per hw interrupt): */
388 int dir = 0;
389 snd_pcm_uframes_t minval = pCfgReq->period_size;
390 err = snd_pcm_hw_params_get_period_size_min(pHWParms, &minval, &dir);
391 AssertLogRelMsgReturn(err >= 0, ("ALSA: Could not determine minimal period size: %s\n", snd_strerror(err)), err);
392
393 snd_pcm_uframes_t period_size_f = pCfgReq->period_size;
394 if (period_size_f < minval)
395 period_size_f = minval;
396 err = snd_pcm_hw_params_set_period_size_near(phPCM, pHWParms, &period_size_f, 0);
397 LogRel2(("ALSA: Period size is: %lu frames (min %lu, requested %lu)\n", period_size_f, minval, pCfgReq->period_size));
398 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set period size %d (%s)\n", period_size_f, snd_strerror(err)), err);
399
400 /* The buffer size: */
401 minval = pCfgReq->buffer_size;
402 err = snd_pcm_hw_params_get_buffer_size_min(pHWParms, &minval);
403 AssertLogRelMsgReturn(err >= 0, ("ALSA: Could not retrieve minimal buffer size: %s\n", snd_strerror(err)), err);
404
405 snd_pcm_uframes_t buffer_size_f = pCfgReq->buffer_size;
406 if (buffer_size_f < minval)
407 buffer_size_f = minval;
408 err = snd_pcm_hw_params_set_buffer_size_near(phPCM, pHWParms, &buffer_size_f);
409 LogRel2(("ALSA: Buffer size is: %lu frames (min %lu, requested %lu)\n", buffer_size_f, minval, pCfgReq->buffer_size));
410 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to set near buffer size %RU32: %s\n", buffer_size_f, snd_strerror(err)), err);
411
412 /*
413 * Set the hardware parameters.
414 */
415 err = snd_pcm_hw_params(phPCM, pHWParms);
416 AssertLogRelMsgReturn(err >= 0, ("ALSA: Failed to apply audio parameters: %s\n", snd_strerror(err)), err);
417
418 /*
419 * Get relevant parameters and put them in the pCfgObt structure.
420 */
421 snd_pcm_uframes_t obt_buffer_size = buffer_size_f;
422 err = snd_pcm_hw_params_get_buffer_size(pHWParms, &obt_buffer_size);
423 AssertLogRelMsgStmt(err >= 0, ("ALSA: Failed to get buffer size: %s\n", snd_strerror(err)), obt_buffer_size = buffer_size_f);
424 pCfgObt->buffer_size = obt_buffer_size;
425
426 snd_pcm_uframes_t obt_period_size = period_size_f;
427 err = snd_pcm_hw_params_get_period_size(pHWParms, &obt_period_size, &dir);
428 AssertLogRelMsgStmt(err >= 0, ("ALSA: Failed to get period size: %s\n", snd_strerror(err)), obt_period_size = period_size_f);
429 pCfgObt->period_size = obt_period_size;
430
431 pCfgObt->access = pCfgReq->access;
432 pCfgObt->fmt = pCfgReq->fmt;
433
434 LogRel2(("ALSA: HW params: %u Hz, %ul frames period, %ul frames buffer, %u channel(s), fmt=%d, access=%d\n",
435 pCfgObt->freq, pCfgObt->period_size, pCfgObt->buffer_size, pCfgObt->nchannels, pCfgObt->fmt, pCfgObt->access));
436 return 0;
437}
438
439
440/**
441 * Opens (creates) an ALSA stream.
442 *
443 * @returns VBox status code.
444 * @param pszDev The name of the device to open.
445 * @param fIn Whether this is an input stream to create or not.
446 * @param pCfgReq Requested configuration to create stream with.
447 * @param pCfgObt Obtained configuration the stream got created on success.
448 * @param pphPCM Where to store the ALSA stream handle on success.
449 */
450static int alsaStreamOpen(const char *pszDev, bool fIn, PALSAAUDIOSTREAMCFG pCfgReq,
451 PALSAAUDIOSTREAMCFG pCfgObt, snd_pcm_t **pphPCM)
452{
453 AssertLogRelMsgReturn(pszDev && *pszDev,
454 ("ALSA: Invalid or no %s device name set\n", fIn ? "input" : "output"),
455 VERR_INVALID_NAME);
456
457 /*
458 * Open the stream.
459 */
460 int rc = VERR_AUDIO_STREAM_COULD_NOT_CREATE;
461 snd_pcm_t *phPCM = NULL;
462 LogRel(("ALSA: Using %s device \"%s\"\n", fIn ? "input" : "output", pszDev));
463 int err = snd_pcm_open(&phPCM, pszDev,
464 fIn ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
465 SND_PCM_NONBLOCK);
466 if (err >= 0)
467 {
468 err = snd_pcm_nonblock(phPCM, 1);
469 if (err >= 0)
470 {
471 /*
472 * Configure hardware stream parameters.
473 */
474 err = alsaStreamSetHwParams(phPCM, pCfgReq, pCfgObt);
475 if (err >= 0)
476 {
477 /*
478 * Prepare it.
479 */
480 rc = VERR_AUDIO_BACKEND_INIT_FAILED;
481 err = snd_pcm_prepare(phPCM);
482 if (err >= 0)
483 {
484 /*
485 * Configure software stream parameters and we're done.
486 */
487 rc = alsaStreamSetSWParams(phPCM, fIn, pCfgReq, pCfgObt);
488 if (RT_SUCCESS(rc))
489 {
490 *pphPCM = phPCM;
491 return VINF_SUCCESS;
492 }
493 }
494 else
495 LogRel(("ALSA: snd_pcm_prepare failed: %s\n", snd_strerror(err)));
496 }
497 }
498 else
499 LogRel(("ALSA: Error setting output non-blocking mode: %s\n", snd_strerror(err)));
500 alsaStreamClose(&phPCM);
501 }
502 else
503 LogRel(("ALSA: Failed to open \"%s\" as %s device: %s\n", pszDev, fIn ? "input" : "output", snd_strerror(err)));
504 return rc;
505}
506
507
508#ifdef DEBUG
509static void alsaDbgErrorHandler(const char *file, int line, const char *function,
510 int err, const char *fmt, ...)
511{
512 /** @todo Implement me! */
513 RT_NOREF(file, line, function, err, fmt);
514}
515#endif
516
517/**
518 * Returns the available audio frames queued.
519 *
520 * @returns VBox status code.
521 * @param phPCM ALSA stream handle.
522 * @param pFramesAvail Where to store the available frames.
523 */
524static int alsaStreamGetAvail(snd_pcm_t *phPCM, snd_pcm_sframes_t *pFramesAvail)
525{
526 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
527 /* pFramesAvail is optional. */
528
529 int rc;
530
531 snd_pcm_sframes_t framesAvail = snd_pcm_avail_update(phPCM);
532 if (framesAvail < 0)
533 {
534 if (framesAvail == -EPIPE)
535 {
536 rc = alsaStreamRecover(phPCM);
537 if (RT_SUCCESS(rc))
538 framesAvail = snd_pcm_avail_update(phPCM);
539 }
540 else
541 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
542 }
543 else
544 rc = VINF_SUCCESS;
545
546 if (RT_SUCCESS(rc))
547 {
548 if (pFramesAvail)
549 *pFramesAvail = framesAvail;
550 }
551
552 LogFunc(("cFrames=%ld, rc=%Rrc\n", framesAvail, rc));
553 return rc;
554}
555
556/**
557 * Tries to recover an ALSA stream.
558 *
559 * @returns VBox status code.
560 * @param phPCM ALSA stream handle.
561 */
562static int alsaStreamRecover(snd_pcm_t *phPCM)
563{
564 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
565
566 int err = snd_pcm_prepare(phPCM);
567 if (err < 0)
568 {
569 LogFunc(("Failed to recover stream %p: %s\n", phPCM, snd_strerror(err)));
570 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
571 }
572
573 return VINF_SUCCESS;
574}
575
576/**
577 * Resumes an ALSA stream.
578 *
579 * @returns VBox status code.
580 * @param phPCM ALSA stream to resume.
581 */
582static int alsaStreamResume(snd_pcm_t *phPCM)
583{
584 AssertPtrReturn(phPCM, VERR_INVALID_POINTER);
585
586 int err = snd_pcm_resume(phPCM);
587 if (err < 0)
588 {
589 LogFunc(("Failed to resume stream %p: %s\n", phPCM, snd_strerror(err)));
590 return VERR_ACCESS_DENIED; /** @todo Find a better rc. */
591 }
592
593 return VINF_SUCCESS;
594}
595
596/**
597 * @interface_method_impl{PDMIHOSTAUDIO,pfnInit}
598 */
599static DECLCALLBACK(int) drvHostAlsaAudioHA_Init(PPDMIHOSTAUDIO pInterface)
600{
601 RT_NOREF(pInterface);
602
603 LogFlowFuncEnter();
604
605 int rc = audioLoadAlsaLib();
606 if (RT_FAILURE(rc))
607 LogRel(("ALSA: Failed to load the ALSA shared library, rc=%Rrc\n", rc));
608 else
609 {
610#ifdef DEBUG
611 snd_lib_error_set_handler(alsaDbgErrorHandler);
612#endif
613 }
614
615 return rc;
616}
617
618/**
619 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCapture}
620 */
621static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCapture(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
622 void *pvBuf, uint32_t uBufSize, uint32_t *puRead)
623{
624 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
625 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
626 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
627 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
628 /* pcbRead is optional. */
629
630 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
631
632 snd_pcm_sframes_t cAvail;
633 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cAvail);
634 if (RT_FAILURE(rc))
635 {
636 LogFunc(("Error getting number of captured frames, rc=%Rrc\n", rc));
637 return rc;
638 }
639
640 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg;
641 AssertPtr(pCfg);
642
643 if (!cAvail) /* No data yet? */
644 {
645 snd_pcm_state_t state = snd_pcm_state(pStreamALSA->phPCM);
646 switch (state)
647 {
648 case SND_PCM_STATE_PREPARED:
649 cAvail = PDMAUDIOSTREAMCFG_B2F(pCfg, uBufSize);
650 break;
651
652 case SND_PCM_STATE_SUSPENDED:
653 {
654 rc = alsaStreamResume(pStreamALSA->phPCM);
655 if (RT_FAILURE(rc))
656 break;
657
658 LogFlow(("Resuming suspended input stream\n"));
659 break;
660 }
661
662 default:
663 LogFlow(("No frames available, state=%d\n", state));
664 break;
665 }
666
667 if (!cAvail)
668 {
669 if (puRead)
670 *puRead = 0;
671 return VINF_SUCCESS;
672 }
673 }
674
675 /*
676 * Check how much we can read from the capture device without overflowing
677 * the mixer buffer.
678 */
679 size_t cbToRead = RT_MIN((size_t)PDMAUDIOSTREAMCFG_F2B(pCfg, cAvail), uBufSize);
680
681 LogFlowFunc(("cbToRead=%zu, cAvail=%RI32\n", cbToRead, cAvail));
682
683 uint32_t cbReadTotal = 0;
684
685 snd_pcm_uframes_t cToRead;
686 snd_pcm_sframes_t cRead;
687
688 while ( cbToRead
689 && RT_SUCCESS(rc))
690 {
691 cToRead = RT_MIN(PDMAUDIOSTREAMCFG_B2F(pCfg, cbToRead),
692 PDMAUDIOSTREAMCFG_B2F(pCfg, pStreamALSA->cbBuf));
693 AssertBreakStmt(cToRead, rc = VERR_NO_DATA);
694 cRead = snd_pcm_readi(pStreamALSA->phPCM, pStreamALSA->pvBuf, cToRead);
695 if (cRead <= 0)
696 {
697 switch (cRead)
698 {
699 case 0:
700 {
701 LogFunc(("No input frames available\n"));
702 rc = VERR_ACCESS_DENIED;
703 break;
704 }
705
706 case -EAGAIN:
707 {
708 /*
709 * Don't set error here because EAGAIN means there are no further frames
710 * available at the moment, try later. As we might have read some frames
711 * already these need to be processed instead.
712 */
713 cbToRead = 0;
714 break;
715 }
716
717 case -EPIPE:
718 {
719 rc = alsaStreamRecover(pStreamALSA->phPCM);
720 if (RT_FAILURE(rc))
721 break;
722
723 LogFlowFunc(("Recovered from capturing\n"));
724 continue;
725 }
726
727 default:
728 {
729 LogFunc(("Failed to read input frames: %s\n", snd_strerror(cRead)));
730 rc = VERR_GENERAL_FAILURE; /** @todo Fudge! */
731 break;
732 }
733 }
734 }
735 else
736 {
737 /*
738 * We should not run into a full mixer buffer or we loose samples and
739 * run into an endless loop if ALSA keeps producing samples ("null"
740 * capture device for example).
741 */
742 uint32_t cbRead = PDMAUDIOSTREAMCFG_F2B(pCfg, cRead);
743
744 memcpy(pvBuf, pStreamALSA->pvBuf, cbRead);
745
746 Assert(cbToRead >= cbRead);
747 cbToRead -= cbRead;
748 cbReadTotal += cbRead;
749 }
750 }
751
752 if (RT_SUCCESS(rc))
753 {
754 if (puRead)
755 *puRead = cbReadTotal;
756 }
757
758 return rc;
759}
760
761/**
762 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamPlay}
763 */
764static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamPlay(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
765 const void *pvBuf, uint32_t uBufSize, uint32_t *puWritten)
766{
767 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
768 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
769 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
770 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
771 AssertReturn(uBufSize, VERR_INVALID_PARAMETER);
772 /* puWritten is optional. */
773 Log4Func(("pvBuf=%p uBufSize=%#x (%u) state=%s - %s\n", pvBuf, uBufSize, uBufSize,
774 snd_pcm_state_name(snd_pcm_state(pStreamALSA->phPCM)), pStreamALSA->pCfg->szName));
775
776 PPDMAUDIOSTREAMCFG pCfg = pStreamALSA->pCfg;
777 AssertPtr(pCfg);
778
779 int rc;
780
781 uint32_t cbWrittenTotal = 0;
782
783 do
784 {
785 snd_pcm_sframes_t csAvail;
786 rc = alsaStreamGetAvail(pStreamALSA->phPCM, &csAvail);
787 if (RT_FAILURE(rc))
788 {
789 LogFunc(("Error getting number of playback frames, rc=%Rrc\n", rc));
790 break;
791 }
792
793 if (!csAvail)
794 break;
795
796 size_t cbToWrite = RT_MIN((unsigned)PDMAUDIOSTREAMCFG_F2B(pCfg, csAvail), pStreamALSA->cbBuf);
797 if (!cbToWrite)
798 break;
799
800 /* Do not write more than available. */
801 if (cbToWrite > uBufSize)
802 cbToWrite = uBufSize;
803
804 memcpy(pStreamALSA->pvBuf, pvBuf, cbToWrite);
805
806 snd_pcm_sframes_t csWritten = 0;
807
808 /* Don't try infinitely on recoverable errors. */
809 unsigned iTry;
810 for (iTry = 0; iTry < ALSA_RECOVERY_TRIES_MAX; iTry++)
811 {
812 csWritten = snd_pcm_writei(pStreamALSA->phPCM, pStreamALSA->pvBuf,
813 PDMAUDIOSTREAMCFG_B2F(pCfg, cbToWrite));
814 Log4Func(("snd_pcm_writei w/ cbToWrite=%u -> %ld (frames) [csAvail=%ld]\n", cbToWrite, csWritten, csAvail));
815 if (csWritten <= 0)
816 {
817 switch (csWritten)
818 {
819 case 0:
820 {
821 LogFunc(("Failed to write %zu bytes\n", cbToWrite));
822 rc = VERR_ACCESS_DENIED;
823 break;
824 }
825
826 case -EPIPE:
827 {
828 rc = alsaStreamRecover(pStreamALSA->phPCM);
829 if (RT_FAILURE(rc))
830 break;
831
832 LogFlowFunc(("Recovered from playback\n"));
833 continue;
834 }
835
836 case -ESTRPIPE:
837 {
838 /* Stream was suspended and waiting for a recovery. */
839 rc = alsaStreamResume(pStreamALSA->phPCM);
840 if (RT_FAILURE(rc))
841 {
842 LogRel(("ALSA: Failed to resume output stream\n"));
843 break;
844 }
845
846 LogFlowFunc(("Resumed suspended output stream\n"));
847 continue;
848 }
849
850 default:
851 LogFlowFunc(("Failed to write %RU32 bytes, error unknown\n", cbToWrite));
852 rc = VERR_GENERAL_FAILURE; /** @todo */
853 break;
854 }
855 }
856 else
857 break;
858 } /* For number of tries. */
859
860 if ( iTry == ALSA_RECOVERY_TRIES_MAX
861 && csWritten <= 0)
862 rc = VERR_BROKEN_PIPE;
863
864 if (RT_FAILURE(rc))
865 break;
866
867 cbWrittenTotal = PDMAUDIOSTREAMCFG_F2B(pCfg, csWritten);
868
869 } while (0);
870
871 if (RT_SUCCESS(rc))
872 {
873 if (puWritten)
874 *puWritten = cbWrittenTotal;
875 }
876
877 return rc;
878}
879
880/**
881 * Destroys an ALSA input stream.
882 *
883 * @returns VBox status code.
884 * @param pStreamALSA ALSA input stream to destroy.
885 */
886static int alsaDestroyStreamIn(PALSAAUDIOSTREAM pStreamALSA)
887{
888 alsaStreamClose(&pStreamALSA->phPCM);
889
890 if (pStreamALSA->pvBuf)
891 {
892 RTMemFree(pStreamALSA->pvBuf);
893 pStreamALSA->pvBuf = NULL;
894 }
895
896 return VINF_SUCCESS;
897}
898
899/**
900 * Destroys an ALSA output stream.
901 *
902 * @returns VBox status code.
903 * @param pStreamALSA ALSA output stream to destroy.
904 */
905static int alsaDestroyStreamOut(PALSAAUDIOSTREAM pStreamALSA)
906{
907 alsaStreamClose(&pStreamALSA->phPCM);
908
909 if (pStreamALSA->pvBuf)
910 {
911 RTMemFree(pStreamALSA->pvBuf);
912 pStreamALSA->pvBuf = NULL;
913 }
914
915 return VINF_SUCCESS;
916}
917
918/**
919 * Creates an ALSA output stream.
920 *
921 * @returns VBox status code.
922 * @param pThis The ALSA driver instance data.
923 * @param pStreamALSA ALSA output stream to create.
924 * @param pCfgReq Requested configuration to create stream with.
925 * @param pCfgAcq Obtained configuration the stream got created
926 * with on success.
927 */
928static int alsaCreateStreamOut(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,
929 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
930{
931 snd_pcm_t *phPCM = NULL;
932
933 int rc;
934
935 do
936 {
937 ALSAAUDIOSTREAMCFG req;
938 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);
939 req.freq = pCfgReq->Props.uHz;
940 req.nchannels = pCfgReq->Props.cChannels;
941 req.period_size = pCfgReq->Backend.cFramesPeriod;
942 req.buffer_size = pCfgReq->Backend.cFramesBufferSize;
943 req.threshold = pCfgReq->Backend.cFramesPreBuffering;
944
945 ALSAAUDIOSTREAMCFG obt;
946 rc = alsaStreamOpen(pThis->szDefaultOut, false /* fIn */, &req, &obt, &phPCM);
947 if (RT_FAILURE(rc))
948 break;
949
950 pCfgAcq->Props.uHz = obt.freq;
951 pCfgAcq->Props.cChannels = obt.nchannels;
952
953 rc = alsaALSAToAudioProps(obt.fmt, &pCfgAcq->Props);
954 if (RT_FAILURE(rc))
955 break;
956
957 pCfgAcq->Backend.cFramesPeriod = obt.period_size;
958 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
959 pCfgAcq->Backend.cFramesPreBuffering = obt.threshold;
960
961 pStreamALSA->cbBuf = pCfgAcq->Backend.cFramesBufferSize * PDMAudioPropsBytesPerFrame(&pCfgAcq->Props);
962 pStreamALSA->pvBuf = RTMemAllocZ(pStreamALSA->cbBuf);
963 if (!pStreamALSA->pvBuf)
964 {
965 LogRel(("ALSA: Not enough memory for output DAC buffer (%zu frames)\n", pCfgAcq->Backend.cFramesBufferSize));
966 rc = VERR_NO_MEMORY;
967 break;
968 }
969
970 pStreamALSA->phPCM = phPCM;
971 }
972 while (0);
973
974 if (RT_FAILURE(rc))
975 alsaStreamClose(&phPCM);
976
977 LogFlowFuncLeaveRC(rc);
978 return rc;
979}
980
981/**
982 * Creates an ALSA input stream.
983 *
984 * @returns VBox status code.
985 * @param pThis The ALSA driver instance data.
986 * @param pStreamALSA ALSA input stream to create.
987 * @param pCfgReq Requested configuration to create stream with.
988 * @param pCfgAcq Obtained configuration the stream got created
989 * with on success.
990 */
991static int alsaCreateStreamIn(PDRVHOSTALSAAUDIO pThis, PALSAAUDIOSTREAM pStreamALSA,
992 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
993{
994 int rc;
995
996 snd_pcm_t *phPCM = NULL;
997
998 do
999 {
1000 ALSAAUDIOSTREAMCFG req;
1001 req.fmt = alsaAudioPropsToALSA(&pCfgReq->Props);
1002 req.freq = pCfgReq->Props.uHz;
1003 req.nchannels = pCfgReq->Props.cChannels;
1004 req.period_size = PDMAudioPropsMilliToFrames(&pCfgReq->Props, 50 /*ms*/); /** @todo Make this configurable. */
1005 req.buffer_size = req.period_size * 2; /** @todo Make this configurable. */
1006 req.threshold = req.period_size;
1007
1008 ALSAAUDIOSTREAMCFG obt;
1009 rc = alsaStreamOpen(pThis->szDefaultIn, true /* fIn */, &req, &obt, &phPCM);
1010 if (RT_FAILURE(rc))
1011 break;
1012
1013 pCfgAcq->Props.uHz = obt.freq;
1014 pCfgAcq->Props.cChannels = obt.nchannels;
1015
1016 rc = alsaALSAToAudioProps(obt.fmt, &pCfgAcq->Props);
1017 if (RT_FAILURE(rc))
1018 break;
1019
1020 pCfgAcq->Backend.cFramesPeriod = obt.period_size;
1021 pCfgAcq->Backend.cFramesBufferSize = obt.buffer_size;
1022 /* No pre-buffering. */
1023
1024 pStreamALSA->cbBuf = pCfgAcq->Backend.cFramesBufferSize * PDMAudioPropsBytesPerFrame(&pCfgAcq->Props);
1025 pStreamALSA->pvBuf = RTMemAlloc(pStreamALSA->cbBuf);
1026 if (!pStreamALSA->pvBuf)
1027 {
1028 LogRel(("ALSA: Not enough memory for input ADC buffer (%zu frames)\n", pCfgAcq->Backend.cFramesBufferSize));
1029 rc = VERR_NO_MEMORY;
1030 break;
1031 }
1032
1033 pStreamALSA->phPCM = phPCM;
1034 }
1035 while (0);
1036
1037 if (RT_FAILURE(rc))
1038 alsaStreamClose(&phPCM);
1039
1040 LogFlowFuncLeaveRC(rc);
1041 return rc;
1042}
1043
1044/**
1045 * Controls an ALSA input stream.
1046 *
1047 * @returns VBox status code.
1048 * @param pStreamALSA ALSA input stream to control.
1049 * @param enmStreamCmd Stream command to issue.
1050 */
1051static int alsaControlStreamIn(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
1052{
1053 int rc = VINF_SUCCESS;
1054
1055 int err;
1056
1057 switch (enmStreamCmd)
1058 {
1059 case PDMAUDIOSTREAMCMD_ENABLE:
1060 case PDMAUDIOSTREAMCMD_RESUME:
1061 {
1062 err = snd_pcm_prepare(pStreamALSA->phPCM);
1063 if (err < 0)
1064 {
1065 LogRel(("ALSA: Error preparing input stream: %s\n", snd_strerror(err)));
1066 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1067 }
1068 else
1069 {
1070 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
1071
1072 /* Only start the PCM stream for input streams. */
1073 err = snd_pcm_start(pStreamALSA->phPCM);
1074 if (err < 0)
1075 {
1076 LogRel(("ALSA: Error starting input stream: %s\n", snd_strerror(err)));
1077 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1078 }
1079 }
1080
1081 break;
1082 }
1083
1084 case PDMAUDIOSTREAMCMD_DISABLE:
1085 {
1086 err = snd_pcm_drop(pStreamALSA->phPCM);
1087 if (err < 0)
1088 {
1089 LogRel(("ALSA: Error disabling input stream: %s\n", snd_strerror(err)));
1090 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1091 }
1092 break;
1093 }
1094
1095 case PDMAUDIOSTREAMCMD_PAUSE:
1096 {
1097 err = snd_pcm_drop(pStreamALSA->phPCM);
1098 if (err < 0)
1099 {
1100 LogRel(("ALSA: Error pausing input stream: %s\n", snd_strerror(err)));
1101 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1102 }
1103 break;
1104 }
1105
1106 default:
1107 rc = VERR_NOT_SUPPORTED;
1108 break;
1109 }
1110
1111 LogFlowFuncLeaveRC(rc);
1112 return rc;
1113}
1114
1115/**
1116 * Controls an ALSA output stream.
1117 *
1118 * @returns VBox status code.
1119 * @param pStreamALSA ALSA output stream to control.
1120 * @param enmStreamCmd Stream command to issue.
1121 */
1122static int alsaControlStreamOut(PALSAAUDIOSTREAM pStreamALSA, PDMAUDIOSTREAMCMD enmStreamCmd)
1123{
1124 int rc = VINF_SUCCESS;
1125
1126 int err;
1127
1128 switch (enmStreamCmd)
1129 {
1130 case PDMAUDIOSTREAMCMD_ENABLE:
1131 case PDMAUDIOSTREAMCMD_RESUME:
1132 {
1133 err = snd_pcm_prepare(pStreamALSA->phPCM);
1134 if (err < 0)
1135 {
1136 LogRel(("ALSA: Error preparing output stream: %s\n", snd_strerror(err)));
1137 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1138 }
1139 else
1140 {
1141 Assert(snd_pcm_state(pStreamALSA->phPCM) == SND_PCM_STATE_PREPARED);
1142 }
1143
1144 break;
1145 }
1146
1147 case PDMAUDIOSTREAMCMD_DISABLE:
1148 {
1149 err = snd_pcm_drop(pStreamALSA->phPCM);
1150 if (err < 0)
1151 {
1152 LogRel(("ALSA: Error disabling output stream: %s\n", snd_strerror(err)));
1153 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1154 }
1155 break;
1156 }
1157
1158 case PDMAUDIOSTREAMCMD_PAUSE:
1159 {
1160 err = snd_pcm_drop(pStreamALSA->phPCM);
1161 if (err < 0)
1162 {
1163 LogRel(("ALSA: Error pausing output stream: %s\n", snd_strerror(err)));
1164 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1165 }
1166 break;
1167 }
1168
1169 case PDMAUDIOSTREAMCMD_DRAIN:
1170 {
1171 snd_pcm_state_t streamState = snd_pcm_state(pStreamALSA->phPCM);
1172 Log2Func(("Stream state is: %d\n", streamState));
1173
1174 if ( streamState == SND_PCM_STATE_PREPARED
1175 || streamState == SND_PCM_STATE_RUNNING)
1176 {
1177 err = snd_pcm_nonblock(pStreamALSA->phPCM, 0);
1178 if (err < 0)
1179 {
1180 LogRel(("ALSA: Error disabling output non-blocking mode: %s\n", snd_strerror(err)));
1181 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1182 break;
1183 }
1184
1185 err = snd_pcm_drain(pStreamALSA->phPCM);
1186 if (err < 0)
1187 {
1188 LogRel(("ALSA: Error draining output: %s\n", snd_strerror(err)));
1189 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1190 break;
1191 }
1192
1193 err = snd_pcm_nonblock(pStreamALSA->phPCM, 1);
1194 if (err < 0)
1195 {
1196 LogRel(("ALSA: Error re-enabling output non-blocking mode: %s\n", snd_strerror(err)));
1197 rc = VERR_ACCESS_DENIED; /** @todo Find a better rc. */
1198 }
1199 }
1200 break;
1201 }
1202
1203 default:
1204 rc = VERR_NOT_SUPPORTED;
1205 break;
1206 }
1207
1208 LogFlowFuncLeaveRC(rc);
1209 return rc;
1210}
1211
1212
1213/**
1214 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetConfig}
1215 */
1216static DECLCALLBACK(int) drvHostAlsaAudioHA_GetConfig(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDCFG pBackendCfg)
1217{
1218 RT_NOREF(pInterface);
1219 AssertPtrReturn(pBackendCfg, VERR_INVALID_POINTER);
1220
1221 RTStrPrintf2(pBackendCfg->szName, sizeof(pBackendCfg->szName), "ALSA");
1222
1223 pBackendCfg->cbStreamIn = sizeof(ALSAAUDIOSTREAM);
1224 pBackendCfg->cbStreamOut = sizeof(ALSAAUDIOSTREAM);
1225
1226 /* Enumerate sound devices. */
1227 char **pszHints;
1228 int err = snd_device_name_hint(-1 /* All cards */, "pcm", (void***)&pszHints);
1229 if (err == 0)
1230 {
1231 char** pszHintCur = pszHints;
1232 while (*pszHintCur != NULL)
1233 {
1234 char *pszDev = snd_device_name_get_hint(*pszHintCur, "NAME");
1235 bool fSkip = !pszDev
1236 || !RTStrICmp("null", pszDev);
1237 if (fSkip)
1238 {
1239 if (pszDev)
1240 free(pszDev);
1241 pszHintCur++;
1242 continue;
1243 }
1244
1245 char *pszIOID = snd_device_name_get_hint(*pszHintCur, "IOID");
1246 if (pszIOID)
1247 {
1248#if 0
1249 if (!RTStrICmp("input", pszIOID))
1250
1251 else if (!RTStrICmp("output", pszIOID))
1252#endif
1253 }
1254 else /* NULL means bidirectional, input + output. */
1255 {
1256 }
1257
1258 LogRel2(("ALSA: Found %s device: %s\n", pszIOID ? RTStrToLower(pszIOID) : "bidirectional", pszDev));
1259
1260 /* Special case for ALSAAudio. */
1261 if ( pszDev
1262 && RTStrIStr("pulse", pszDev) != NULL)
1263 LogRel2(("ALSA: ALSAAudio plugin in use\n"));
1264
1265 if (pszIOID)
1266 free(pszIOID);
1267
1268 if (pszDev)
1269 free(pszDev);
1270
1271 pszHintCur++;
1272 }
1273
1274 snd_device_name_free_hint((void **)pszHints);
1275 pszHints = NULL;
1276 }
1277 else
1278 LogRel2(("ALSA: Error enumerating PCM devices: %Rrc (%d)\n", RTErrConvertFromErrno(err), err));
1279
1280 /* ALSA allows exactly one input and one output used at a time for the selected device(s). */
1281 pBackendCfg->cMaxStreamsIn = 1;
1282 pBackendCfg->cMaxStreamsOut = 1;
1283
1284 return VINF_SUCCESS;
1285}
1286
1287
1288/**
1289 * @interface_method_impl{PDMIHOSTAUDIO,pfnShutdown}
1290 */
1291static DECLCALLBACK(void) drvHostAlsaAudioHA_Shutdown(PPDMIHOSTAUDIO pInterface)
1292{
1293 RT_NOREF(pInterface);
1294}
1295
1296
1297/**
1298 * @interface_method_impl{PDMIHOSTAUDIO,pfnGetStatus}
1299 */
1300static DECLCALLBACK(PDMAUDIOBACKENDSTS) drvHostAlsaAudioHA_GetStatus(PPDMIHOSTAUDIO pInterface, PDMAUDIODIR enmDir)
1301{
1302 RT_NOREF(enmDir);
1303 AssertPtrReturn(pInterface, PDMAUDIOBACKENDSTS_UNKNOWN);
1304
1305 return PDMAUDIOBACKENDSTS_RUNNING;
1306}
1307
1308
1309/**
1310 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamCreate}
1311 */
1312static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamCreate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream,
1313 PPDMAUDIOSTREAMCFG pCfgReq, PPDMAUDIOSTREAMCFG pCfgAcq)
1314{
1315 PDRVHOSTALSAAUDIO pThis = RT_FROM_MEMBER(pInterface, DRVHOSTALSAAUDIO, IHostAudio);
1316 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1317 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1318 AssertPtrReturn(pCfgReq, VERR_INVALID_POINTER);
1319 AssertPtrReturn(pCfgAcq, VERR_INVALID_POINTER);
1320
1321 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1322
1323 int rc;
1324 if (pCfgReq->enmDir == PDMAUDIODIR_IN)
1325 rc = alsaCreateStreamIn( pThis, pStreamALSA, pCfgReq, pCfgAcq);
1326 else
1327 rc = alsaCreateStreamOut(pThis, pStreamALSA, pCfgReq, pCfgAcq);
1328
1329 if (RT_SUCCESS(rc))
1330 {
1331 pStreamALSA->pCfg = PDMAudioStrmCfgDup(pCfgAcq);
1332 if (!pStreamALSA->pCfg)
1333 rc = VERR_NO_MEMORY;
1334 }
1335
1336 return rc;
1337}
1338
1339
1340/**
1341 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamDestroy}
1342 */
1343static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamDestroy(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1344{
1345 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1346 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1347
1348 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1349
1350 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
1351 return VINF_SUCCESS;
1352
1353 int rc;
1354 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
1355 rc = alsaDestroyStreamIn(pStreamALSA);
1356 else
1357 rc = alsaDestroyStreamOut(pStreamALSA);
1358
1359 if (RT_SUCCESS(rc))
1360 {
1361 PDMAudioStrmCfgFree(pStreamALSA->pCfg);
1362 pStreamALSA->pCfg = NULL;
1363 }
1364
1365 return rc;
1366}
1367
1368
1369/**
1370 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamControl}
1371 */
1372static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamControl(PPDMIHOSTAUDIO pInterface,
1373 PPDMAUDIOBACKENDSTREAM pStream, PDMAUDIOSTREAMCMD enmStreamCmd)
1374{
1375 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1376 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1377
1378 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1379
1380 if (!pStreamALSA->pCfg) /* Not (yet) configured? Skip. */
1381 return VINF_SUCCESS;
1382
1383 int rc;
1384 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_IN)
1385 rc = alsaControlStreamIn (pStreamALSA, enmStreamCmd);
1386 else
1387 rc = alsaControlStreamOut(pStreamALSA, enmStreamCmd);
1388
1389 return rc;
1390}
1391
1392
1393/**
1394 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetReadable}
1395 */
1396static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetReadable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1397{
1398 RT_NOREF(pInterface);
1399
1400 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1401
1402 uint32_t cbAvail = 0;
1403
1404 snd_pcm_sframes_t cFramesAvail;
1405 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
1406 if (RT_SUCCESS(rc))
1407 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
1408
1409 return cbAvail;
1410}
1411
1412
1413/**
1414 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetWritable}
1415 */
1416static DECLCALLBACK(uint32_t) drvHostAlsaAudioHA_StreamGetWritable(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1417{
1418 RT_NOREF(pInterface);
1419
1420 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1421
1422 uint32_t cbAvail = 0;
1423
1424 snd_pcm_sframes_t cFramesAvail;
1425 int rc = alsaStreamGetAvail(pStreamALSA->phPCM, &cFramesAvail);
1426 if (RT_SUCCESS(rc))
1427 cbAvail = PDMAUDIOSTREAMCFG_F2B(pStreamALSA->pCfg, cFramesAvail);
1428
1429 return cbAvail;
1430}
1431
1432
1433/**
1434 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetPending}
1435 */
1436static DECLCALLBACK(uint32_t) drvHostALSAStreamGetPending(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1437{
1438 RT_NOREF(pInterface);
1439 AssertPtrReturn(pStream, 0);
1440
1441 PALSAAUDIOSTREAM pStreamALSA = (PALSAAUDIOSTREAM)pStream;
1442
1443 snd_pcm_sframes_t cFramesDelay = 0;
1444 snd_pcm_state_t enmState = snd_pcm_state(pStreamALSA->phPCM);
1445
1446 int rc = VINF_SUCCESS;
1447
1448 AssertPtr(pStreamALSA->pCfg);
1449 if (pStreamALSA->pCfg->enmDir == PDMAUDIODIR_OUT)
1450 {
1451 /* Getting the delay (in audio frames) reports the time it will take
1452 * to hear a new sample after all queued samples have been played out. */
1453 int rc2 = snd_pcm_delay(pStreamALSA->phPCM, &cFramesDelay);
1454 if (RT_SUCCESS(rc))
1455 rc = rc2;
1456
1457 /* Make sure to check the stream's status.
1458 * If it's anything but SND_PCM_STATE_RUNNING, the delay is meaningless and therefore 0. */
1459 if (enmState != SND_PCM_STATE_RUNNING)
1460 cFramesDelay = 0;
1461 }
1462
1463 /* Note: For input streams we never have pending data left. */
1464
1465 Log2Func(("cFramesDelay=%RI32, enmState=%d, rc=%d\n", cFramesDelay, enmState, rc));
1466
1467 return PDMAudioPropsFramesToBytes(&pStreamALSA->pCfg->Props, cFramesDelay);
1468}
1469
1470
1471/**
1472 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamGetStatus}
1473 */
1474static DECLCALLBACK(PDMAUDIOSTREAMSTS) drvHostAlsaAudioHA_StreamGetStatus(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1475{
1476 RT_NOREF(pInterface, pStream);
1477
1478 return PDMAUDIOSTREAMSTS_FLAGS_INITIALIZED | PDMAUDIOSTREAMSTS_FLAGS_ENABLED;
1479}
1480
1481
1482/**
1483 * @interface_method_impl{PDMIHOSTAUDIO,pfnStreamIterate}
1484 */
1485static DECLCALLBACK(int) drvHostAlsaAudioHA_StreamIterate(PPDMIHOSTAUDIO pInterface, PPDMAUDIOBACKENDSTREAM pStream)
1486{
1487 AssertPtrReturn(pInterface, VERR_INVALID_POINTER);
1488 AssertPtrReturn(pStream, VERR_INVALID_POINTER);
1489
1490 LogFlowFuncEnter();
1491
1492 /* Nothing to do here for ALSA. */
1493 return VINF_SUCCESS;
1494}
1495
1496
1497/**
1498 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1499 */
1500static DECLCALLBACK(void *) drvHostAlsaAudioQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1501{
1502 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
1503 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1504 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
1505 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHOSTAUDIO, &pThis->IHostAudio);
1506
1507 return NULL;
1508}
1509
1510
1511/**
1512 * Construct a DirectSound Audio driver instance.
1513 *
1514 * @copydoc FNPDMDRVCONSTRUCT
1515 */
1516static DECLCALLBACK(int) drvHostAlsaAudioConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
1517{
1518 RT_NOREF(fFlags);
1519 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
1520 PDRVHOSTALSAAUDIO pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTALSAAUDIO);
1521 LogRel(("Audio: Initializing ALSA driver\n"));
1522
1523 /*
1524 * Init the static parts.
1525 */
1526 pThis->pDrvIns = pDrvIns;
1527 /* IBase */
1528 pDrvIns->IBase.pfnQueryInterface = drvHostAlsaAudioQueryInterface;
1529 /* IHostAudio */
1530 pThis->IHostAudio.pfnInit = drvHostAlsaAudioHA_Init;
1531 pThis->IHostAudio.pfnShutdown = drvHostAlsaAudioHA_Shutdown;
1532 pThis->IHostAudio.pfnGetConfig = drvHostAlsaAudioHA_GetConfig;
1533 pThis->IHostAudio.pfnGetStatus = drvHostAlsaAudioHA_GetStatus;
1534 pThis->IHostAudio.pfnStreamCreate = drvHostAlsaAudioHA_StreamCreate;
1535 pThis->IHostAudio.pfnStreamDestroy = drvHostAlsaAudioHA_StreamDestroy;
1536 pThis->IHostAudio.pfnStreamControl = drvHostAlsaAudioHA_StreamControl;
1537 pThis->IHostAudio.pfnStreamGetReadable = drvHostAlsaAudioHA_StreamGetReadable;
1538 pThis->IHostAudio.pfnStreamGetWritable = drvHostAlsaAudioHA_StreamGetWritable;
1539 pThis->IHostAudio.pfnStreamGetStatus = drvHostAlsaAudioHA_StreamGetStatus;
1540 pThis->IHostAudio.pfnStreamIterate = drvHostAlsaAudioHA_StreamIterate;
1541 pThis->IHostAudio.pfnStreamPlay = drvHostAlsaAudioHA_StreamPlay;
1542 pThis->IHostAudio.pfnStreamCapture = drvHostAlsaAudioHA_StreamCapture;
1543 pThis->IHostAudio.pfnSetCallback = NULL;
1544 pThis->IHostAudio.pfnGetDevices = NULL;
1545 pThis->IHostAudio.pfnStreamGetPending = drvHostALSAStreamGetPending;
1546 pThis->IHostAudio.pfnStreamPlayBegin = NULL;
1547 pThis->IHostAudio.pfnStreamPlayEnd = NULL;
1548 pThis->IHostAudio.pfnStreamCaptureBegin = NULL;
1549 pThis->IHostAudio.pfnStreamCaptureEnd = NULL;
1550
1551 /*
1552 * Read configuration.
1553 */
1554 PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "StreamName|DefaultOutput|DefaultInput", "");
1555
1556 int rc = CFGMR3QueryStringDef(pCfg, "DefaultInput", pThis->szDefaultIn, sizeof(pThis->szDefaultIn), "default");
1557 AssertRCReturn(rc, rc);
1558 rc = CFGMR3QueryStringDef(pCfg, "DefaultOutput", pThis->szDefaultOut, sizeof(pThis->szDefaultOut), "default");
1559 AssertRCReturn(rc, rc);
1560
1561 return VINF_SUCCESS;
1562}
1563
1564
1565/**
1566 * Char driver registration record.
1567 */
1568const PDMDRVREG g_DrvHostALSAAudio =
1569{
1570 /* u32Version */
1571 PDM_DRVREG_VERSION,
1572 /* szName */
1573 "ALSAAudio",
1574 /* szRCMod */
1575 "",
1576 /* szR0Mod */
1577 "",
1578 /* pszDescription */
1579 "ALSA host audio driver",
1580 /* fFlags */
1581 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
1582 /* fClass. */
1583 PDM_DRVREG_CLASS_AUDIO,
1584 /* cMaxInstances */
1585 ~0U,
1586 /* cbInstance */
1587 sizeof(DRVHOSTALSAAUDIO),
1588 /* pfnConstruct */
1589 drvHostAlsaAudioConstruct,
1590 /* pfnDestruct */
1591 NULL,
1592 /* pfnRelocate */
1593 NULL,
1594 /* pfnIOCtl */
1595 NULL,
1596 /* pfnPowerOn */
1597 NULL,
1598 /* pfnReset */
1599 NULL,
1600 /* pfnSuspend */
1601 NULL,
1602 /* pfnResume */
1603 NULL,
1604 /* pfnAttach */
1605 NULL,
1606 /* pfnDetach */
1607 NULL,
1608 /* pfnPowerOff */
1609 NULL,
1610 /* pfnSoftReset */
1611 NULL,
1612 /* u32EndVersion */
1613 PDM_DRVREG_VERSION
1614};
1615
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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