VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/pulseaudio.c@ 25678

Last change on this file since 25678 was 25678, checked in by vboxsync, 15 years ago

Devices/pulseaudio: Several fixes from Arthur Taylor (thanks!), in particular for pcm_in though pcm_in currently untested plus some cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.4 KB
Line 
1/** @file
2 *
3 * VBox PulseAudio backend
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DEV_AUDIO
27#include <VBox/log.h>
28#include <iprt/mem.h>
29
30#include <pulse/pulseaudio.h>
31#include "pulse_stubs.h"
32
33#include "../../vl_vbox.h"
34#include "audio.h"
35#define AUDIO_CAP "pulse"
36#include "audio_int.h"
37#include <stdio.h>
38
39#define MAX_LOG_REL_ERRORS 32
40
41/*
42 * We use a g_pMainLoop in a separate thread g_pContext. We have to call functions for
43 * manipulating objects either from callback functions or we have to protect
44 * these functions by pa_threaded_mainloop_lock() / pa_threaded_mainloop_unlock().
45 */
46static struct pa_threaded_mainloop *g_pMainLoop;
47static struct pa_context *g_pContext;
48
49typedef struct PulseVoice
50{
51 HWVoiceOut hw;
52 /** DAC buffer */
53 void *pPCMBuf;
54 /** Pulse stream */
55 pa_stream *pStream;
56 /** Pulse sample format and attribute specification */
57 pa_sample_spec SampleSpec;
58 /** Pulse playback and buffer metrics */
59 pa_buffer_attr BufAttr;
60 int fOpSuccess;
61 /** number of logged errors */
62 unsigned cErrors;
63 const uint8_t *pu8PeekBuf;
64 size_t cbPeekBuf;
65 size_t offPeekBuf;
66} PulseVoice;
67
68/* The desired max buffer size in milliseconds. The desired latency will be
69 * calculated to be 1/10 of the value */
70static struct
71{
72 int buffer_msecs_out;
73 int buffer_msecs_in;
74} conf
75=
76{
77 INIT_FIELD (.buffer_msecs_out = ) 100,
78 INIT_FIELD (.buffer_msecs_in = ) 300,
79};
80
81static pa_sample_format_t aud_to_pulsefmt (audfmt_e fmt)
82{
83 switch (fmt)
84 {
85 case AUD_FMT_U8:
86 return PA_SAMPLE_U8;
87
88 case AUD_FMT_S16:
89 return PA_SAMPLE_S16LE;
90
91#ifdef PA_SAMPLE_S32LE
92 case AUD_FMT_S32:
93 return PA_SAMPLE_S32LE;
94#endif
95
96 default:
97 dolog ("Bad audio format %d\n", fmt);
98 return PA_SAMPLE_U8;
99 }
100}
101
102
103static int pulse_to_audfmt (pa_sample_format_t pulsefmt, audfmt_e *fmt, int *endianess)
104{
105 switch (pulsefmt)
106 {
107 case PA_SAMPLE_U8:
108 *endianess = 0;
109 *fmt = AUD_FMT_U8;
110 break;
111
112 case PA_SAMPLE_S16LE:
113 *fmt = AUD_FMT_S16;
114 *endianess = 0;
115 break;
116
117 case PA_SAMPLE_S16BE:
118 *fmt = AUD_FMT_S16;
119 *endianess = 1;
120 break;
121
122#ifdef PA_SAMPLE_S32LE
123 case PA_SAMPLE_S32LE:
124 *fmt = AUD_FMT_S32;
125 *endianess = 0;
126 break;
127#endif
128
129#ifdef PA_SAMPLE_S32BE
130 case PA_SAMPLE_S32BE:
131 *fmt = AUD_FMT_S32;
132 *endianess = 1;
133 break;
134#endif
135
136 default:
137 return -1;
138 }
139 return 0;
140}
141
142static void context_state_callback(pa_context *c, void *userdata)
143{
144 PulseVoice *pPulse = (PulseVoice *)userdata;
145 switch (pa_context_get_state(c))
146 {
147 case PA_CONTEXT_READY:
148 case PA_CONTEXT_TERMINATED:
149 pa_threaded_mainloop_signal(g_pMainLoop, 0);
150 break;
151
152 case PA_CONTEXT_FAILED:
153 LogRel(("Pulse: Audio input/output stopped!\n"));
154 pPulse->cErrors = MAX_LOG_REL_ERRORS;
155 pa_threaded_mainloop_signal(g_pMainLoop, 0);
156 break;
157
158 default:
159 break;
160 }
161}
162
163static void stream_state_callback(pa_stream *s, void *userdata)
164{
165 switch (pa_stream_get_state(s))
166 {
167 case PA_STREAM_READY:
168 case PA_STREAM_FAILED:
169 case PA_STREAM_TERMINATED:
170 pa_threaded_mainloop_signal(g_pMainLoop, 0);
171 break;
172
173 default:
174 break;
175 }
176}
177
178static int pulse_open (int fIn, pa_stream **ppStream, pa_sample_spec *pSampleSpec,
179 pa_buffer_attr *pBufAttr)
180{
181 const pa_buffer_attr *pBufAttrObtained;
182 pa_stream *pStream = NULL;
183 char achPCMName[64];
184 pa_stream_flags_t flags = 0;
185 const char *stream_name = audio_get_stream_name();
186
187 RTStrPrintf(achPCMName, sizeof(achPCMName), "%.32s%s%s%s",
188 stream_name ? stream_name : "",
189 stream_name ? " (" : "",
190 fIn ? "pcm_in" : "pcm_out",
191 stream_name ? ")" : "");
192
193 LogRel(("Pulse: open %s rate=%dHz channels=%d format=%s\n",
194 fIn ? "PCM_IN" : "PCM_OUT", pSampleSpec->rate, pSampleSpec->channels,
195 pa_sample_format_to_string(pSampleSpec->format)));
196
197 if (!pa_sample_spec_valid(pSampleSpec))
198 {
199 LogRel(("Pulse: Unsupported sample specification\n"));
200 goto fail;
201 }
202
203 pa_threaded_mainloop_lock(g_pMainLoop);
204
205 if (!(pStream = pa_stream_new(g_pContext, achPCMName, pSampleSpec, /*channel_map=*/NULL)))
206 {
207 LogRel(("Pulse: Cannot create stream %s\n", achPCMName));
208 goto unlock_and_fail;
209 }
210
211 pa_stream_set_state_callback(pStream, stream_state_callback, NULL);
212
213#if PA_API_VERSION >= 12
214 /* XXX */
215 flags |= PA_STREAM_ADJUST_LATENCY;
216#endif
217
218 if (fIn)
219 {
220 LogRel(("Pulse: Requested record buffer attributes: maxlength=%d fragsize=%d\n",
221 pBufAttr->maxlength, pBufAttr->fragsize));
222#if 0
223 /* not applicable as we don't use pa_stream_get_latency() and pa_stream_get_time() */
224 flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
225#endif
226 if (pa_stream_connect_record(pStream, /*dev=*/NULL, pBufAttr, flags) < 0)
227 {
228 LogRel(("Pulse: Cannot connect record stream: %s\n",
229 pa_strerror(pa_context_errno(g_pContext))));
230 goto disconnect_unlock_and_fail;
231 }
232 }
233 else
234 {
235 LogRel(("Pulse: Requested playback buffer attributes: maxlength=%d tlength=%d prebuf=%d minreq=%d\n",
236 pBufAttr->maxlength, pBufAttr->tlength, pBufAttr->prebuf, pBufAttr->minreq));
237
238 flags |= PA_STREAM_START_CORKED;
239
240 if (pa_stream_connect_playback(pStream, /*dev=*/NULL, pBufAttr, flags,
241 /*cvolume=*/NULL, /*sync_stream=*/NULL) < 0)
242 {
243 LogRel(("Pulse: Cannot connect playback stream: %s\n",
244 pa_strerror(pa_context_errno(g_pContext))));
245 goto disconnect_unlock_and_fail;
246 }
247 }
248
249 /* Wait until the stream is ready */
250 for (;;)
251 {
252 pa_stream_state_t sstate;
253 pa_threaded_mainloop_wait(g_pMainLoop);
254
255 sstate = pa_stream_get_state(pStream);
256 if (sstate == PA_STREAM_READY)
257 break;
258 else if (sstate == PA_STREAM_FAILED || sstate == PA_STREAM_TERMINATED)
259 {
260 LogRel(("Pulse: Failed to initialize stream (state %d)\n", sstate));
261 goto disconnect_unlock_and_fail;
262 }
263 }
264
265 pBufAttrObtained = pa_stream_get_buffer_attr(pStream);
266 memcpy(pBufAttr, pBufAttrObtained, sizeof(pa_buffer_attr));
267
268 LogRel(("Pulse: Obtained buffer attributes: tlength=%d maxlength=%d prebuf=%d minreq=%d fragsize=%d\n",
269 pBufAttr->tlength, pBufAttr->maxlength, pBufAttr->prebuf, pBufAttr->minreq, pBufAttr->fragsize));
270
271 pa_threaded_mainloop_unlock(g_pMainLoop);
272 *ppStream = pStream;
273 return 0;
274
275disconnect_unlock_and_fail:
276 pa_stream_disconnect(pStream);
277
278unlock_and_fail:
279 pa_threaded_mainloop_unlock(g_pMainLoop);
280
281fail:
282 if (pStream)
283 pa_stream_unref(pStream);
284
285 *ppStream = NULL;
286 return -1;
287}
288
289static int pulse_init_out (HWVoiceOut *hw, audsettings_t *as)
290{
291 PulseVoice *pPulse = (PulseVoice *) hw;
292 audsettings_t obt_as;
293 int cbBuf;
294
295 pPulse->SampleSpec.format = aud_to_pulsefmt (as->fmt);
296 pPulse->SampleSpec.rate = as->freq;
297 pPulse->SampleSpec.channels = as->nchannels;
298
299 /* Note that setting maxlength to -1 does not work for older PulseAudio libraries
300 * (at least not for 0.9.10). So use the suggested value of 3/2 of tlength */
301 pPulse->BufAttr.tlength = (pa_bytes_per_second(&pPulse->SampleSpec)
302 * conf.buffer_msecs_out) / 1000;
303 pPulse->BufAttr.maxlength = (pPulse->BufAttr.tlength * 3) / 2;
304 pPulse->BufAttr.prebuf = -1; /* Same as tlength */
305 pPulse->BufAttr.minreq = -1; /* Pulse should set something sensible for minreq on it's own */
306
307 /* Notice that the struct BufAttr is updated to the obtained values after this call */
308 if (pulse_open (0, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
309 return -1;
310
311 if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
312 {
313 LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
314 return -1;
315 }
316
317 obt_as.freq = pPulse->SampleSpec.rate;
318 obt_as.nchannels = pPulse->SampleSpec.channels;
319
320 audio_pcm_init_info (&hw->info, &obt_as);
321 cbBuf = audio_MIN(pPulse->BufAttr.tlength * 2, pPulse->BufAttr.maxlength);
322
323 pPulse->pPCMBuf = RTMemAllocZ(cbBuf);
324 if (!pPulse->pPCMBuf)
325 {
326 LogRel(("Pulse: Could not allocate DAC buffer of %d bytes\n", cbBuf));
327 return -1;
328 }
329
330 /* Convert from bytes to frames (aka samples) */
331 hw->samples = cbBuf >> hw->info.shift;
332
333 return 0;
334}
335
336static void pulse_fini_out (HWVoiceOut *hw)
337{
338 PulseVoice *pPulse = (PulseVoice *)hw;
339
340 if (pPulse->pStream)
341 {
342 pa_threaded_mainloop_lock(g_pMainLoop);
343 pa_stream_disconnect(pPulse->pStream);
344 pa_stream_unref(pPulse->pStream);
345 pa_threaded_mainloop_unlock(g_pMainLoop);
346 pPulse->pStream = NULL;
347 }
348
349 if (pPulse->pPCMBuf)
350 {
351 RTMemFree (pPulse->pPCMBuf);
352 pPulse->pPCMBuf = NULL;
353 }
354}
355
356static int pulse_run_out (HWVoiceOut *hw)
357{
358 PulseVoice *pPulse = (PulseVoice *) hw;
359 int cFramesLive;
360 int cFramesWritten = 0;
361 int csSamples;
362 int cFramesToWrite;
363 int cFramesAvail;
364 size_t cbAvail;
365 size_t cbToWrite;
366 uint8_t *pu8Dst;
367 st_sample_t *psSrc;
368
369 cFramesLive = audio_pcm_hw_get_live_out (hw);
370 if (!cFramesLive)
371 return 0;
372
373 pa_threaded_mainloop_lock(g_pMainLoop);
374
375 cbAvail = pa_stream_writable_size (pPulse->pStream);
376 if (cbAvail == (size_t)-1)
377 {
378 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
379 {
380 int rc = pa_context_errno(g_pContext);
381 pPulse->cErrors++;
382 LogRel(("Pulse: Failed to determine the writable size: %s\n",
383 pa_strerror(rc)));
384 }
385 goto unlock_and_exit;
386 }
387
388 cFramesAvail = cbAvail >> hw->info.shift; /* bytes => samples */
389 cFramesWritten = audio_MIN (cFramesLive, cFramesAvail);
390 csSamples = cFramesWritten;
391
392 while (csSamples)
393 {
394 /* split request at the end of our samples buffer */
395 cFramesToWrite = audio_MIN (csSamples, hw->samples - hw->rpos);
396 cbToWrite = cFramesToWrite << hw->info.shift;
397 psSrc = hw->mix_buf + hw->rpos;
398 pu8Dst = advance (pPulse->pPCMBuf, hw->rpos << hw->info.shift);
399
400 hw->clip (pu8Dst, psSrc, cFramesToWrite);
401
402 if (pa_stream_write (pPulse->pStream, pu8Dst, cbToWrite,
403 /*cleanup_callback=*/NULL, 0, PA_SEEK_RELATIVE) < 0)
404 {
405 LogRel(("Pulse: Failed to write %d samples: %s\n",
406 cFramesToWrite, pa_strerror(pa_context_errno(g_pContext))));
407 break;
408 }
409 hw->rpos = (hw->rpos + cFramesToWrite) % hw->samples;
410 csSamples -= cFramesToWrite;
411 }
412
413unlock_and_exit:
414 pa_threaded_mainloop_unlock(g_pMainLoop);
415
416 return cFramesWritten;
417}
418
419static int pulse_write (SWVoiceOut *sw, void *buf, int len)
420{
421 return audio_pcm_sw_write (sw, buf, len);
422}
423
424static void stream_success_callback(pa_stream *pStream, int success, void *userdata)
425{
426 PulseVoice *pPulse = (PulseVoice *) userdata;
427 pPulse->fOpSuccess = success;
428 pa_threaded_mainloop_signal(g_pMainLoop, 0);
429}
430
431typedef enum
432{
433 Unpause,
434 Pause,
435 Trigger,
436 Drain
437} pulse_cmd_t;
438
439static int pulse_ctrl (PulseVoice *pPulse, pulse_cmd_t cmd)
440{
441 pa_operation *op = NULL;
442 const char *cmd_str = cmd == Unpause ? "unpause" :
443 cmd == Pause ? "pause" :
444 cmd == Trigger ? "trigger" :
445 cmd == Drain ? "drain" : NULL;
446
447 if (!pPulse->pStream)
448 return 0;
449
450 LogRel(("Pulse: ctrl cmd=%s\n", cmd_str));
451
452 pa_threaded_mainloop_lock(g_pMainLoop);
453 switch (cmd)
454 {
455 case Pause:
456 op = pa_stream_cork(pPulse->pStream, 1, stream_success_callback, pPulse);
457 break;
458 case Unpause:
459 op = pa_stream_cork(pPulse->pStream, 0, stream_success_callback, pPulse);
460 break;
461 case Drain:
462 op = pa_stream_drain(pPulse->pStream, stream_success_callback, pPulse);
463 break;
464 case Trigger:
465 op = pa_stream_trigger(pPulse->pStream, stream_success_callback, pPulse);
466 break;
467 default:
468 goto unlock_and_exit;
469 }
470 if (!op)
471 {
472 if (pPulse->cErrors < MAX_LOG_REL_ERRORS)
473 {
474 int rc = pa_context_errno(g_pContext);
475 pPulse->cErrors++;
476 LogRel(("Pulse: Failed ctrl cmd=%s to stream: %s\n", cmd_str, pa_strerror(rc)));
477 }
478 }
479 else
480 {
481 while (pa_operation_get_state(op) == PA_OPERATION_RUNNING)
482 pa_threaded_mainloop_wait(g_pMainLoop);
483 pa_operation_unref(op);
484 }
485
486unlock_and_exit:
487 pa_threaded_mainloop_unlock(g_pMainLoop);
488 return 0;
489}
490
491static int pulse_ctl_out (HWVoiceOut *hw, int cmd, ...)
492{
493 switch (cmd)
494 {
495 case VOICE_ENABLE:
496 pulse_ctrl((PulseVoice *)hw, Unpause);
497 break;
498
499 case VOICE_DISABLE:
500 pulse_ctrl((PulseVoice *)hw, Trigger);
501 pulse_ctrl((PulseVoice *)hw, Drain);
502 pulse_ctrl((PulseVoice *)hw, Pause);
503 break;
504
505 default:
506 return -1;
507 }
508 return 0;
509}
510
511static int pulse_init_in (HWVoiceIn *hw, audsettings_t *as)
512{
513 PulseVoice *pPulse = (PulseVoice *) hw;
514 audsettings_t obt_as;
515
516 pPulse->SampleSpec.format = aud_to_pulsefmt (as->fmt);
517 pPulse->SampleSpec.rate = as->freq;
518 pPulse->SampleSpec.channels = as->nchannels;
519
520 /* XXX check these values */
521 pPulse->BufAttr.fragsize = (pa_bytes_per_second(&pPulse->SampleSpec)
522 * conf.buffer_msecs_in) / 2000; /* one tenth */
523 pPulse->BufAttr.maxlength = (pa_bytes_per_second(&pPulse->SampleSpec)
524 * conf.buffer_msecs_in) / 1000;
525 /* Other memebers of pa_buffer_attr are ignored for record streams */
526
527 if (pulse_open (1, &pPulse->pStream, &pPulse->SampleSpec, &pPulse->BufAttr))
528 return -1;
529
530 if (pulse_to_audfmt (pPulse->SampleSpec.format, &obt_as.fmt, &obt_as.endianness))
531 {
532 LogRel(("Pulse: Cannot find audio format %d\n", pPulse->SampleSpec.format));
533 return -1;
534 }
535
536 obt_as.freq = pPulse->SampleSpec.rate;
537 obt_as.nchannels = pPulse->SampleSpec.channels;
538 audio_pcm_init_info (&hw->info, &obt_as);
539 hw->samples = audio_MIN(pPulse->BufAttr.fragsize * 10, pPulse->BufAttr.maxlength)
540 >> hw->info.shift;
541 pPulse->pu8PeekBuf = NULL;
542
543 return 0;
544}
545
546static void pulse_fini_in (HWVoiceIn *hw)
547{
548 PulseVoice *pPulse = (PulseVoice *)hw;
549
550 if (pPulse->pStream)
551 {
552 pa_threaded_mainloop_lock(g_pMainLoop);
553 pa_stream_disconnect(pPulse->pStream);
554 pa_stream_unref(pPulse->pStream);
555 pa_threaded_mainloop_unlock(g_pMainLoop);
556 pPulse->pStream = NULL;
557 }
558}
559
560static int pulse_run_in (HWVoiceIn *hw)
561{
562 PulseVoice *pPulse = (PulseVoice *) hw;
563 const int hwshift = hw->info.shift;
564 int cFramesRead = 0; /* total frames which have been read this call */
565 int cFramesAvail; /* total frames available from pulse at start of call */
566 int cFramesToRead; /* the largest amount we want/can get this call */
567 int cFramesToPeek; /* the largest amount we want/can get this peek */
568
569 /* We should only call pa_stream_readable_size() once and trust the first value */
570 pa_threaded_mainloop_lock(g_pMainLoop);
571 cFramesAvail = pa_stream_readable_size(pPulse->pStream) >> hwshift;
572 pa_threaded_mainloop_unlock(g_pMainLoop);
573
574 /* If the buffer was not dropped last call, add what remains */
575 if (pPulse->pu8PeekBuf)
576 cFramesAvail += (pPulse->cbPeekBuf - pPulse->offPeekBuf) >> hwshift;
577
578 cFramesToRead = audio_MIN(cFramesAvail, hw->samples - audio_pcm_hw_get_live_in(hw));
579 for (; cFramesToRead; cFramesToRead -= cFramesToPeek)
580 {
581 /* If there is no data, do another peek */
582 if (!pPulse->pu8PeekBuf)
583 {
584 pa_threaded_mainloop_lock(g_pMainLoop);
585 pa_stream_peek(pPulse->pStream, (const void**)&pPulse->pu8PeekBuf, &pPulse->cbPeekBuf);
586 pa_threaded_mainloop_unlock(g_pMainLoop);
587 pPulse->offPeekBuf = 0;
588 if (!pPulse->cbPeekBuf)
589 break;
590 }
591
592 cFramesToPeek = audio_MIN((signed)( pPulse->cbPeekBuf
593 - pPulse->offPeekBuf) >> hwshift,
594 cFramesToRead);
595
596 /* Check for wrapping around the buffer end */
597 if (hw->wpos + cFramesToPeek > hw->samples)
598 {
599 int cFramesDelta = hw->samples - hw->wpos;
600
601 hw->conv(hw->conv_buf + hw->wpos,
602 pPulse->pu8PeekBuf + pPulse->offPeekBuf,
603 cFramesDelta,
604 &nominal_volume);
605
606 hw->conv(hw->conv_buf,
607 pPulse->pu8PeekBuf + pPulse->offPeekBuf + (cFramesDelta << hwshift),
608 cFramesToPeek - cFramesDelta,
609 &nominal_volume);
610 }
611 else
612 {
613 hw->conv(hw->conv_buf + hw->wpos,
614 pPulse->pu8PeekBuf + pPulse->offPeekBuf,
615 cFramesToPeek,
616 &nominal_volume);
617 }
618
619 cFramesRead += cFramesToPeek;
620 hw->wpos = (hw->wpos + cFramesToPeek) % hw->samples;
621 pPulse->offPeekBuf += cFramesToPeek << hwshift;
622
623 /* If the buffer is done, drop it */
624 if (pPulse->offPeekBuf == pPulse->cbPeekBuf)
625 {
626 pa_threaded_mainloop_lock(g_pMainLoop);
627 pa_stream_drop(pPulse->pStream);
628 pa_threaded_mainloop_unlock(g_pMainLoop);
629 pPulse->pu8PeekBuf = NULL;
630 }
631 }
632
633 return cFramesRead;
634}
635
636static int pulse_read (SWVoiceIn *sw, void *buf, int size)
637{
638 return audio_pcm_sw_read (sw, buf, size);
639}
640
641static int pulse_ctl_in (HWVoiceIn *hw, int cmd, ...)
642{
643 PulseVoice *pPulse = (PulseVoice *)hw;
644
645 switch (cmd)
646 {
647 case VOICE_ENABLE:
648 pulse_ctrl((PulseVoice *)hw, Unpause);
649 break;
650 case VOICE_DISABLE:
651 if (pPulse->pu8PeekBuf)
652 {
653 pa_stream_drop(pPulse->pStream);
654 pPulse->pu8PeekBuf = NULL;
655 }
656 pulse_ctrl((PulseVoice *)hw, Pause);
657 break;
658 default:
659 return -1;
660 }
661 return 0;
662}
663
664static void *pulse_audio_init (void)
665{
666 int rc;
667
668 rc = audioLoadPulseLib();
669 if (RT_FAILURE(rc))
670 {
671 LogRel(("Pulse: Failed to load the PulseAudio shared library! Error %Rrc\n", rc));
672 return NULL;
673 }
674
675 if (!(g_pMainLoop = pa_threaded_mainloop_new()))
676 {
677 LogRel(("Pulse: Failed to allocate main loop: %s\n",
678 pa_strerror(pa_context_errno(g_pContext))));
679 goto fail;
680 }
681
682 if (!(g_pContext = pa_context_new(pa_threaded_mainloop_get_api(g_pMainLoop), "VBox")))
683 {
684 LogRel(("Pulse: Failed to allocate context: %s\n",
685 pa_strerror(pa_context_errno(g_pContext))));
686 goto fail;
687 }
688
689 if (pa_threaded_mainloop_start(g_pMainLoop) < 0)
690 {
691 LogRel(("Pulse: Failed to start threaded mainloop: %s\n",
692 pa_strerror(pa_context_errno(g_pContext))));
693 goto fail;
694 }
695
696 pa_context_set_state_callback(g_pContext, context_state_callback, NULL);
697 pa_threaded_mainloop_lock(g_pMainLoop);
698
699 if (pa_context_connect(g_pContext, /*server=*/NULL, 0, NULL) < 0)
700 {
701 LogRel(("Pulse: Failed to connect to server: %s\n",
702 pa_strerror(pa_context_errno(g_pContext))));
703 goto unlock_and_fail;
704 }
705
706 /* Wait until the g_pContext is ready */
707 for (;;)
708 {
709 pa_context_state_t cstate;
710 pa_threaded_mainloop_wait(g_pMainLoop);
711 cstate = pa_context_get_state(g_pContext);
712 if (cstate == PA_CONTEXT_READY)
713 break;
714 else if (cstate == PA_CONTEXT_TERMINATED || cstate == PA_CONTEXT_FAILED)
715 {
716 LogRel(("Pulse: Failed to initialize context (state %d)\n", cstate));
717 goto unlock_and_fail;
718 }
719 }
720 pa_threaded_mainloop_unlock(g_pMainLoop);
721
722 return &conf;
723
724unlock_and_fail:
725 if (g_pMainLoop)
726 pa_threaded_mainloop_unlock(g_pMainLoop);
727
728fail:
729 if (g_pMainLoop)
730 pa_threaded_mainloop_stop(g_pMainLoop);
731
732 if (g_pContext)
733 {
734 pa_context_disconnect(g_pContext);
735 pa_context_unref(g_pContext);
736 g_pContext = NULL;
737 }
738
739 if (g_pMainLoop)
740 {
741 pa_threaded_mainloop_free(g_pMainLoop);
742 g_pMainLoop = NULL;
743 }
744
745 return NULL;
746}
747
748static void pulse_audio_fini (void *opaque)
749{
750 if (g_pMainLoop)
751 pa_threaded_mainloop_stop(g_pMainLoop);
752
753 if (g_pContext)
754 {
755 pa_context_disconnect(g_pContext);
756 pa_context_unref(g_pContext);
757 g_pContext = NULL;
758 }
759
760 if (g_pMainLoop)
761 {
762 pa_threaded_mainloop_free(g_pMainLoop);
763 g_pMainLoop = NULL;
764 }
765
766 (void) opaque;
767}
768
769static struct audio_option pulse_options[] =
770{
771 {"DAC_MS", AUD_OPT_INT, &conf.buffer_msecs_out,
772 "DAC period size in milliseconds", NULL, 0},
773 {"ADC_MS", AUD_OPT_INT, &conf.buffer_msecs_in,
774 "ADC period size in milliseconds", NULL, 0},
775 {NULL, 0, NULL, NULL, NULL, 0}
776};
777
778static struct audio_pcm_ops pulse_pcm_ops =
779{
780 pulse_init_out,
781 pulse_fini_out,
782 pulse_run_out,
783 pulse_write,
784 pulse_ctl_out,
785
786 pulse_init_in,
787 pulse_fini_in,
788 pulse_run_in,
789 pulse_read,
790 pulse_ctl_in
791};
792
793struct audio_driver pulse_audio_driver =
794{
795 INIT_FIELD (name = ) "pulse",
796 INIT_FIELD (descr = ) "PulseAudio http://www.pulseaudio.org",
797 INIT_FIELD (options = ) pulse_options,
798 INIT_FIELD (init = ) pulse_audio_init,
799 INIT_FIELD (fini = ) pulse_audio_fini,
800 INIT_FIELD (pcm_ops = ) &pulse_pcm_ops,
801 INIT_FIELD (can_be_default = ) 1,
802 INIT_FIELD (max_voices_out = ) INT_MAX,
803 INIT_FIELD (max_voices_in = ) INT_MAX,
804 INIT_FIELD (voice_size_out = ) sizeof (PulseVoice),
805 INIT_FIELD (voice_size_in = ) sizeof (PulseVoice)
806};
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