/
SDL_netbsdaudio.c
412 lines (353 loc) · 12 KB
1
2
/*
Simple DirectMedia Layer
3
Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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"
23
#if SDL_AUDIO_DRIVER_NETBSD
24
25
/*
26
* Driver for native NetBSD audio(4).
27
28
29
30
31
32
33
34
35
36
37
38
39
40
* 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"
41
#include "../../core/unix/SDL_poll.h"
42
43
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
44
#include "SDL_netbsdaudio.h"
45
46
47
48
49
50
51
52
53
/* Use timer for synchronization */
/* #define USE_TIMER_SYNC */
/* #define DEBUG_AUDIO */
/* #define DEBUG_AUDIO_STREAM */
static void
54
NETBSDAUDIO_DetectDevices(void)
55
56
57
58
59
60
{
SDL_EnumUnixAudioDevices(0, NULL);
}
static void
61
NETBSDAUDIO_Status(_THIS)
62
63
64
65
{
#ifdef DEBUG_AUDIO
/* *INDENT-OFF* */
audio_info_t info;
66
const audio_prinfo *prinfo;
67
68
69
70
71
if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
fprintf(stderr, "AUDIO_GETINFO failed.\n");
return;
}
72
73
74
prinfo = this->iscapture ? &info.play : &info.record;
75
fprintf(stderr, "\n"
76
"[%s info]\n"
77
78
79
80
81
82
83
84
85
86
87
88
89
"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"
"",
90
91
92
93
94
95
96
97
98
99
100
101
102
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");
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
125
NETBSDAUDIO_WaitDevice(_THIS)
126
127
128
129
130
131
132
133
134
135
136
137
{
#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 {
138
/* Use SDL_IOReady() for audio synchronization */
139
140
141
#ifdef DEBUG_AUDIO
fprintf(stderr, "Waiting for audio to get ready\n");
#endif
142
if (SDL_IOReady(this->hidden->audio_fd, SDL_TRUE, 10 * 1000)
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<= 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
166
NETBSDAUDIO_PlayDevice(_THIS)
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
{
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;
}
183
184
185
186
187
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
#endif
if (p < this->hidden->mixlen
188
189
190
|| ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
SDL_Delay(1); /* Let a little CPU time go by */
}
191
} while (p < this->hidden->mixlen);
192
193
194
195
196
197
198
199
200
201
202
203
204
/* 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 *
205
NETBSDAUDIO_GetDeviceBuf(_THIS)
206
207
208
209
{
return (this->hidden->mixbuf);
}
210
211
static int
212
NETBSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
213
214
215
216
{
Uint8 *buffer = (Uint8 *) _buffer;
int br, p = 0;
217
/* Capture the audio data, checking for EAGAIN on broken audio drivers */
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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
240
NETBSDAUDIO_FlushCapture(_THIS)
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
{
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;
}
}
261
static void
262
NETBSDAUDIO_CloseDevice(_THIS)
263
{
264
265
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
266
}
267
SDL_free(this->hidden->mixbuf);
268
SDL_free(this->hidden);
269
270
271
}
static int
272
NETBSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
273
{
274
const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
275
276
SDL_AudioFormat format = 0;
audio_info_t info;
277
audio_prinfo *prinfo = iscapture ? &info.play : &info.record;
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/* 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();
}
294
SDL_zerop(this->hidden);
295
296
297
298
299
300
301
302
303
304
305
306
307
/* 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 */
308
info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
309
310
311
312
313
314
315
316
317
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:
318
319
prinfo->encoding = AUDIO_ENCODING_ULINEAR;
prinfo->precision = 8;
320
321
break;
case AUDIO_S8:
322
323
prinfo->encoding = AUDIO_ENCODING_SLINEAR;
prinfo->precision = 8;
324
325
break;
case AUDIO_S16LSB:
326
327
prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
prinfo->precision = 16;
328
329
break;
case AUDIO_S16MSB:
330
331
prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
prinfo->precision = 16;
332
333
break;
case AUDIO_U16LSB:
334
335
prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
prinfo->precision = 16;
336
337
break;
case AUDIO_U16MSB:
338
339
prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
prinfo->precision = 16;
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
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);
357
prinfo->channels = this->spec.channels;
358
359
360
361
if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
this->spec.channels = 1;
}
AUDIO_INITINFO(&info);
362
prinfo->sample_rate = this->spec.freq;
363
364
365
366
367
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);
368
369
370
371
372
this->spec.freq = prinfo->sample_rate;
if (!iscapture) {
/* Allocate mixing buffer */
this->hidden->mixlen = this->spec.size;
373
this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
374
375
376
377
if (this->hidden->mixbuf == NULL) {
return SDL_OutOfMemory();
}
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
378
379
}
380
NETBSDAUDIO_Status(this);
381
382
383
384
385
386
/* We're ready to rock and roll. :-) */
return 0;
}
static int
387
NETBSDAUDIO_Init(SDL_AudioDriverImpl * impl)
388
389
{
/* Set the function pointers */
390
391
392
393
394
395
396
397
impl->DetectDevices = NETBSDAUDIO_DetectDevices;
impl->OpenDevice = NETBSDAUDIO_OpenDevice;
impl->PlayDevice = NETBSDAUDIO_PlayDevice;
impl->WaitDevice = NETBSDAUDIO_WaitDevice;
impl->GetDeviceBuf = NETBSDAUDIO_GetDeviceBuf;
impl->CloseDevice = NETBSDAUDIO_CloseDevice;
impl->CaptureFromDevice = NETBSDAUDIO_CaptureFromDevice;
impl->FlushCapture = NETBSDAUDIO_FlushCapture;
398
399
impl->HasCaptureSupport = SDL_TRUE;
400
401
402
403
404
405
impl->AllowsArbitraryDeviceNames = 1;
return 1; /* this audio target is available. */
}
406
407
AudioBootStrap NETBSDAUDIO_bootstrap = {
"netbsd", "NetBSD audio", NETBSDAUDIO_Init, 0
408
409
};
410
#endif /* SDL_AUDIO_DRIVER_NETBSD */
411
412
/* vi: set ts=4 sw=4 expandtab: */