SDL  2.0
SDL_dspaudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_AUDIO_DRIVER_OSS
24 
25 /* Allow access to a raw mixing buffer */
26 
27 #include <stdio.h> /* For perror() */
28 #include <string.h> /* For strerror() */
29 #include <errno.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <signal.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 
37 #if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
38 /* This is installed on some systems */
39 #include <soundcard.h>
40 #else
41 /* This is recommended by OSS */
42 #include <sys/soundcard.h>
43 #endif
44 
45 #include "SDL_timer.h"
46 #include "SDL_audio.h"
47 #include "../SDL_audio_c.h"
48 #include "../SDL_audiodev_c.h"
49 #include "SDL_dspaudio.h"
50 
51 
52 static void
53 DSP_DetectDevices(void)
54 {
56 }
57 
58 
59 static void
60 DSP_CloseDevice(_THIS)
61 {
62  if (this->hidden->audio_fd >= 0) {
63  close(this->hidden->audio_fd);
64  }
65  SDL_free(this->hidden->mixbuf);
66  SDL_free(this->hidden);
67 }
68 
69 
70 static int
71 DSP_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
72 {
73  const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
74  int format;
75  int value;
76  int frag_spec;
77  SDL_AudioFormat test_format;
78 
79  /* We don't care what the devname is...we'll try to open anything. */
80  /* ...but default to first name in the list... */
81  if (devname == NULL) {
82  devname = SDL_GetAudioDeviceName(0, iscapture);
83  if (devname == NULL) {
84  return SDL_SetError("No such audio device");
85  }
86  }
87 
88  /* Make sure fragment size stays a power of 2, or OSS fails. */
89  /* I don't know which of these are actually legal values, though... */
90  if (this->spec.channels > 8)
91  this->spec.channels = 8;
92  else if (this->spec.channels > 4)
93  this->spec.channels = 4;
94  else if (this->spec.channels > 2)
95  this->spec.channels = 2;
96 
97  /* Initialize all variables that we clean on shutdown */
98  this->hidden = (struct SDL_PrivateAudioData *)
99  SDL_malloc((sizeof *this->hidden));
100  if (this->hidden == NULL) {
101  return SDL_OutOfMemory();
102  }
103  SDL_zerop(this->hidden);
104 
105  /* Open the audio device */
106  this->hidden->audio_fd = open(devname, flags, 0);
107  if (this->hidden->audio_fd < 0) {
108  return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
109  }
110 
111  /* Make the file descriptor use blocking i/o with fcntl() */
112  {
113  long ctlflags;
114  ctlflags = fcntl(this->hidden->audio_fd, F_GETFL);
115  ctlflags &= ~O_NONBLOCK;
116  if (fcntl(this->hidden->audio_fd, F_SETFL, ctlflags) < 0) {
117  return SDL_SetError("Couldn't set audio blocking mode");
118  }
119  }
120 
121  /* Get a list of supported hardware formats */
122  if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) {
123  perror("SNDCTL_DSP_GETFMTS");
124  return SDL_SetError("Couldn't get audio format list");
125  }
126 
127  /* Try for a closest match on audio format */
128  format = 0;
129  for (test_format = SDL_FirstAudioFormat(this->spec.format);
130  !format && test_format;) {
131 #ifdef DEBUG_AUDIO
132  fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
133 #endif
134  switch (test_format) {
135  case AUDIO_U8:
136  if (value & AFMT_U8) {
137  format = AFMT_U8;
138  }
139  break;
140  case AUDIO_S16LSB:
141  if (value & AFMT_S16_LE) {
142  format = AFMT_S16_LE;
143  }
144  break;
145  case AUDIO_S16MSB:
146  if (value & AFMT_S16_BE) {
147  format = AFMT_S16_BE;
148  }
149  break;
150 #if 0
151 /*
152  * These formats are not used by any real life systems so they are not
153  * needed here.
154  */
155  case AUDIO_S8:
156  if (value & AFMT_S8) {
157  format = AFMT_S8;
158  }
159  break;
160  case AUDIO_U16LSB:
161  if (value & AFMT_U16_LE) {
162  format = AFMT_U16_LE;
163  }
164  break;
165  case AUDIO_U16MSB:
166  if (value & AFMT_U16_BE) {
167  format = AFMT_U16_BE;
168  }
169  break;
170 #endif
171  default:
172  format = 0;
173  break;
174  }
175  if (!format) {
176  test_format = SDL_NextAudioFormat();
177  }
178  }
179  if (format == 0) {
180  return SDL_SetError("Couldn't find any hardware audio formats");
181  }
182  this->spec.format = test_format;
183 
184  /* Set the audio format */
185  value = format;
186  if ((ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
187  (value != format)) {
188  perror("SNDCTL_DSP_SETFMT");
189  return SDL_SetError("Couldn't set audio format");
190  }
191 
192  /* Set the number of channels of output */
193  value = this->spec.channels;
194  if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) {
195  perror("SNDCTL_DSP_CHANNELS");
196  return SDL_SetError("Cannot set the number of channels");
197  }
198  this->spec.channels = value;
199 
200  /* Set the DSP frequency */
201  value = this->spec.freq;
202  if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) {
203  perror("SNDCTL_DSP_SPEED");
204  return SDL_SetError("Couldn't set audio frequency");
205  }
206  this->spec.freq = value;
207 
208  /* Calculate the final parameters for this audio specification */
210 
211  /* Determine the power of two of the fragment size */
212  for (frag_spec = 0; (0x01U << frag_spec) < this->spec.size; ++frag_spec);
213  if ((0x01U << frag_spec) != this->spec.size) {
214  return SDL_SetError("Fragment size must be a power of two");
215  }
216  frag_spec |= 0x00020000; /* two fragments, for low latency */
217 
218  /* Set the audio buffering parameters */
219 #ifdef DEBUG_AUDIO
220  fprintf(stderr, "Requesting %d fragments of size %d\n",
221  (frag_spec >> 16), 1 << (frag_spec & 0xFFFF));
222 #endif
223  if (ioctl(this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) {
224  perror("SNDCTL_DSP_SETFRAGMENT");
225  }
226 #ifdef DEBUG_AUDIO
227  {
228  audio_buf_info info;
229  ioctl(this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info);
230  fprintf(stderr, "fragments = %d\n", info.fragments);
231  fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
232  fprintf(stderr, "fragsize = %d\n", info.fragsize);
233  fprintf(stderr, "bytes = %d\n", info.bytes);
234  }
235 #endif
236 
237  /* Allocate mixing buffer */
238  if (!iscapture) {
239  this->hidden->mixlen = this->spec.size;
240  this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
241  if (this->hidden->mixbuf == NULL) {
242  return SDL_OutOfMemory();
243  }
244  SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
245  }
246 
247  /* We're ready to rock and roll. :-) */
248  return 0;
249 }
250 
251 
252 static void
253 DSP_PlayDevice(_THIS)
254 {
255  struct SDL_PrivateAudioData *h = this->hidden;
256  if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) {
257  perror("Audio write");
259  }
260 #ifdef DEBUG_AUDIO
261  fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen);
262 #endif
263 }
264 
265 static Uint8 *
266 DSP_GetDeviceBuf(_THIS)
267 {
268  return (this->hidden->mixbuf);
269 }
270 
271 static int
272 DSP_CaptureFromDevice(_THIS, void *buffer, int buflen)
273 {
274  return (int) read(this->hidden->audio_fd, buffer, buflen);
275 }
276 
277 static void
278 DSP_FlushCapture(_THIS)
279 {
280  struct SDL_PrivateAudioData *h = this->hidden;
281  audio_buf_info info;
282  if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) {
283  while (info.bytes > 0) {
284  char buf[512];
285  const size_t len = SDL_min(sizeof (buf), info.bytes);
286  const ssize_t br = read(h->audio_fd, buf, len);
287  if (br <= 0) {
288  break;
289  }
290  info.bytes -= br;
291  }
292  }
293 }
294 
295 static int
296 DSP_Init(SDL_AudioDriverImpl * impl)
297 {
298  /* Set the function pointers */
299  impl->DetectDevices = DSP_DetectDevices;
300  impl->OpenDevice = DSP_OpenDevice;
301  impl->PlayDevice = DSP_PlayDevice;
302  impl->GetDeviceBuf = DSP_GetDeviceBuf;
303  impl->CloseDevice = DSP_CloseDevice;
304  impl->CaptureFromDevice = DSP_CaptureFromDevice;
305  impl->FlushCapture = DSP_FlushCapture;
306 
307  impl->AllowsArbitraryDeviceNames = 1;
308  impl->HasCaptureSupport = SDL_TRUE;
309 
310  return 1; /* this audio target is available. */
311 }
312 
313 
315  "dsp", "OSS /dev/dsp standard audio", DSP_Init, 0
316 };
317 
318 #endif /* SDL_AUDIO_DRIVER_OSS */
319 
320 /* vi: set ts=4 sw=4 expandtab: */
AudioBootStrap DSP_bootstrap
#define OPEN_FLAGS_INPUT
#define SDL_min(x, y)
Definition: SDL_stdinc.h:406
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1584
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:67
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
GLfloat GLfloat GLfloat GLfloat h
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:449
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
GLenum GLsizei len
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1596
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1572
SDL_AudioSpec spec
Definition: loopwave.c:31
#define SDL_GetAudioDeviceName
#define AUDIO_U8
Definition: SDL_audio.h:89
void SDL_EnumUnixAudioDevices(const int classic, int(*test)(int))
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
Uint8 channels
Definition: SDL_audio.h:181
#define OPEN_FLAGS_OUTPUT
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
GLsizei const GLfloat * value
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1605
GLenum GLuint GLenum GLsizei const GLchar * buf
Uint32 size
Definition: SDL_audio.h:185
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
GLuint buffer
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:76
#define SDL_SetError
GLbitfield flags
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
SDL_AudioFormat format
Definition: SDL_audio.h:180
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:77
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:75
#define SDL_malloc
#define AUDIO_S8
Definition: SDL_audio.h:90
#define SDL_memset
#define AUDIO_U16MSB
Definition: SDL_audio.h:93