VirtualBox

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

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

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 10.6 KB
Line 
1/* $Id: solaudio.c 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * VirtualBox Audio Driver - Solaris host.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * innotek GmbH confidential
10 * All rights reserved
11 */
12
13/*******************************************************************************
14* Header Files *
15*******************************************************************************/
16#include <unistd.h>
17#include <errno.h>
18#include <stropts.h>
19#include <fcntl.h>
20#include <sys/audio.h>
21#include <sys/fcntl.h>
22#include <sys/stat.h>
23#include <sys/mman.h>
24
25#define LOG_GROUP LOG_GROUP_DEV_AUDIO
26#include <VBox/log.h>
27
28#include "Builtins.h"
29#include "vl_vbox.h"
30#include "audio.h"
31#include <iprt/alloc.h>
32
33#define AUDIO_CAP "solaudio"
34#include "audio_int.h"
35
36
37/*******************************************************************************
38* Structures and Typedefs *
39*******************************************************************************/
40typedef struct solaudioVoiceOut {
41 HWVoiceOut hw;
42 int fd;
43 int ctl_fd;
44 int buffers_played;
45 void *pcm_buf;
46} solaudioVoiceOut;
47
48
49/*******************************************************************************
50* Global Variables *
51*******************************************************************************/
52struct
53{
54 int buffer_size;
55 int nbuffers;
56} conf =
57{
58 INIT_FIELD (buffer_size =) 4096,
59 INIT_FIELD (nbuffers =) 4,
60};
61
62static void GCC_FMT_ATTR (2, 3) solaudio_logerr (int err, const char *fmt, ...)
63{
64 va_list ap;
65
66 va_start (ap, fmt);
67 AUD_vlog (AUDIO_CAP, fmt, ap);
68 va_end (ap);
69
70 AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
71}
72
73
74static int aud_to_solfmt (audfmt_e fmt)
75{
76 switch (fmt)
77 {
78 case AUD_FMT_S8:
79 case AUD_FMT_U8:
80 return AUDIO_PRECISION_8;
81
82 case AUD_FMT_S16:
83 case AUD_FMT_U16:
84 return AUDIO_PRECISION_16;
85
86 default:
87 solaudio_logerr (-1, "Bad audio format %d\n", fmt);
88 return AUDIO_PRECISION_8;
89 }
90}
91
92
93static int sol_to_audfmt (int fmt, int encoding)
94{
95 switch (fmt)
96 {
97 case AUDIO_PRECISION_8:
98 {
99 if (encoding == AUDIO_ENCODING_LINEAR8)
100 return AUD_FMT_U8;
101 else
102 return AUD_FMT_S8;
103 break;
104 }
105
106 case AUDIO_PRECISION_16:
107 return AUD_FMT_U16;
108
109 default:
110 solaudio_logerr (-1, "Bad audio format %d\n", fmt);
111 return AUD_FMT_S8;
112 }
113}
114
115static int solaudio_open (int in, audio_info_t *info, int *pfd, int *pctl_fd)
116{
117 int fd;
118 int ctl_fd;
119 struct stat st;
120 const char *deviceName = "/dev/audio";
121 const char *ctlDeviceName = "/dev/audioctl";
122 audio_info_t audInfo;
123
124 /* @todo add Log for failures. Use AUDIO_GETDEV instead of hardcoding /dev/audio */
125 if (stat(deviceName, &st) < 0)
126 return -1;
127
128 if (!S_ISCHR(st.st_mode))
129 return -1;
130
131 fd = open(deviceName, O_WRONLY | O_NONBLOCK);
132 if (fd < 0)
133 return -1;
134
135 ctl_fd = open(ctlDeviceName, O_WRONLY | O_NONBLOCK);
136 if (ctl_fd < 0)
137 {
138 close(fd);
139 return -1;
140 }
141
142 AUDIO_INITINFO(&audInfo);
143 if (ioctl(fd, AUDIO_GETINFO, &audInfo) < 0)
144 {
145 LogRel(("solaudio: AUDIO_GETINFO failed\n"));
146 close(fd);
147 return -1;
148 }
149 audInfo.play.sample_rate = info->play.sample_rate;
150 audInfo.play.channels = info->play.channels;
151 audInfo.play.precision = info->play.precision;
152 audInfo.play.encoding = info->play.encoding;
153 audInfo.play.buffer_size = info->play.buffer_size;
154 audInfo.play.gain = AUDIO_MID_GAIN;
155 if (ioctl(fd, AUDIO_SETINFO, &audInfo) < 0)
156 {
157 LogRel(("solaudio: AUDIO_SETINFO failed\n"));
158 close(fd);
159 return -1;
160 }
161 LogFlow(("solaudio: system buffer_size=%d\n", audInfo.play.buffer_size));
162 *pfd = fd;
163 *pctl_fd = ctl_fd;
164 return 0;
165}
166
167
168static int solaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
169{
170 solaudioVoiceOut *sol = (solaudioVoiceOut *) hw;
171 audio_info_t audioInfo;
172 audsettings_t obt_as;
173 int fd = -1;
174 int ctl_fd = -1;
175
176 AUDIO_INITINFO(&audioInfo);
177 audioInfo.play.sample_rate = as->freq;
178 audioInfo.play.channels = as->nchannels;
179 audioInfo.play.precision = aud_to_solfmt(as->fmt);
180 audioInfo.play.buffer_size = conf.buffer_size;
181 if (as->fmt == AUD_FMT_U8)
182 audioInfo.play.encoding = AUDIO_ENCODING_LINEAR8;
183 else
184 audioInfo.play.encoding = AUDIO_ENCODING_LINEAR;
185
186 if (solaudio_open(0, &audioInfo, &fd, &ctl_fd))
187 return -1;
188
189 sol->fd = fd;
190 sol->ctl_fd = ctl_fd;
191 obt_as.freq = audioInfo.play.sample_rate;
192 obt_as.nchannels = audioInfo.play.channels;
193 obt_as.fmt = sol_to_audfmt(audioInfo.play.precision, audioInfo.play.encoding);
194 obt_as.endianness = as->endianness;
195
196 audio_pcm_init_info (&hw->info, &obt_as);
197 sol->buffers_played = audioInfo.play.eof;
198
199 hw->samples = audioInfo.play.buffer_size / 2;
200 sol->pcm_buf = RTMemAllocZ(hw->samples << hw->info.shift);
201 if (!sol->pcm_buf)
202 {
203 LogRel(("solaudio: failed to alloc %d %d bytes to pcm_buf\n", hw->samples << hw->info.shift, hw->samples));
204 return -1;
205 }
206 LogFlow(("solaudio: hw->samples=%d play.buffer_size=%d\n", hw->samples, audioInfo.play.buffer_size));
207 return 0;
208}
209
210static void solaudio_stop (solaudioVoiceOut *solvw)
211{
212 LogFlow(("solaudio: stop\n"));
213 if (solvw->fd < 0 || solvw->ctl_fd < 0)
214 return;
215
216 if (ioctl(solvw->ctl_fd, I_SETSIG, 0) < 0)
217 return;
218
219 if (ioctl(solvw->fd, I_FLUSH, FLUSHW) < 0)
220 return;
221
222 close(solvw->fd);
223 solvw->fd = -1;
224 close(solvw->ctl_fd);
225 solvw->ctl_fd = -1;
226 solvw->buffers_played = 0;
227 if (solvw->pcm_buf)
228 {
229 RTMemFree(solvw->pcm_buf);
230 solvw->pcm_buf = NULL;
231 }
232}
233
234static void solaudio_fini_out (HWVoiceOut *hw)
235{
236 solaudioVoiceOut *sol = (solaudioVoiceOut *) hw;
237 solaudio_stop (sol);
238 LogFlow(("solaudio: fini_out done.\n"));
239}
240
241
242static int solaudio_availbuf (solaudioVoiceOut *solvw)
243{
244 audio_info_t audioInfo;
245 int buffers;
246
247 AUDIO_INITINFO(&audioInfo);
248 if (ioctl(solvw->fd, AUDIO_GETINFO, &audioInfo) < 0)
249 return -1;
250
251 buffers = audioInfo.play.buffer_size * (2 + audioInfo.play.eof - solvw->buffers_played);
252
253 LogFlow(("avail: eof=%d samples=%d bufsize=%d bufplayed=%d avail=%d\n", audioInfo.play.eof, audioInfo.play.samples,
254 audioInfo.play.buffer_size, solvw->buffers_played, buffers));
255 return buffers;
256}
257
258static int solaudio_run_out (HWVoiceOut *hw)
259{
260 solaudioVoiceOut *sol = (solaudioVoiceOut *) hw;
261 int live, samples, rpos, decr, avail;
262 uint16_t *dst = NULL;
263 st_sample_t *src;
264
265 live = audio_pcm_hw_get_live_out (hw);
266 if (!live)
267 return 0;
268
269 avail = solaudio_availbuf(sol);
270 if (avail <= 0)
271 return 0;
272
273 decr = audio_MIN(live, avail);
274 samples = decr;
275 rpos = hw->rpos;
276 LogFlow(("solaudio: run_out: samples=%d live=%d avail=%d\n", samples, live, avail));
277 while (samples)
278 {
279 int left_till_end_samples = hw->samples - rpos;
280 int convert_samples = audio_MIN (samples, left_till_end_samples);
281 int written = 0;
282
283 src = hw->mix_buf + rpos;
284 dst = advance (sol->pcm_buf, rpos << hw->info.shift);
285 hw->clip (dst, src, convert_samples << hw->info.shift);
286
287 written = write(sol->fd, (char *)dst, convert_samples << hw->info.shift);
288 if (written == -1)
289 {
290 LogRel(("solaudio: Write error!\n"));
291 decr = 0;
292 break;
293 }
294
295 if (written != convert_samples << hw->info.shift)
296 {
297 int wsamples = written >> hw->info.shift;
298 int wbytes = wsamples << hw->info.shift;
299 if (wbytes != written)
300 LogRel(("solaudio: Misaligned write %d (requested %d) alignment %d\n", wbytes, written, hw->info.align + 1));
301
302 decr -= wsamples;
303 rpos = (rpos + wsamples) % hw->samples;
304 LogFlow(("solaudio: Partial write=%d expected=%d\n", written, convert_samples << hw->info.shift));
305 break;
306 }
307
308 rpos = (rpos + convert_samples) % hw->samples;
309 samples -= convert_samples;
310
311 sol->buffers_played++;
312 write(sol->fd, NULL, 0); /* Increment audio eof marker. */
313 LogFlow(("solaudio: samples=%d written=%d\n", samples, written));
314 }
315
316 LogFlow(("solaudio: run_out: decr=%d\n", decr));
317 hw->rpos = rpos;
318 return decr;
319}
320
321static int solaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
322{
323 solaudioVoiceOut *sol = (solaudioVoiceOut *) hw;
324 switch (cmd)
325 {
326 case VOICE_ENABLE:
327 {
328 /* reset the eof marker and samples markers */
329 audio_info_t audioInfo;
330 AUDIO_INITINFO(&audioInfo);
331 ioctl(sol->fd, AUDIO_GETINFO, &audioInfo);
332 audioInfo.play.eof = 0;
333 audioInfo.play.samples = 0;
334 ioctl(sol->fd, AUDIO_SETINFO, &audioInfo);
335
336 sol->buffers_played = 0;
337 audio_pcm_info_clear_buf (&hw->info, sol->pcm_buf, hw->samples);
338 LogFlow(("solaudio: voice_enable\n"));
339 break;
340 }
341
342 case VOICE_DISABLE:
343 {
344 LogFlow(("solaudio: voice_disable\n"));
345 solaudio_stop(sol);
346 break;
347 }
348 }
349 return 0;
350}
351
352
353static int solaudio_write (SWVoiceOut *sw, void *buf, int len)
354{
355 return audio_pcm_sw_write (sw, buf, len);
356}
357
358static void *solaudio_audio_init (void)
359{
360 return &conf;
361}
362
363static void solaudio_audio_fini (void *opaque)
364{
365 (void) opaque;
366}
367
368static struct audio_pcm_ops solaudio_pcm_ops = {
369 solaudio_init_out,
370 solaudio_fini_out,
371 solaudio_run_out,
372 solaudio_write,
373 solaudio_ctl_out,
374
375 NULL,
376 NULL,
377 NULL,
378 NULL,
379 NULL
380};
381
382
383static struct audio_option solaudio_options[] = {
384 {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size,
385 "Size of the buffer in frames", NULL, 0},
386 {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
387 "Number of buffers", NULL, 0},
388 {NULL, 0, NULL, NULL, NULL, 0}
389};
390
391struct audio_driver solaudio_audio_driver = {
392 INIT_FIELD (name = ) "solaudio",
393 INIT_FIELD (descr = ) "SolarisAudio http://sun.com",
394 INIT_FIELD (options = ) solaudio_options,
395 INIT_FIELD (init = ) solaudio_audio_init,
396 INIT_FIELD (fini = ) solaudio_audio_fini,
397 INIT_FIELD (pcm_ops = ) &solaudio_pcm_ops,
398 INIT_FIELD (can_be_default = ) 1,
399 INIT_FIELD (max_voices_out = ) 1,
400 INIT_FIELD (max_voices_in = ) 0,
401 INIT_FIELD (voice_size_out = ) sizeof (solaudioVoiceOut),
402 INIT_FIELD (voice_size_in = ) 0
403};
404
Note: See TracBrowser for help on using the repository browser.

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