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