This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_dspaudio.c
392 lines (341 loc) · 10.9 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2009 Sam Lantinga
4
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
10
11
12
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
13
Lesser General Public License for more details.
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
Sam Lantinga
20
slouken@libsdl.org
21
22
23
Modified in Oct 2004 by Hannu Savolainen
hannu@opensound.com
24
*/
25
#include "SDL_config.h"
26
27
28
/* Allow access to a raw mixing buffer */
29
30
#include <stdio.h> /* For perror() */
#include <string.h> /* For strerror() */
31
32
33
34
35
36
37
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
38
39
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
40
41
42
43
/* This is installed on some systems */
#include <soundcard.h>
#else
/* This is recommended by OSS */
44
#include <sys/soundcard.h>
47
#include "SDL_timer.h"
48
#include "SDL_audio.h"
49
50
51
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
#include "../SDL_audiodev_c.h"
52
53
54
55
56
57
#include "SDL_dspaudio.h"
/* The tag name used by DSP audio */
#define DSP_DRIVER_NAME "dsp"
/* Open the audio device for playback, and don't block if busy */
58
59
#define OPEN_FLAGS_OUTPUT (O_WRONLY|O_NONBLOCK)
#define OPEN_FLAGS_INPUT (O_RDONLY|O_NONBLOCK)
61
62
63
64
static char **outputDevices = NULL;
static int outputDeviceCount = 0;
static char **inputDevices = NULL;
static int inputDeviceCount = 0;
66
67
static inline void
free_device_list(char ***devs, int *count)
69
SDL_FreeUnixAudioDevices(devs, count);
70
71
}
72
73
static inline void
build_device_list(int iscapture, char ***devs, int *count)
75
76
77
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
free_device_list(devs, count);
SDL_EnumUnixAudioDevices(flags, 0, NULL, devs, count);
78
79
}
80
81
static inline void
build_device_lists(void)
83
84
85
build_device_list(0, &outputDevices, &outputDeviceCount);
build_device_list(1, &inputDevices, &inputDeviceCount);
}
88
89
90
91
92
static inline void
free_device_lists(void)
{
free_device_list(&outputDevices, &outputDeviceCount);
free_device_list(&inputDevices, &inputDeviceCount);
93
94
95
}
97
DSP_Deinitialize(void)
99
free_device_lists();
100
101
}
102
103
104
static int
DSP_DetectDevices(int iscapture)
105
{
106
107
108
109
110
111
if (iscapture) {
build_device_list(1, &inputDevices, &inputDeviceCount);
return inputDeviceCount;
} else {
build_device_list(0, &outputDevices, &outputDeviceCount);
return outputDeviceCount;
114
return 0; /* shouldn't ever hit this. */
115
116
}
117
118
static const char *
DSP_GetDeviceName(int index, int iscapture)
119
{
120
121
122
123
124
125
126
127
if ((iscapture) && (index < inputDeviceCount)) {
return inputDevices[index];
} else if ((!iscapture) && (index < outputDeviceCount)) {
return outputDevices[index];
}
SDL_SetError("No such device");
return NULL;
128
129
}
132
DSP_CloseDevice(_THIS)
133
{
134
135
136
137
138
139
140
141
142
143
144
if (this->hidden != NULL) {
if (this->hidden->mixbuf != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
}
if (this->hidden->audio_fd >= 0) {
close(this->hidden->audio_fd);
this->hidden->audio_fd = -1;
}
SDL_free(this->hidden);
this->hidden = NULL;
146
147
}
150
DSP_OpenDevice(_THIS, const char *devname, int iscapture)
151
{
152
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
153
154
155
int format;
int value;
int frag_spec;
156
SDL_AudioFormat test_format;
158
159
160
/* 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) {
161
162
if (((iscapture) && (inputDeviceCount == 0)) ||
((!iscapture) && (outputDeviceCount == 0))) {
163
164
165
166
167
168
SDL_SetError("No such audio device");
return 0;
}
devname = ((iscapture) ? inputDevices[0] : outputDevices[0]);
}
169
170
171
172
173
174
175
176
177
/* Make sure fragment size stays a power of 2, or OSS fails. */
/* I don't know which of these are actually legal values, though... */
if (this->spec.channels > 8)
this->spec.channels = 8;
else if (this->spec.channels > 4)
this->spec.channels = 4;
else if (this->spec.channels > 2)
this->spec.channels = 2;
178
179
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
180
SDL_malloc((sizeof *this->hidden));
181
182
183
184
185
186
if (this->hidden == NULL) {
SDL_OutOfMemory();
return 0;
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
187
/* Open the audio device */
188
189
190
191
192
this->hidden->audio_fd = open(devname, flags, 0);
if (this->hidden->audio_fd < 0) {
DSP_CloseDevice(this);
SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
return 0;
194
this->hidden->mixbuf = NULL;
195
196
197
/* Make the file descriptor use blocking writes with fcntl() */
{
198
199
200
201
202
long ctlflags;
ctlflags = fcntl(this->hidden->audio_fd, F_GETFL);
ctlflags &= ~O_NONBLOCK;
if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
DSP_CloseDevice(this);
203
SDL_SetError("Couldn't set audio blocking mode");
205
206
207
208
}
}
/* Get a list of supported hardware formats */
209
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
210
perror("SNDCTL_DSP_GETFMTS");
211
DSP_CloseDevice(this);
212
SDL_SetError("Couldn't get audio format list");
214
215
216
217
}
/* Try for a closest match on audio format */
format = 0;
218
for (test_format = SDL_FirstAudioFormat(this->spec.format);
219
!format && test_format;) {
220
#ifdef DEBUG_AUDIO
221
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
222
#endif
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
switch (test_format) {
case AUDIO_U8:
if (value & AFMT_U8) {
format = AFMT_U8;
}
break;
case AUDIO_S16LSB:
if (value & AFMT_S16_LE) {
format = AFMT_S16_LE;
}
break;
case AUDIO_S16MSB:
if (value & AFMT_S16_BE) {
format = AFMT_S16_BE;
}
break;
239
240
241
242
243
#if 0
/*
* These formats are not used by any real life systems so they are not
* needed here.
*/
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
case AUDIO_S8:
if (value & AFMT_S8) {
format = AFMT_S8;
}
break;
case AUDIO_U16LSB:
if (value & AFMT_U16_LE) {
format = AFMT_U16_LE;
}
break;
case AUDIO_U16MSB:
if (value & AFMT_U16_BE) {
format = AFMT_U16_BE;
}
break;
260
261
262
263
264
265
266
267
268
default:
format = 0;
break;
}
if (!format) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
269
DSP_CloseDevice(this);
270
SDL_SetError("Couldn't find any hardware audio formats");
273
this->spec.format = test_format;
274
275
276
/* Set the audio format */
value = format;
277
278
if ((ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
(value != format)) {
279
perror("SNDCTL_DSP_SETFMT");
280
DSP_CloseDevice(this);
281
SDL_SetError("Couldn't set audio format");
283
284
285
}
/* Set the number of channels of output */
286
287
value = this->spec.channels;
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
288
perror("SNDCTL_DSP_CHANNELS");
289
DSP_CloseDevice(this);
290
SDL_SetError("Cannot set the number of channels");
293
this->spec.channels = value;
294
295
/* Set the DSP frequency */
296
297
value = this->spec.freq;
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
298
perror("SNDCTL_DSP_SPEED");
299
DSP_CloseDevice(this);
300
SDL_SetError("Couldn't set audio frequency");
303
this->spec.freq = value;
304
305
/* Calculate the final parameters for this audio specification */
306
SDL_CalculateAudioSpec(&this->spec);
307
308
/* Determine the power of two of the fragment size */
309
310
311
for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
if ((0x01U << frag_spec) != this->spec.size) {
DSP_CloseDevice(this);
312
SDL_SetError("Fragment size must be a power of two");
314
315
316
317
}
frag_spec |= 0x00020000; /* two fragments, for low latency */
/* Set the audio buffering parameters */
318
#ifdef DEBUG_AUDIO
319
320
fprintf(stderr, "Requesting %d fragments of size %d\n",
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
322
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
323
324
perror("SNDCTL_DSP_SETFRAGMENT");
}
325
#ifdef DEBUG_AUDIO
326
327
{
audio_buf_info info;
328
ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
329
330
331
332
333
fprintf(stderr, "fragments = %d\n", info.fragments);
fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
fprintf(stderr, "fragsize = %d\n", info.fragsize);
fprintf(stderr, "bytes = %d\n", info.bytes);
}
336
/* Allocate mixing buffer */
337
338
339
340
341
342
this->hidden->mixlen = this->spec.size;
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
DSP_CloseDevice(this);
SDL_OutOfMemory();
return 0;
344
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
346
/* We're ready to rock and roll. :-) */
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
return 1;
}
static void
DSP_PlayDevice(_THIS)
{
const Uint8 *mixbuf = this->hidden->mixbuf;
const int mixlen = this->hidden->mixlen;
if (write(this->hidden->audio_fd, mixbuf, mixlen) == -1) {
perror("Audio write");
this->enabled = 0;
}
#ifdef DEBUG_AUDIO
fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
#endif
363
}
365
366
367
368
369
370
371
static Uint8 *
DSP_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
static int
372
DSP_Init(SDL_AudioDriverImpl * impl)
373
374
375
376
377
378
379
380
381
382
383
{
/* Set the function pointers */
impl->DetectDevices = DSP_DetectDevices;
impl->GetDeviceName = DSP_GetDeviceName;
impl->OpenDevice = DSP_OpenDevice;
impl->PlayDevice = DSP_PlayDevice;
impl->GetDeviceBuf = DSP_GetDeviceBuf;
impl->CloseDevice = DSP_CloseDevice;
impl->Deinitialize = DSP_Deinitialize;
build_device_lists();
384
return (outputDeviceCount > 0) ? 2 : 1;
385
386
387
388
389
390
391
}
AudioBootStrap DSP_bootstrap = {
DSP_DRIVER_NAME, "OSS /dev/dsp standard audio", DSP_Init, 0
};
392
/* vi: set ts=4 sw=4 expandtab: */