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