1/* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997-2012 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 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 19 Sam Lantinga 20 slouken@libsdl.org 21 22 Modified in Oct 2004 by Hannu Savolainen 23 hannu@opensound.com 24*/ 25#include "SDL_config.h" 26 27/* Allow access to a raw mixing buffer */ 28 29#include <stdio.h> /* For perror() */ 30#include <string.h> /* For strerror() */ 31#include <errno.h> 32#include <unistd.h> 33#include <fcntl.h> 34#include <signal.h> 35#include <sys/time.h> 36#include <sys/ioctl.h> 37#include <sys/stat.h> 38 39#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H 40/* This is installed on some systems */ 41#include <soundcard.h> 42#else 43/* This is recommended by OSS */ 44#include <sys/soundcard.h> 45#endif 46 47#include "SDL_timer.h" 48#include "SDL_audio.h" 49#include "../SDL_audiomem.h" 50#include "../SDL_audio_c.h" 51#include "../SDL_audiodev_c.h" 52#include "SDL_dspaudio.h" 53 54/* The tag name used by DSP audio */ 55#define DSP_DRIVER_NAME "dsp" 56 57/* Open the audio device for playback, and don't block if busy */ 58#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) 59 60/* Audio driver functions */ 61static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); 62static void DSP_WaitAudio(_THIS); 63static void DSP_PlayAudio(_THIS); 64static Uint8 *DSP_GetAudioBuf(_THIS); 65static void DSP_CloseAudio(_THIS); 66 67/* Audio driver bootstrap functions */ 68 69static int Audio_Available(void) 70{ 71 int fd; 72 int available; 73 74 available = 0; 75 fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); 76 if ( fd >= 0 ) { 77 available = 1; 78 close(fd); 79 } 80 return(available); 81} 82 83static void Audio_DeleteDevice(SDL_AudioDevice *device) 84{ 85 SDL_free(device->hidden); 86 SDL_free(device); 87} 88 89static SDL_AudioDevice *Audio_CreateDevice(int devindex) 90{ 91 SDL_AudioDevice *this; 92 93 /* Initialize all variables that we clean on shutdown */ 94 this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); 95 if ( this ) { 96 SDL_memset(this, 0, (sizeof *this)); 97 this->hidden = (struct SDL_PrivateAudioData *) 98 SDL_malloc((sizeof *this->hidden)); 99 } 100 if ( (this == NULL) || (this->hidden == NULL) ) { 101 SDL_OutOfMemory(); 102 if ( this ) { 103 SDL_free(this); 104 } 105 return(0); 106 } 107 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 108 audio_fd = -1; 109 110 /* Set the function pointers */ 111 this->OpenAudio = DSP_OpenAudio; 112 this->WaitAudio = DSP_WaitAudio; 113 this->PlayAudio = DSP_PlayAudio; 114 this->GetAudioBuf = DSP_GetAudioBuf; 115 this->CloseAudio = DSP_CloseAudio; 116 117 this->free = Audio_DeleteDevice; 118 119 return this; 120} 121 122AudioBootStrap DSP_bootstrap = { 123 DSP_DRIVER_NAME, "OSS /dev/dsp standard audio", 124 Audio_Available, Audio_CreateDevice 125}; 126 127/* This function waits until it is possible to write a full sound buffer */ 128static void DSP_WaitAudio(_THIS) 129{ 130 /* Not needed at all since OSS handles waiting automagically */ 131} 132 133static void DSP_PlayAudio(_THIS) 134{ 135 if (write(audio_fd, mixbuf, mixlen)==-1) 136 { 137 perror("Audio write"); 138 this->enabled = 0; 139 } 140 141#ifdef DEBUG_AUDIO 142 fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen); 143#endif 144} 145 146static Uint8 *DSP_GetAudioBuf(_THIS) 147{ 148 return(mixbuf); 149} 150 151static void DSP_CloseAudio(_THIS) 152{ 153 if ( mixbuf != NULL ) { 154 SDL_FreeAudioMem(mixbuf); 155 mixbuf = NULL; 156 } 157 if ( audio_fd >= 0 ) { 158 close(audio_fd); 159 audio_fd = -1; 160 } 161} 162 163static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec) 164{ 165 char audiodev[1024]; 166 int format; 167 int value; 168 int frag_spec; 169 Uint16 test_format; 170 171 /* Make sure fragment size stays a power of 2, or OSS fails. */ 172 /* I don't know which of these are actually legal values, though... */ 173 if (spec->channels > 8) 174 spec->channels = 8; 175 else if (spec->channels > 4) 176 spec->channels = 4; 177 else if (spec->channels > 2) 178 spec->channels = 2; 179 180 /* Open the audio device */ 181 audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); 182 if ( audio_fd < 0 ) { 183 SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); 184 return(-1); 185 } 186 mixbuf = NULL; 187 188 /* Make the file descriptor use blocking writes with fcntl() */ 189 { long flags; 190 flags = fcntl(audio_fd, F_GETFL); 191 flags &= ~O_NONBLOCK; 192 if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) { 193 SDL_SetError("Couldn't set audio blocking mode"); 194 DSP_CloseAudio(this); 195 return(-1); 196 } 197 } 198 199 /* Get a list of supported hardware formats */ 200 if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) { 201 perror("SNDCTL_DSP_GETFMTS"); 202 SDL_SetError("Couldn't get audio format list"); 203 DSP_CloseAudio(this); 204 return(-1); 205 } 206 207 /* Try for a closest match on audio format */ 208 format = 0; 209 for ( test_format = SDL_FirstAudioFormat(spec->format); 210 ! format && test_format; ) { 211#ifdef DEBUG_AUDIO 212 fprintf(stderr, "Trying format 0x%4.4x\n", test_format); 213#endif 214 switch ( test_format ) { 215 case AUDIO_U8: 216 if ( value & AFMT_U8 ) { 217 format = AFMT_U8; 218 } 219 break; 220 case AUDIO_S16LSB: 221 if ( value & AFMT_S16_LE ) { 222 format = AFMT_S16_LE; 223 } 224 break; 225 case AUDIO_S16MSB: 226 if ( value & AFMT_S16_BE ) { 227 format = AFMT_S16_BE; 228 } 229 break; 230#if 0 231/* 232 * These formats are not used by any real life systems so they are not 233 * needed here. 234 */ 235 case AUDIO_S8: 236 if ( value & AFMT_S8 ) { 237 format = AFMT_S8; 238 } 239 break; 240 case AUDIO_U16LSB: 241 if ( value & AFMT_U16_LE ) { 242 format = AFMT_U16_LE; 243 } 244 break; 245 case AUDIO_U16MSB: 246 if ( value & AFMT_U16_BE ) { 247 format = AFMT_U16_BE; 248 } 249 break; 250#endif 251 default: 252 format = 0; 253 break; 254 } 255 if ( ! format ) { 256 test_format = SDL_NextAudioFormat(); 257 } 258 } 259 if ( format == 0 ) { 260 SDL_SetError("Couldn't find any hardware audio formats"); 261 DSP_CloseAudio(this); 262 return(-1); 263 } 264 spec->format = test_format; 265 266 /* Set the audio format */ 267 value = format; 268 if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || 269 (value != format) ) { 270 perror("SNDCTL_DSP_SETFMT"); 271 SDL_SetError("Couldn't set audio format"); 272 DSP_CloseAudio(this); 273 return(-1); 274 } 275 276 /* Set the number of channels of output */ 277 value = spec->channels; 278 if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) { 279 perror("SNDCTL_DSP_CHANNELS"); 280 SDL_SetError("Cannot set the number of channels"); 281 DSP_CloseAudio(this); 282 return(-1); 283 } 284 spec->channels = value; 285 286 /* Set the DSP frequency */ 287 value = spec->freq; 288 if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) { 289 perror("SNDCTL_DSP_SPEED"); 290 SDL_SetError("Couldn't set audio frequency"); 291 DSP_CloseAudio(this); 292 return(-1); 293 } 294 spec->freq = value; 295 296 /* Calculate the final parameters for this audio specification */ 297 SDL_CalculateAudioSpec(spec); 298 299 /* Determine the power of two of the fragment size */ 300 for ( frag_spec = 0; (0x01U<<frag_spec) < spec->size; ++frag_spec ); 301 if ( (0x01U<<frag_spec) != spec->size ) { 302 SDL_SetError("Fragment size must be a power of two"); 303 DSP_CloseAudio(this); 304 return(-1); 305 } 306 frag_spec |= 0x00020000; /* two fragments, for low latency */ 307 308 /* Set the audio buffering parameters */ 309#ifdef DEBUG_AUDIO 310 fprintf(stderr, "Requesting %d fragments of size %d\n", 311 (frag_spec >> 16), 1<<(frag_spec&0xFFFF)); 312#endif 313 if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) { 314 perror("SNDCTL_DSP_SETFRAGMENT"); 315 } 316#ifdef DEBUG_AUDIO 317 { audio_buf_info info; 318 ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info); 319 fprintf(stderr, "fragments = %d\n", info.fragments); 320 fprintf(stderr, "fragstotal = %d\n", info.fragstotal); 321 fprintf(stderr, "fragsize = %d\n", info.fragsize); 322 fprintf(stderr, "bytes = %d\n", info.bytes); 323 } 324#endif 325 326 /* Allocate mixing buffer */ 327 mixlen = spec->size; 328 mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); 329 if ( mixbuf == NULL ) { 330 DSP_CloseAudio(this); 331 return(-1); 332 } 333 SDL_memset(mixbuf, spec->silence, spec->size); 334 335 /* Get the parent process id (we're the parent of the audio thread) */ 336 parent = getpid(); 337 338 /* We're ready to rock and roll. :-) */ 339 return(0); 340} 341