Mercurial > sdl-ios-xcode
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 } |