This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_dspaudio.c
354 lines (308 loc) · 9.67 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
58
59
60
#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 */
#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK)
/* Audio driver functions */
61
62
63
64
65
66
67
static int DSP_DetectDevices(int iscapture);
static const char *DSP_GetDeviceName(int index, int iscapture);
static int DSP_OpenDevice(_THIS, const char *devname, int iscapture);
static void DSP_WaitDevice(_THIS);
static void DSP_PlayDevice(_THIS);
static Uint8 *DSP_GetDeviceBuf(_THIS);
static void DSP_CloseDevice(_THIS);
68
69
70
/* Audio driver bootstrap functions */
71
static int
72
DSP_Available(void)
73
{
74
75
76
77
78
79
/*
* !!! FIXME: maybe change this to always available, and move this to
* !!! FIXME: to device enumeration and opening?
*/
int fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
int available = 0;
80
81
82
83
84
if (fd >= 0) {
available = 1;
close(fd);
}
return (available);
85
86
87
}
88
89
static int
DSP_Init(SDL_AudioDriverImpl *impl)
90
{
91
/* Set the function pointers */
92
93
94
95
96
97
98
99
100
impl->DetectDevices = DSP_DetectDevices;
impl->GetDeviceName = DSP_GetDeviceName;
impl->OpenDevice = DSP_OpenDevice;
impl->WaitDevice = DSP_WaitDevice;
impl->PlayDevice = DSP_PlayDevice;
impl->GetDeviceBuf = DSP_GetDeviceBuf;
impl->CloseDevice = DSP_CloseDevice;
return 1;
101
102
}
103
104
AudioBootStrap DSP_bootstrap = {
105
DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
106
DSP_Available, DSP_Init
107
108
109
};
110
111
static int
DSP_DetectDevices(int iscapture)
112
{
113
return -1; /* !!! FIXME */
114
115
116
}
117
118
static const char *
DSP_GetDeviceName(int index, int iscapture)
119
{
120
121
SDL_SetError("No such device"); /* !!! FIXME */
return NULL;
122
123
}
124
125
static int
126
DSP_OpenDevice(_THIS, const char *devname, int iscapture)
127
{
128
char dev[1024];
129
130
131
int format;
int value;
int frag_spec;
132
SDL_AudioFormat test_format;
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* 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;
/* !!! FIXME: handle devname */
/* !!! FIXME: handle iscapture */
147
/* Open the audio device */
148
149
150
151
this->hidden->audio_fd = SDL_OpenAudioPath(dev, sizeof(dev), OPEN_FLAGS, 0);
if (this->hidden->audio_fd < 0) {
SDL_SetError("Couldn't open %s: %s", dev, strerror(errno));
return 0;
152
}
153
this->hidden->mixbuf = NULL;
154
155
156
157
/* Make the file descriptor use blocking writes with fcntl() */
{
long flags;
158
flags = fcntl(this->hidden->audio_fd, F_GETFL);
159
flags &= ~O_NONBLOCK;
160
if (fcntl(this->hidden->audio_fd, F_SETFL, flags) < 0) {
161
SDL_SetError("Couldn't set audio blocking mode");
162
163
DSP_CloseDevice(this);
return 0;
164
165
166
167
}
}
/* Get a list of supported hardware formats */
168
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
169
170
perror("SNDCTL_DSP_GETFMTS");
SDL_SetError("Couldn't get audio format list");
171
172
DSP_CloseDevice(this);
return 0;
173
174
175
176
}
/* Try for a closest match on audio format */
format = 0;
177
for (test_format = SDL_FirstAudioFormat(this->spec.format);
178
!format && test_format;) {
179
#ifdef DEBUG_AUDIO
180
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
181
#endif
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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;
198
199
200
201
202
#if 0
/*
* These formats are not used by any real life systems so they are not
* needed here.
*/
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
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;
218
#endif
219
220
221
222
223
224
225
226
227
228
default:
format = 0;
break;
}
if (!format) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
SDL_SetError("Couldn't find any hardware audio formats");
229
230
DSP_CloseDevice(this);
return 0;
231
}
232
this->spec.format = test_format;
233
234
235
/* Set the audio format */
value = format;
236
237
if ( (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
(value != format) ) {
238
239
perror("SNDCTL_DSP_SETFMT");
SDL_SetError("Couldn't set audio format");
240
241
DSP_CloseDevice(this);
return 0;
242
243
244
}
/* Set the number of channels of output */
245
246
value = this->spec.channels;
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
247
248
perror("SNDCTL_DSP_CHANNELS");
SDL_SetError("Cannot set the number of channels");
249
250
DSP_CloseDevice(this);
return 0;
251
}
252
this->spec.channels = value;
253
254
/* Set the DSP frequency */
255
256
value = this->spec.freq;
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
257
258
perror("SNDCTL_DSP_SPEED");
SDL_SetError("Couldn't set audio frequency");
259
260
DSP_CloseDevice(this);
return 0;
261
}
262
this->spec.freq = value;
263
264
/* Calculate the final parameters for this audio specification */
265
SDL_CalculateAudioSpec(&this->spec);
266
267
/* Determine the power of two of the fragment size */
268
269
for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
if ((0x01U << frag_spec) != this->spec.size) {
270
SDL_SetError("Fragment size must be a power of two");
271
272
DSP_CloseDevice(this);
return 0;
273
274
275
276
}
frag_spec |= 0x00020000; /* two fragments, for low latency */
/* Set the audio buffering parameters */
277
#ifdef DEBUG_AUDIO
278
279
fprintf(stderr, "Requesting %d fragments of size %d\n",
(frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
280
#endif
281
if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
282
283
perror("SNDCTL_DSP_SETFRAGMENT");
}
284
#ifdef DEBUG_AUDIO
285
286
{
audio_buf_info info;
287
ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
288
289
290
291
292
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);
}
293
#endif
294
295
/* Allocate mixing buffer */
296
297
298
299
300
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;
301
}
302
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
303
304
/* We're ready to rock and roll. :-) */
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
return 1;
}
/* This function waits until it is possible to write a full sound buffer */
static void
DSP_WaitDevice(_THIS)
{
/* Not needed at all since OSS handles waiting automagically */
}
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);
}
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;
}
352
}
353
354
/* vi: set ts=4 sw=4 expandtab: */