This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_dspaudio.c
393 lines (341 loc) · 10.7 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2006 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.
14
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>
45
#endif
46
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)
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
static char **outputDevices = NULL;
static int outputDeviceCount = 0;
static char **inputDevices = NULL;
static int inputDeviceCount = 0;
static inline void
free_device_list(char ***devs, int *count)
{
SDL_FreeUnixAudioDevices(devs, count);
}
static inline void
build_device_list(int iscapture, char ***devs, int *count)
{
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
free_device_list(devs, count);
SDL_EnumUnixAudioDevices(flags, 0, NULL, devs, count);
}
static inline void
build_device_lists(void)
{
build_device_list(0, &outputDevices, &outputDeviceCount);
build_device_list(1, &inputDevices, &inputDeviceCount);
}
static inline void
free_device_lists(void)
{
free_device_list(&outputDevices, &outputDeviceCount);
free_device_list(&inputDevices, &inputDeviceCount);
}
96
static int
97
DSP_Available(void)
98
{
99
int available = 0;
100
101
102
103
build_device_lists();
available = ((outputDeviceCount > 0) || (inputDeviceCount > 0));
free_device_lists();
return available;
104
105
106
}
107
108
static void
DSP_Deinitialize(void)
109
110
111
112
113
{
free_device_lists();
}
114
115
static int
DSP_DetectDevices(int iscapture)
116
{
117
118
119
120
121
122
123
if (iscapture) {
build_device_list(1, &inputDevices, &inputDeviceCount);
return inputDeviceCount;
} else {
build_device_list(0, &outputDevices, &outputDeviceCount);
return outputDeviceCount;
}
124
125
126
return 0; /* shouldn't ever hit this. */
}
127
128
129
static const char *
DSP_GetDeviceName(int index, int iscapture)
130
{
131
132
133
134
135
136
137
if ((iscapture) && (index < inputDeviceCount)) {
return inputDevices[index];
} else if ((!iscapture) && (index < outputDeviceCount)) {
return outputDevices[index];
}
SDL_SetError("No such device");
138
return NULL;
139
140
}
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
static void
DSP_CloseDevice(_THIS)
{
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;
}
}
160
static int
161
DSP_OpenDevice(_THIS, const char *devname, int iscapture)
162
{
163
const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
164
165
166
int format;
int value;
int frag_spec;
167
SDL_AudioFormat test_format;
168
169
170
171
172
173
174
175
176
177
178
179
/* 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) {
if ( ((iscapture) && (inputDeviceCount == 0)) ||
((!iscapture) && (outputDeviceCount == 0)) ) {
SDL_SetError("No such audio device");
return 0;
}
devname = ((iscapture) ? inputDevices[0] : outputDevices[0]);
}
180
181
182
183
184
185
186
187
188
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
if (this->hidden == NULL) {
SDL_OutOfMemory();
return 0;
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
189
/* Open the audio device */
190
this->hidden->audio_fd = open(devname, flags, 0);
191
if (this->hidden->audio_fd < 0) {
192
SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
193
return 0;
194
}
195
this->hidden->mixbuf = NULL;
196
197
198
199
/* Make the file descriptor use blocking writes with fcntl() */
{
long flags;
200
flags = fcntl(this->hidden->audio_fd, F_GETFL);
201
flags &= ~O_NONBLOCK;
202
if (fcntl(this->hidden->audio_fd, F_SETFL, flags) < 0) {
203
SDL_SetError("Couldn't set audio blocking mode");
204
205
DSP_CloseDevice(this);
return 0;
206
207
208
209
}
}
/* Get a list of supported hardware formats */
210
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
211
212
perror("SNDCTL_DSP_GETFMTS");
SDL_SetError("Couldn't get audio format list");
213
214
DSP_CloseDevice(this);
return 0;
215
216
217
218
}
/* Try for a closest match on audio format */
format = 0;
219
for (test_format = SDL_FirstAudioFormat(this->spec.format);
220
!format && test_format;) {
221
#ifdef DEBUG_AUDIO
222
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
223
#endif
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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;
240
241
242
243
244
#if 0
/*
* These formats are not used by any real life systems so they are not
* needed here.
*/
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
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
#endif
261
262
263
264
265
266
267
268
269
270
default:
format = 0;
break;
}
if (!format) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
SDL_SetError("Couldn't find any hardware audio formats");
271
272
DSP_CloseDevice(this);
return 0;
273
}
274
this->spec.format = test_format;
275
276
277
/* Set the audio format */
value = format;
278
279
if ( (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
(value != format) ) {
280
281
perror("SNDCTL_DSP_SETFMT");
SDL_SetError("Couldn't set audio format");
282
283
DSP_CloseDevice(this);
return 0;
284
285
286
}
/* Set the number of channels of output */
287
288
value = this->spec.channels;
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
289
290
perror("SNDCTL_DSP_CHANNELS");
SDL_SetError("Cannot set the number of channels");
291
292
DSP_CloseDevice(this);
return 0;
293
}
294
this->spec.channels = value;
295
296
/* Set the DSP frequency */
297
298
value = this->spec.freq;
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
299
300
perror("SNDCTL_DSP_SPEED");
SDL_SetError("Couldn't set audio frequency");
301
302
DSP_CloseDevice(this);
return 0;
303
}
304
this->spec.freq = value;
305
306
/* Calculate the final parameters for this audio specification */
307
SDL_CalculateAudioSpec(&this->spec);
308
309
/* Determine the power of two of the fragment size */
310
311
for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
if ((0x01U << frag_spec) != this->spec.size) {
312
SDL_SetError("Fragment size must be a power of two");
313
314
DSP_CloseDevice(this);
return 0;
315
316
317
318
}
frag_spec |= 0x00020000; /* two fragments, for low latency */
/* Set the audio buffering parameters */
319
#ifdef DEBUG_AUDIO
320
321
fprintf(stderr, "Requesting %d fragments of size %d\n",
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
322
#endif
323
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
324
325
perror("SNDCTL_DSP_SETFRAGMENT");
}
326
#ifdef DEBUG_AUDIO
327
328
{
audio_buf_info info;
329
ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
330
331
332
333
334
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);
}
335
#endif
336
337
/* Allocate mixing buffer */
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);
return 0;
343
}
344
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
345
346
/* We're ready to rock and roll. :-) */
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
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
}
static Uint8 *
DSP_GetDeviceBuf(_THIS)
{
return (this->hidden->mixbuf);
}
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
static int
DSP_Init(SDL_AudioDriverImpl *impl)
{
/* 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();
return 1;
}
AudioBootStrap DSP_bootstrap = {
DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
DSP_Available, DSP_Init, 0
};
393
/* vi: set ts=4 sw=4 expandtab: */