VirtualBox

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

Last change on this file since 63689 was 63534, checked in by vboxsync, 8 years ago

Audio: Renaming.

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

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