VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/solaudio.c@ 7784

Last change on this file since 7784 was 7661, checked in by vboxsync, 17 years ago

Solaudio: fixed occasional ticking problem.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.9 KB
Line 
1/* $Id: solaudio.c 7661 2008-03-31 13:48:00Z vboxsync $ */
2/** @file
3 * VirtualBox Audio Driver - Solaris host.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
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/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <unistd.h>
22#include <errno.h>
23#include <stropts.h>
24#include <fcntl.h>
25#include <sys/audio.h>
26#include <sys/stat.h>
27#include <sys/time.h>
28
29#define LOG_GROUP LOG_GROUP_DEV_AUDIO
30#include <VBox/log.h>
31#include <iprt/env.h>
32
33#include "Builtins.h"
34#include "vl_vbox.h"
35#include "audio.h"
36#include <iprt/alloc.h>
37
38#define AUDIO_CAP "solaudio"
39#include "audio_int.h"
40
41/*******************************************************************************
42* Structures and Typedefs *
43*******************************************************************************/
44typedef struct solaudioVoiceOut {
45 HWVoiceOut Hw;
46 int AudioDev;
47 int AudioCtl;
48 int cBuffersPlayed;
49 void *pPCMBuf;
50} solaudioVoiceOut;
51
52
53/*******************************************************************************
54* Global Variables *
55*******************************************************************************/
56struct
57{
58 int cbBuffer;
59} conf =
60{
61 INIT_FIELD (cbBuffer =) 4352,
62};
63
64
65static void GCC_FMT_ATTR (2, 3) solaudio_logerr (int err, const char *fmt, ...)
66{
67 va_list ap;
68
69 va_start(ap, fmt);
70 AUD_vlog(AUDIO_CAP, fmt, ap);
71 va_end(ap);
72
73 AUD_log(AUDIO_CAP, "Reason: %s\n", strerror(err));
74}
75
76
77static int aud_to_solfmt (audfmt_e fmt)
78{
79 switch (fmt)
80 {
81 case AUD_FMT_S8:
82 case AUD_FMT_U8:
83 return AUDIO_PRECISION_8;
84
85 case AUD_FMT_S16:
86 case AUD_FMT_U16:
87 return AUDIO_PRECISION_16;
88
89 default:
90 solaudio_logerr (-1, "Bad audio format %d\n", fmt);
91 return AUDIO_PRECISION_8;
92 }
93}
94
95
96static int sol_to_audfmt (int fmt, int encoding)
97{
98 switch (fmt)
99 {
100 case AUDIO_PRECISION_8:
101 {
102 if (encoding == AUDIO_ENCODING_LINEAR8)
103 return AUD_FMT_U8;
104 else
105 return AUD_FMT_S8;
106 break;
107 }
108
109 case AUDIO_PRECISION_16:
110 {
111 if (encoding == AUDIO_ENCODING_LINEAR)
112 return AUD_FMT_S16;
113 else
114 return AUD_FMT_U16;
115 break;
116 }
117
118 default:
119 solaudio_logerr (-1, "Bad audio format %d\n", fmt);
120 return AUD_FMT_S8;
121 }
122}
123
124
125static char *solaudio_getdevice (void)
126{
127 /* This is for multiple audio devices where env. var determines current one,
128 * otherwise else we fallback to default.
129 */
130 const char *pszAudioDev = RTEnvGet("AUDIODEV");
131 if (pszAudioDev)
132 return RTStrDup(pszAudioDev);
133
134 return RTStrDup("/dev/audio");
135}
136
137
138static int solaudio_open (int in, audio_info_t *info, int *pfd, int *pctl_fd)
139{
140 int AudioDev;
141 int AudioCtl;
142 struct stat FileStat;
143 char *pszAudioDev = NULL;
144 char *pszAudioCtl = NULL;
145 audio_info_t AudioInfo;
146
147 pszAudioDev = solaudio_getdevice();
148 if (!pszAudioDev)
149 {
150 LogRel(("solaudio: solaudio_getdevice() failed to return a valid device.\n"));
151 return -1;
152 }
153
154 if (stat(pszAudioDev, &FileStat) < 0)
155 {
156 LogRel(("solaudio: failed to stat %s\n", pszAudioDev));
157 goto err2;
158 }
159
160 if (!S_ISCHR(FileStat.st_mode))
161 {
162 LogRel(("solaudio: invalid mode for %s\n", pszAudioDev));
163 goto err2;
164 }
165
166 AudioDev = open(pszAudioDev, O_WRONLY | O_NONBLOCK);
167 if (AudioDev < 0)
168 {
169 LogRel(("solaudio: failed to open %s\n", pszAudioDev));
170 goto err2;
171 }
172
173 RTStrAPrintf(&pszAudioCtl, "%sctl", pszAudioDev);
174 AudioCtl = open(pszAudioCtl, O_WRONLY | O_NONBLOCK);
175 if (AudioCtl < 0)
176 {
177 LogRel(("solaudio: failed to open %s\n", pszAudioCtl));
178 close(AudioDev);
179 goto err;
180 }
181
182 AUDIO_INITINFO(&AudioInfo);
183 if (ioctl(AudioDev, AUDIO_GETINFO, &AudioInfo) < 0)
184 {
185 LogRel(("solaudio: AUDIO_GETINFO failed\n"));
186 close(AudioDev);
187 close(AudioCtl);
188 goto err;
189 }
190 AudioInfo.play.sample_rate = info->play.sample_rate;
191 AudioInfo.play.channels = info->play.channels;
192 AudioInfo.play.precision = info->play.precision;
193 AudioInfo.play.encoding = info->play.encoding;
194 AudioInfo.play.buffer_size = info->play.buffer_size;
195 AudioInfo.play.gain = AUDIO_MAX_GAIN;
196 if (ioctl(AudioDev, AUDIO_SETINFO, &AudioInfo) < 0)
197 {
198 LogRel(("solaudio: AUDIO_SETINFO failed\n"));
199 close(AudioDev);
200 close(AudioCtl);
201 goto err;
202 }
203 LogFlow(("solaudio: buffer_size=%d\n", AudioInfo.play.buffer_size));
204 *pfd = AudioDev;
205 *pctl_fd = AudioCtl;
206 RTStrFree(pszAudioDev);
207 RTStrFree(pszAudioCtl);
208 return 0;
209
210err:
211 RTStrFree(pszAudioCtl);
212err2:
213 RTStrFree(pszAudioDev);
214 return -1;
215}
216
217
218static int solaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
219{
220 solaudioVoiceOut *pSol = (solaudioVoiceOut *)hw;
221 audio_info_t AudioInfo;
222 audsettings_t ObtAudioInfo;
223 int AudioDev = -1;
224 int AudioCtl = -1;
225
226 AUDIO_INITINFO(&AudioInfo);
227 AudioInfo.play.sample_rate = as->freq;
228 AudioInfo.play.channels = as->nchannels;
229 AudioInfo.play.precision = aud_to_solfmt(as->fmt);
230#if 0
231 /* Not really needed. */
232 int cbPerSample = (AudioInfo.play.channels * AudioInfo.play.precision) / 8;
233 int cbPerSecond = cbPerSample * AudioInfo.play.sample_rate;
234 AudioInfo.play.buffer_size = cbPerSecond > 131072 ? conf.cbBuffer : conf.cbBuffer / 2;
235#endif
236 AudioInfo.play.buffer_size = conf.cbBuffer;
237
238 if (as->fmt == AUD_FMT_U8)
239 AudioInfo.play.encoding = AUDIO_ENCODING_LINEAR8;
240 else
241 AudioInfo.play.encoding = AUDIO_ENCODING_LINEAR;
242
243 if (solaudio_open(0, &AudioInfo, &AudioDev, &AudioCtl))
244 {
245 LogRel(("solaudio: solaudio_open failed\n"));
246 return -1;
247 }
248
249 pSol->AudioDev = AudioDev;
250 pSol->AudioCtl = AudioCtl;
251 ObtAudioInfo.freq = AudioInfo.play.sample_rate;
252 ObtAudioInfo.nchannels = AudioInfo.play.channels;
253 ObtAudioInfo.fmt = sol_to_audfmt(AudioInfo.play.precision, AudioInfo.play.encoding);
254 ObtAudioInfo.endianness = as->endianness;
255
256 audio_pcm_init_info(&hw->info, &ObtAudioInfo);
257 pSol->cBuffersPlayed = AudioInfo.play.eof;
258
259 hw->samples = AudioInfo.play.buffer_size >> hw->info.shift;
260 pSol->pPCMBuf = RTMemAllocZ(AudioInfo.play.buffer_size);
261 if (!pSol->pPCMBuf)
262 {
263 LogRel(("solaudio: failed to alloc %d %d bytes to pPCMBuf\n", hw->samples << hw->info.shift, hw->samples));
264 return -1;
265 }
266 LogFlow(("solaudio: hw->samples=%d play.buffer_size=%d\n", hw->samples, AudioInfo.play.buffer_size));
267 return 0;
268}
269
270
271static void solaudio_stop (solaudioVoiceOut *sol)
272{
273 audio_info_t AudioInfo;
274 LogFlow(("solaudio: stop\n"));
275 if (sol->AudioDev < 0 || sol->AudioCtl < 0)
276 {
277 Log(("solaudio: invalid file descriptors\n"));
278 return;
279 }
280
281 if (ioctl(sol->AudioCtl, I_SETSIG, 0) < 0)
282 {
283 Log(("solaudio: failed to stop signalling\n"));
284 return;
285 }
286
287 if (ioctl(sol->AudioDev, I_FLUSH, FLUSHW) < 0)
288 {
289 LogRel(("solaudio: failed to drop unplayed buffers\n"));
290 return;
291 }
292
293 AUDIO_INITINFO(&AudioInfo);
294 AudioInfo.play.samples = 0;
295 AudioInfo.play.pause = 0;
296 AudioInfo.play.eof = 0;
297 AudioInfo.play.error = 0;
298 sol->cBuffersPlayed = 0;
299 if (ioctl(sol->AudioDev, AUDIO_SETINFO, &AudioInfo) < 0)
300 {
301 LogRel(("solaudio: AUDIO_SETINFO failed during stop.\n"));
302 return;
303 }
304}
305
306
307static void solaudio_fini_out (HWVoiceOut *hw)
308{
309 solaudioVoiceOut *sol = (solaudioVoiceOut *) hw;
310 solaudio_stop (sol);
311
312 close(sol->AudioDev);
313 sol->AudioDev = -1;
314 close(sol->AudioCtl);
315 sol->AudioCtl = -1;
316 if (sol->pPCMBuf)
317 {
318 RTMemFree(sol->pPCMBuf);
319 sol->pPCMBuf = NULL;
320 }
321
322 LogFlow(("solaudio: fini_out done\n"));
323}
324
325
326static int solaudio_availbuf (solaudioVoiceOut *sol)
327{
328 audio_info_t AudioInfo;
329 int cbBuffer = 0;
330
331 AUDIO_INITINFO(&AudioInfo);
332 if (ioctl(sol->AudioDev, AUDIO_GETINFO, &AudioInfo) < 0)
333 {
334 Log(("solaudio: AUDIO_GETINFO ioctl failed\n"));
335 return -1;
336 }
337
338 if (sol->cBuffersPlayed - AudioInfo.play.eof <= 2)
339 cbBuffer = AudioInfo.play.buffer_size;
340
341 LogFlow(("avail: eof=%d samples=%d bufsize=%d bufplayed=%d avail=%d\n", AudioInfo.play.eof, AudioInfo.play.samples,
342 AudioInfo.play.buffer_size, sol->cBuffersPlayed, cbBuffer));
343 return cbBuffer;
344}
345
346#if 0
347static void solaudio_yield (solaudioVoiceOut *pSol)
348{
349 audio_info_t AudioInfo;
350 timespec_t WaitTimeSpec;
351 if (ioctl(pSol->AudioDev, AUDIO_GETINFO, &AudioInfo) < 0)
352 return;
353
354 WaitTimeSpec.tv_sec = 0;
355 WaitTimeSpec.tv_nsec = 100000000;
356
357 while (AudioInfo.play.eof + 10 < pSol->cBuffersPlayed)
358 {
359 nanosleep(&WaitTimeSpec, NULL);
360 if (ioctl(pSol->AudioDev, AUDIO_GETINFO, &AudioInfo) < 0)
361 break;
362 }
363}
364#endif
365
366static int solaudio_run_out (HWVoiceOut *hw)
367{
368 solaudioVoiceOut *pSol = (solaudioVoiceOut *) hw;
369 int csLive, csDecr, csSamples, csToWrite, csAvail;
370 size_t cbAvail, cbToWrite, cbWritten;
371 uint8_t *pu8Dst;
372 st_sample_t *psSrc;
373
374 csLive = audio_pcm_hw_get_live_out(hw);
375 if (!csLive)
376 return 0;
377
378 cbAvail = solaudio_availbuf(pSol);
379 if (cbAvail <= 0)
380 return 0;
381
382 csAvail = cbAvail >> hw->info.shift; /* bytes => samples */
383 csDecr = audio_MIN(csLive, csAvail);
384 csSamples = csDecr;
385
386 while (csSamples)
387 {
388 /* split request at the end of our samples buffer */
389 csToWrite = audio_MIN(csSamples, hw->samples - hw->rpos);
390 cbToWrite = csToWrite << hw->info.shift;
391 psSrc = hw->mix_buf + hw->rpos;
392 pu8Dst = advance(pSol->pPCMBuf, hw->rpos << hw->info.shift);
393
394 hw->clip(pu8Dst, psSrc, csToWrite);
395
396 cbWritten = write(pSol->AudioDev, pu8Dst, cbToWrite);
397 if (cbWritten < 0)
398 break;
399
400 hw->rpos = (hw->rpos + csToWrite) % hw->samples;
401 csSamples -= csToWrite;
402 }
403
404 /* Increment eof marker for synchronous buffer processed */
405 write (pSol->AudioDev, NULL, 0);
406 pSol->cBuffersPlayed++;
407 return csDecr;
408}
409
410
411static int solaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
412{
413 solaudioVoiceOut *pSol = (solaudioVoiceOut *) hw;
414 switch (cmd)
415 {
416 case VOICE_ENABLE:
417 {
418 /* reset the eof marker and samples markers */
419 audio_info_t AudioInfo;
420 LogFlow(("solaudio: voice_enable\n"));
421 AUDIO_INITINFO(&AudioInfo);
422 ioctl(pSol->AudioDev, AUDIO_GETINFO, &AudioInfo);
423 AudioInfo.play.eof = 0;
424 AudioInfo.play.samples = 0;
425 ioctl(pSol->AudioDev, AUDIO_SETINFO, &AudioInfo);
426 pSol->cBuffersPlayed = 0;
427
428 audio_pcm_info_clear_buf(&hw->info, pSol->pPCMBuf, hw->samples);
429 break;
430 }
431
432 case VOICE_DISABLE:
433 {
434 LogFlow(("solaudio: voice_disable\n"));
435 solaudio_stop(pSol);
436 break;
437 }
438 }
439 return 0;
440}
441
442
443static int solaudio_write (SWVoiceOut *sw, void *buf, int len)
444{
445 return audio_pcm_sw_write (sw, buf, len);
446}
447
448
449static void *solaudio_audio_init (void)
450{
451 return &conf;
452}
453
454
455static void solaudio_audio_fini (void *opaque)
456{
457 NOREF(opaque);
458}
459
460
461static struct audio_pcm_ops solaudio_pcm_ops =
462{
463 solaudio_init_out,
464 solaudio_fini_out,
465 solaudio_run_out,
466 solaudio_write,
467 solaudio_ctl_out,
468
469 NULL,
470 NULL,
471 NULL,
472 NULL,
473 NULL
474};
475
476static struct audio_option solaudio_options[] =
477{
478 {"BUFFER_SIZE", AUD_OPT_INT, &conf.cbBuffer,
479 "Size of the buffer in bytes", NULL, 0},
480 {NULL, 0, NULL, NULL, NULL, 0}
481};
482
483struct audio_driver solaudio_audio_driver =
484{
485 INIT_FIELD (name = ) "solaudio",
486 INIT_FIELD (descr = ) "SolarisAudio http://sun.com",
487 INIT_FIELD (options = ) solaudio_options,
488 INIT_FIELD (init = ) solaudio_audio_init,
489 INIT_FIELD (fini = ) solaudio_audio_fini,
490 INIT_FIELD (pcm_ops = ) &solaudio_pcm_ops,
491 INIT_FIELD (can_be_default = ) 1,
492 INIT_FIELD (max_voices_out = ) 1,
493 INIT_FIELD (max_voices_in = ) 0,
494 INIT_FIELD (voice_size_out = ) sizeof (solaudioVoiceOut),
495 INIT_FIELD (voice_size_in = ) 0
496};
497
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