Skip to content

Latest commit

 

History

History
418 lines (358 loc) · 12.1 KB

SDL_bsdaudio.c

File metadata and controls

418 lines (358 loc) · 12.1 KB
 
1
2
/*
Simple DirectMedia Layer
Jan 2, 2016
Jan 2, 2016
3
Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_BSD
/*
* Driver for native OpenBSD/NetBSD audio(4).
* 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>
#include "SDL_timer.h"
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
#include "SDL_bsdaudio.h"
/* Use timer for synchronization */
/* #define USE_TIMER_SYNC */
/* #define DEBUG_AUDIO */
/* #define DEBUG_AUDIO_STREAM */
static void
BSDAUDIO_DetectDevices(void)
{
SDL_EnumUnixAudioDevices(0, NULL);
}
static void
BSDAUDIO_Status(_THIS)
{
#ifdef DEBUG_AUDIO
/* *INDENT-OFF* */
audio_info_t info;
Aug 3, 2016
Aug 3, 2016
65
const audio_prinfo *prinfo;
66
67
68
69
70
if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
fprintf(stderr, "AUDIO_GETINFO failed.\n");
return;
}
Aug 3, 2016
Aug 3, 2016
71
72
73
prinfo = this->iscapture ? &info.play : &info.record;
74
fprintf(stderr, "\n"
Aug 3, 2016
Aug 3, 2016
75
"[%s info]\n"
76
77
78
79
80
81
82
83
84
85
86
87
88
"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"
"",
Aug 3, 2016
Aug 3, 2016
89
90
91
92
93
94
95
96
97
98
99
100
101
this->iscapture ? "record" : "play",
prinfo->buffer_size,
prinfo->sample_rate,
prinfo->channels,
prinfo->precision,
prinfo->encoding,
prinfo->seek,
prinfo->samples,
prinfo->eof,
prinfo->pause ? "yes" : "no",
prinfo->error ? "yes" : "no",
prinfo->waiting ? "yes" : "no",
prinfo->active ? "yes" : "no");
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
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" : "?"));
/* *INDENT-ON* */
#endif /* DEBUG_AUDIO */
}
/* This function waits until it is possible to write a full sound buffer */
static void
BSDAUDIO_WaitDevice(_THIS)
{
#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
/* See if we need to use timed audio synchronization */
if (this->hidden->frame_ticks) {
/* Use timer for general audio synchronization */
Sint32 ticks;
ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
if (ticks > 0) {
SDL_Delay(ticks);
}
} else {
/* Use select() for audio synchronization */
fd_set fdset;
struct timeval timeout;
FD_ZERO(&fdset);
FD_SET(this->hidden->audio_fd, &fdset);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
#ifdef DEBUG_AUDIO
fprintf(stderr, "Waiting for audio to get ready\n");
#endif
if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
<= 0) {
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.
*/
fprintf(stderr, "SDL: %s\n", message);
SDL_OpenedAudioDeviceDisconnected(this);
/* Don't try to close - may hang */
this->hidden->audio_fd = -1;
#ifdef DEBUG_AUDIO
fprintf(stderr, "Done disabling audio\n");
#endif
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Ready!\n");
#endif
}
#endif /* !USE_BLOCKING_WRITES */
}
static void
BSDAUDIO_PlayDevice(_THIS)
{
int written, p = 0;
/* Write the audio data, checking for EAGAIN on broken audio drivers */
do {
written = write(this->hidden->audio_fd,
&this->hidden->mixbuf[p], this->hidden->mixlen - p);
if (written > 0)
p += written;
if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
/* Non recoverable error has occurred. It should be reported!!! */
perror("audio");
break;
}
Aug 3, 2016
Aug 3, 2016
189
190
191
192
193
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
if (p < this->hidden->mixlen
194
195
196
|| ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
SDL_Delay(1); /* Let a little CPU time go by */
}
Aug 3, 2016
Aug 3, 2016
197
} while (p < this->hidden->mixlen);
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/* If timer synchronization is enabled, set the next write frame */
if (this->hidden->frame_ticks) {
this->hidden->next_frame += this->hidden->frame_ticks;
}
/* If we couldn't write, assume fatal error for now */
if (written < 0) {
SDL_OpenedAudioDeviceDisconnected(this);
}
}
static Uint8 *
BSDAUDIO_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
Aug 3, 2016
Aug 3, 2016
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
static int
BSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
{
Uint8 *buffer = (Uint8 *) _buffer;
int br, p = 0;
/* Write the audio data, checking for EAGAIN on broken audio drivers */
do {
br = read(this->hidden->audio_fd, buffer + p, buflen - p);
if (br > 0)
p += br;
if (br == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
/* Non recoverable error has occurred. It should be reported!!! */
perror("audio");
return p ? p : -1;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Captured %d bytes of audio data\n", br);
#endif
if (p < buflen
|| ((br < 0) && ((errno == 0) || (errno == EAGAIN)))) {
SDL_Delay(1); /* Let a little CPU time go by */
}
} while (p < buflen);
}
static void
BSDAUDIO_FlushCapture(_THIS)
{
audio_info_t info;
size_t remain;
Uint8 buf[512];
if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
return; /* oh well. */
}
remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8));
while (remain > 0) {
const size_t len = SDL_min(sizeof (buf), remain);
const int br = read(this->hidden->audio_fd, buf, len);
if (br <= 0) {
return; /* oh well. */
}
remain -= br;
}
}
267
268
269
static void
BSDAUDIO_CloseDevice(_THIS)
{
Aug 5, 2016
Aug 5, 2016
270
271
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
Aug 5, 2016
Aug 5, 2016
273
SDL_free(this->hidden->mixbuf);
Aug 5, 2016
Aug 5, 2016
274
SDL_free(this->hidden);
275
276
277
278
279
}
static int
BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
{
Aug 3, 2016
Aug 3, 2016
280
const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
281
282
SDL_AudioFormat format = 0;
audio_info_t info;
Aug 3, 2016
Aug 3, 2016
283
audio_prinfo *prinfo = iscapture ? &info.play : &info.record;
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/* We don't care what the devname is...we'll try to open anything. */
/* ...but default to first name in the list... */
if (devname == NULL) {
devname = SDL_GetAudioDeviceName(0, iscapture);
if (devname == NULL) {
return SDL_SetError("No such audio device");
}
}
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
if (this->hidden == NULL) {
return SDL_OutOfMemory();
}
Aug 5, 2016
Aug 5, 2016
300
SDL_zerop(this->hidden);
301
302
303
304
305
306
307
308
309
310
311
312
313
/* Open the audio device */
this->hidden->audio_fd = open(devname, flags, 0);
if (this->hidden->audio_fd < 0) {
return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
}
AUDIO_INITINFO(&info);
/* Calculate the final parameters for this audio specification */
SDL_CalculateAudioSpec(&this->spec);
/* Set to play mode */
Aug 3, 2016
Aug 3, 2016
314
info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
315
316
317
318
319
320
321
322
323
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
return SDL_SetError("Couldn't put device into play mode");
}
AUDIO_INITINFO(&info);
for (format = SDL_FirstAudioFormat(this->spec.format);
format; format = SDL_NextAudioFormat()) {
switch (format) {
case AUDIO_U8:
Aug 3, 2016
Aug 3, 2016
324
325
prinfo->encoding = AUDIO_ENCODING_ULINEAR;
prinfo->precision = 8;
326
327
break;
case AUDIO_S8:
Aug 3, 2016
Aug 3, 2016
328
329
prinfo->encoding = AUDIO_ENCODING_SLINEAR;
prinfo->precision = 8;
330
331
break;
case AUDIO_S16LSB:
Aug 3, 2016
Aug 3, 2016
332
333
prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
prinfo->precision = 16;
334
335
break;
case AUDIO_S16MSB:
Aug 3, 2016
Aug 3, 2016
336
337
prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
prinfo->precision = 16;
338
339
break;
case AUDIO_U16LSB:
Aug 3, 2016
Aug 3, 2016
340
341
prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
prinfo->precision = 16;
342
343
break;
case AUDIO_U16MSB:
Aug 3, 2016
Aug 3, 2016
344
345
prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
prinfo->precision = 16;
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
break;
default:
continue;
}
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
break;
}
}
if (!format) {
return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
}
this->spec.format = format;
AUDIO_INITINFO(&info);
Aug 3, 2016
Aug 3, 2016
363
prinfo->channels = this->spec.channels;
364
365
366
367
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
this->spec.channels = 1;
}
AUDIO_INITINFO(&info);
Aug 3, 2016
Aug 3, 2016
368
prinfo->sample_rate = this->spec.freq;
369
370
371
372
373
info.blocksize = this->spec.size;
info.hiwat = 5;
info.lowat = 3;
(void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
(void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
Aug 3, 2016
Aug 3, 2016
374
375
376
377
378
this->spec.freq = prinfo->sample_rate;
if (!iscapture) {
/* Allocate mixing buffer */
this->hidden->mixlen = this->spec.size;
Aug 5, 2016
Aug 5, 2016
379
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
Aug 3, 2016
Aug 3, 2016
380
381
382
383
if (this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
}
BSDAUDIO_Status(this);
/* We're ready to rock and roll. :-) */
return 0;
}
static int
BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
{
/* Set the function pointers */
impl->DetectDevices = BSDAUDIO_DetectDevices;
impl->OpenDevice = BSDAUDIO_OpenDevice;
impl->PlayDevice = BSDAUDIO_PlayDevice;
impl->WaitDevice = BSDAUDIO_WaitDevice;
impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
impl->CloseDevice = BSDAUDIO_CloseDevice;
Aug 3, 2016
Aug 3, 2016
402
403
impl->CaptureFromDevice = BSDAUDIO_CaptureFromDevice;
impl->FlushCapture = BSDAUDIO_FlushCapture;
Aug 3, 2016
Aug 3, 2016
405
impl->HasCaptureSupport = SDL_TRUE;
406
407
408
409
410
411
412
413
414
415
416
417
418
impl->AllowsArbitraryDeviceNames = 1;
return 1; /* this audio target is available. */
}
AudioBootStrap BSD_AUDIO_bootstrap = {
"bsd", "BSD audio", BSDAUDIO_Init, 0
};
#endif /* SDL_AUDIO_DRIVER_BSD */
/* vi: set ts=4 sw=4 expandtab: */