comparison src/audio/openbsd/SDL_openbsdaudio.c @ 37:3ad7157c6cfa

Added native OpenBSD audio driver (thanks vedge!)
author Sam Lantinga <slouken@lokigames.com>
date Sat, 26 May 2001 16:58:37 +0000
parents
children ae6e6b73333f
comparison
equal deleted inserted replaced
36:13ee9f4834ea 37:3ad7157c6cfa
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@devolution.com
21 */
22
23 /*
24 * Driver for native OpenBSD audio(4).
25 * vedge@vedge.com.ar.
26 */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <sys/time.h>
36 #include <sys/ioctl.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <sys/audioio.h>
40
41 #include "SDL_audio.h"
42 #include "SDL_error.h"
43 #include "SDL_audiomem.h"
44 #include "SDL_audio_c.h"
45 #include "SDL_timer.h"
46 #include "SDL_audiodev_c.h"
47 #include "SDL_openbsdaudio.h"
48
49 /* The tag name used by OpenBSD audio */
50 #define OBSD_DRIVER_NAME "openbsd"
51
52 /* Open the audio device for playback, and don't block if busy */
53 /* #define USE_BLOCKING_WRITES */
54
55 /* Use timer for synchronization */
56 /* #define USE_TIMER_SYNC */
57
58 /* #define DEBUG_AUDIO */
59 /* #define DEBUG_AUDIO_STREAM */
60
61 #ifdef USE_BLOCKING_WRITES
62 #define OPEN_FLAGS O_WRONLY
63 #else
64 #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK)
65 #endif
66
67 /* Audio driver functions */
68 static void OBSD_WaitAudio(_THIS);
69 static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec);
70 static void OBSD_PlayAudio(_THIS);
71 static Uint8 *OBSD_GetAudioBuf(_THIS);
72 static void OBSD_CloseAudio(_THIS);
73
74 #ifdef DEBUG_AUDIO
75 static void OBSD_Status(_THIS);
76 #endif
77
78 /* Audio driver bootstrap functions */
79
80 static int
81 Audio_Available(void)
82 {
83 int fd;
84 int available;
85
86 available = 0;
87 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
88 if(fd >= 0) {
89 available = 1;
90 close(fd);
91 }
92 return(available);
93 }
94
95 static void
96 Audio_DeleteDevice(SDL_AudioDevice *device)
97 {
98 free(device->hidden);
99 free(device);
100 }
101
102 static SDL_AudioDevice
103 *Audio_CreateDevice(int devindex)
104 {
105 SDL_AudioDevice *this;
106
107 /* Initialize all variables that we clean on shutdown */
108 this = (SDL_AudioDevice*)malloc(sizeof(SDL_AudioDevice));
109 if(this) {
110 memset(this, 0, (sizeof *this));
111 this->hidden =
112 (struct SDL_PrivateAudioData*)malloc((sizeof *this->hidden));
113 }
114 if((this == NULL) || (this->hidden == NULL)) {
115 SDL_OutOfMemory();
116 if(this) free(this);
117 return(0);
118 }
119 memset(this->hidden, 0, (sizeof *this->hidden));
120 audio_fd = -1;
121
122 /* Set the function pointers */
123 this->OpenAudio = OBSD_OpenAudio;
124 this->WaitAudio = OBSD_WaitAudio;
125 this->PlayAudio = OBSD_PlayAudio;
126 this->GetAudioBuf = OBSD_GetAudioBuf;
127 this->CloseAudio = OBSD_CloseAudio;
128
129 this->free = Audio_DeleteDevice;
130
131 return this;
132 }
133
134 AudioBootStrap OBSD_bootstrap = {
135 OBSD_DRIVER_NAME, "Native OpenBSD audio",
136 Audio_Available, Audio_CreateDevice
137 };
138
139 /* This function waits until it is possible to write a full sound buffer */
140 static void
141 OBSD_WaitAudio(_THIS)
142 {
143 #ifndef USE_BLOCKING_WRITES
144 fd_set fdset;
145
146 /* Check to see if the thread-parent process is still alive */
147 {
148 static int cnt = 0;
149 /* Note that this only works with thread implementations
150 that use a different process id for each thread. */
151 if(parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
152 if(kill(parent, 0) < 0)
153 this->enabled = 0;
154 }
155 }
156
157 #ifdef USE_TIMER_SYNC
158 /* See if we need to use timed audio synchronization */
159 if(frame_ticks)
160 {
161 /* Use timer for general audio synchronization */
162 Sint32 ticks;
163
164 ticks = ((Sint32)(next_frame - SDL_GetTicks())) - FUDGE_TICKS;
165 if(ticks > 0)
166 SDL_Delay(ticks);
167 }
168 else
169 #endif /* USE_TIMER_SYNC */
170 {
171 /* Use select() for audio synchronization */
172 struct timeval timeout;
173 FD_ZERO(&fdset);
174 FD_SET(audio_fd, &fdset);
175 timeout.tv_sec = 10;
176 timeout.tv_usec = 0;
177
178 #if defined(DEBUG_AUDIO_STREAM) && defined(DEBUG_AUDIO_STREAM)
179 OBSD_Status(this);
180 #endif
181 if(select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0)
182 {
183 const char *message =
184 "Audio timeout - buggy audio driver? (disabled)";
185 fprintf(stderr, "SDL: %s\n", message);
186 this->enabled = 0;
187 audio_fd = -1;
188 }
189 }
190 #endif /* !USE_BLOCKING_WRITES */
191
192 }
193
194 static void
195 OBSD_PlayAudio(_THIS)
196 {
197 int written;
198
199 /* Write the audio data, checking for EAGAIN on broken audio drivers */
200 do
201 {
202 written = write(audio_fd, mixbuf, mixlen);
203 if((written < 0) && ((errno == 0) || (errno == EAGAIN)))
204 SDL_Delay(1);
205 }
206 while((written < 0) &&
207 ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)));
208
209 #ifdef USE_TIMER_SYNC
210 if(frame_ticks)
211 next_frame += frame_ticks;
212 #endif
213
214 /* If we couldn't write, assume fatal error for now */
215 if(written < 0)
216 this->enabled = 0;
217
218 #ifdef DEBUG_AUDIO_STREAM
219 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
220 #endif
221 }
222
223 static Uint8
224 *OBSD_GetAudioBuf(_THIS)
225 {
226 return(mixbuf);
227 }
228
229 static void
230 OBSD_CloseAudio(_THIS)
231 {
232 if(mixbuf != NULL) {
233 SDL_FreeAudioMem(mixbuf);
234 mixbuf = NULL;
235 }
236 if(audio_fd >= 0) {
237 close(audio_fd);
238 audio_fd = -1;
239 }
240 }
241
242 #ifdef DEBUG_AUDIO
243 void
244 OBSD_Status(_THIS)
245 {
246 audio_info_t info;
247
248 if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
249 fprintf(stderr,"AUDIO_GETINFO failed.\n");
250 return;
251 }
252
253 fprintf(stderr,"
254 [play/record info]
255 buffer size : %d bytes
256 sample rate : %i Hz
257 channels : %i
258 precision : %i-bit
259 encoding : 0x%x
260 seek : %i
261 sample count : %i
262 EOF count : %i
263 paused : %s
264 error occured : %s
265 waiting : %s
266 active : %s
267 ",
268 info.play.buffer_size,
269 info.play.sample_rate,
270 info.play.channels,
271 info.play.precision,
272 info.play.encoding,
273 info.play.seek,
274 info.play.samples,
275 info.play.eof,
276 info.play.pause ? "yes" : "no",
277 info.play.error ? "yes" : "no",
278 info.play.waiting ? "yes" : "no",
279 info.play.active ? "yes": "no");
280
281 fprintf(stderr,"
282 [audio info]
283 monitor_gain : %i
284 hw block size : %d bytes
285 hi watermark : %i
286 lo watermark : %i
287 audio mode : %s
288 ",
289 info.monitor_gain,
290 info.blocksize,
291 info.hiwat, info.lowat,
292 (info.mode == AUMODE_PLAY) ? "PLAY"
293 : (info.mode = AUMODE_RECORD) ? "RECORD"
294 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL"
295 : "???"));
296 }
297 #endif /* DEBUG_AUDIO */
298
299 static int
300 OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec)
301 {
302 char audiodev[64];
303 Uint16 setenc;
304 audio_encoding_t enc;
305 audio_info_t info;
306
307 AUDIO_INITINFO(&info);
308
309 /* Calculate the final parameters for this audio specification */
310 SDL_CalculateAudioSpec(spec);
311
312 #ifdef USE_TIMER_SYNC
313 frame_ticks = 0.0;
314 #endif
315
316 /* Open the audio device */
317 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
318 if(audio_fd < 0) {
319 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
320 return(-1);
321 }
322
323 /* Set to play mode */
324 info.mode = AUMODE_PLAY;
325 if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) {
326 SDL_SetError("Couldn't put device into play mode");
327 return(-1);
328 }
329
330 mixbuf = NULL;
331 setenc = 0;
332
333 for(enc.index = 0; (ioctl(audio_fd, AUDIO_GETENC, &enc)>=0)
334 && (enc.encoding != setenc); enc.index++)
335 {
336 switch(spec->format)
337 {
338 case AUDIO_U8: /* 8-bit unsigned linear */
339 setenc = AUDIO_ENCODING_PCM8;
340 break;
341 case AUDIO_S8: /* 8-bit signed linear */
342 setenc = AUDIO_ENCODING_SLINEAR;
343 break;
344 case AUDIO_U16LSB: /* 16-bit unsigned linear, LSB */
345 setenc = AUDIO_ENCODING_ULINEAR_LE;
346 break;
347 case AUDIO_U16MSB: /* 16-bit unsigned linear, MSB */
348 setenc = AUDIO_ENCODING_ULINEAR_BE;
349 break;
350 case AUDIO_S16LSB: /* 16-bit signed linear, LSB */
351 setenc = AUDIO_ENCODING_SLINEAR_LE;
352 break;
353 case AUDIO_S16MSB: /* 16-bit signed linear, MSB */
354 setenc = AUDIO_ENCODING_SLINEAR_BE;
355 break;
356 }
357 #ifdef DEBUG_AUDIO
358 fprintf(stderr,"encoding #%i: \"%s\" %i-bit (0x%x) flags=%i...\n",
359 enc.index, enc.name, enc.precision, enc.encoding, enc.flags);
360 #endif
361 }
362
363 if(!setenc) {
364 SDL_SetError("No supported encoding for 0x%x", spec->format);
365 return(-1);
366 }
367
368 /* Set audio encoding */
369 info.play.encoding = enc.encoding;
370 info.play.precision = enc.precision;
371 if((ioctl(audio_fd, AUDIO_SETINFO, &info) < 0)) {
372 SDL_SetError("Couldn't set encoding to 0x%x %i-bit",
373 enc.encoding, enc.precision);
374 return(-1);
375 }
376
377 /* Set audio channels */
378 info.play.channels = spec->channels;
379 if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) {
380 info.play.channels = (spec->channels > 1);
381 ioctl(audio_fd, AUDIO_SETINFO, &info);
382 }
383
384 /* Set the sample rate */
385 info.play.sample_rate = spec->freq;
386 if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) {
387 SDL_SetError("Couldn't set sample rate to %i Hz", spec->freq);
388 return(-1);
389 }
390
391 /* Allocate mixing buffer */
392 mixlen = spec->size;
393 mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen);
394 if(mixbuf == NULL) {
395 return(-1);
396 }
397 memset(mixbuf, spec->silence, spec->size);
398
399 /* Get the parent process id (we're the parent of the audio thread) */
400 parent = getpid();
401
402 #ifdef DEBUG_AUDIO
403 OBSD_Status(this);
404 #endif
405
406 /* We're ready to rock and roll. :-) */
407 return(0);
408 }