annotate Alc/backends/oss.c @ 0:f9476ff7637e

initial forking of open-al to create multiple listeners
author Robert McIntyre <rlm@mit.edu>
date Tue, 25 Oct 2011 13:02:31 -0700
parents
children
rev   line source
rlm@0 1 /**
rlm@0 2 * OpenAL cross platform audio library
rlm@0 3 * Copyright (C) 1999-2007 by authors.
rlm@0 4 * This library is free software; you can redistribute it and/or
rlm@0 5 * modify it under the terms of the GNU Library General Public
rlm@0 6 * License as published by the Free Software Foundation; either
rlm@0 7 * version 2 of the License, or (at your option) any later version.
rlm@0 8 *
rlm@0 9 * This library is distributed in the hope that it will be useful,
rlm@0 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rlm@0 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
rlm@0 12 * Library General Public License for more details.
rlm@0 13 *
rlm@0 14 * You should have received a copy of the GNU Library General Public
rlm@0 15 * License along with this library; if not, write to the
rlm@0 16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
rlm@0 17 * Boston, MA 02111-1307, USA.
rlm@0 18 * Or go to http://www.gnu.org/copyleft/lgpl.html
rlm@0 19 */
rlm@0 20
rlm@0 21 #include "config.h"
rlm@0 22
rlm@0 23 #include <sys/ioctl.h>
rlm@0 24 #include <sys/types.h>
rlm@0 25 #include <sys/stat.h>
rlm@0 26 #include <fcntl.h>
rlm@0 27 #include <stdlib.h>
rlm@0 28 #include <stdio.h>
rlm@0 29 #include <memory.h>
rlm@0 30 #include <unistd.h>
rlm@0 31 #include <errno.h>
rlm@0 32 #include <math.h>
rlm@0 33 #include "alMain.h"
rlm@0 34 #include "AL/al.h"
rlm@0 35 #include "AL/alc.h"
rlm@0 36
rlm@0 37 #include <sys/soundcard.h>
rlm@0 38
rlm@0 39 /*
rlm@0 40 * The OSS documentation talks about SOUND_MIXER_READ, but the header
rlm@0 41 * only contains MIXER_READ. Play safe. Same for WRITE.
rlm@0 42 */
rlm@0 43 #ifndef SOUND_MIXER_READ
rlm@0 44 #define SOUND_MIXER_READ MIXER_READ
rlm@0 45 #endif
rlm@0 46 #ifndef SOUND_MIXER_WRITE
rlm@0 47 #define SOUND_MIXER_WRITE MIXER_WRITE
rlm@0 48 #endif
rlm@0 49
rlm@0 50 static const ALCchar oss_device[] = "OSS Default";
rlm@0 51
rlm@0 52 typedef struct {
rlm@0 53 int fd;
rlm@0 54 volatile int killNow;
rlm@0 55 ALvoid *thread;
rlm@0 56
rlm@0 57 ALubyte *mix_data;
rlm@0 58 int data_size;
rlm@0 59
rlm@0 60 RingBuffer *ring;
rlm@0 61 int doCapture;
rlm@0 62 } oss_data;
rlm@0 63
rlm@0 64
rlm@0 65 static int log2i(ALCuint x)
rlm@0 66 {
rlm@0 67 int y = 0;
rlm@0 68 while (x > 1)
rlm@0 69 {
rlm@0 70 x >>= 1;
rlm@0 71 y++;
rlm@0 72 }
rlm@0 73 return y;
rlm@0 74 }
rlm@0 75
rlm@0 76
rlm@0 77 static ALuint OSSProc(ALvoid *ptr)
rlm@0 78 {
rlm@0 79 ALCdevice *pDevice = (ALCdevice*)ptr;
rlm@0 80 oss_data *data = (oss_data*)pDevice->ExtraData;
rlm@0 81 ALint frameSize;
rlm@0 82 ssize_t wrote;
rlm@0 83
rlm@0 84 SetRTPriority();
rlm@0 85
rlm@0 86 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
rlm@0 87
rlm@0 88 while(!data->killNow && pDevice->Connected)
rlm@0 89 {
rlm@0 90 ALint len = data->data_size;
rlm@0 91 ALubyte *WritePtr = data->mix_data;
rlm@0 92
rlm@0 93 aluMixData(pDevice, WritePtr, len/frameSize);
rlm@0 94 while(len > 0 && !data->killNow)
rlm@0 95 {
rlm@0 96 wrote = write(data->fd, WritePtr, len);
rlm@0 97 if(wrote < 0)
rlm@0 98 {
rlm@0 99 if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
rlm@0 100 {
rlm@0 101 ERR("write failed: %s\n", strerror(errno));
rlm@0 102 aluHandleDisconnect(pDevice);
rlm@0 103 break;
rlm@0 104 }
rlm@0 105
rlm@0 106 Sleep(1);
rlm@0 107 continue;
rlm@0 108 }
rlm@0 109
rlm@0 110 len -= wrote;
rlm@0 111 WritePtr += wrote;
rlm@0 112 }
rlm@0 113 }
rlm@0 114
rlm@0 115 return 0;
rlm@0 116 }
rlm@0 117
rlm@0 118 static ALuint OSSCaptureProc(ALvoid *ptr)
rlm@0 119 {
rlm@0 120 ALCdevice *pDevice = (ALCdevice*)ptr;
rlm@0 121 oss_data *data = (oss_data*)pDevice->ExtraData;
rlm@0 122 int frameSize;
rlm@0 123 int amt;
rlm@0 124
rlm@0 125 SetRTPriority();
rlm@0 126
rlm@0 127 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
rlm@0 128
rlm@0 129 while(!data->killNow)
rlm@0 130 {
rlm@0 131 amt = read(data->fd, data->mix_data, data->data_size);
rlm@0 132 if(amt < 0)
rlm@0 133 {
rlm@0 134 ERR("read failed: %s\n", strerror(errno));
rlm@0 135 aluHandleDisconnect(pDevice);
rlm@0 136 break;
rlm@0 137 }
rlm@0 138 if(amt == 0)
rlm@0 139 {
rlm@0 140 Sleep(1);
rlm@0 141 continue;
rlm@0 142 }
rlm@0 143 if(data->doCapture)
rlm@0 144 WriteRingBuffer(data->ring, data->mix_data, amt/frameSize);
rlm@0 145 }
rlm@0 146
rlm@0 147 return 0;
rlm@0 148 }
rlm@0 149
rlm@0 150 static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName)
rlm@0 151 {
rlm@0 152 char driver[64];
rlm@0 153 oss_data *data;
rlm@0 154
rlm@0 155 strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1);
rlm@0 156 driver[sizeof(driver)-1] = 0;
rlm@0 157 if(!deviceName)
rlm@0 158 deviceName = oss_device;
rlm@0 159 else if(strcmp(deviceName, oss_device) != 0)
rlm@0 160 return ALC_FALSE;
rlm@0 161
rlm@0 162 data = (oss_data*)calloc(1, sizeof(oss_data));
rlm@0 163 data->killNow = 0;
rlm@0 164
rlm@0 165 data->fd = open(driver, O_WRONLY);
rlm@0 166 if(data->fd == -1)
rlm@0 167 {
rlm@0 168 free(data);
rlm@0 169 ERR("Could not open %s: %s\n", driver, strerror(errno));
rlm@0 170 return ALC_FALSE;
rlm@0 171 }
rlm@0 172
rlm@0 173 device->szDeviceName = strdup(deviceName);
rlm@0 174 device->ExtraData = data;
rlm@0 175 return ALC_TRUE;
rlm@0 176 }
rlm@0 177
rlm@0 178 static void oss_close_playback(ALCdevice *device)
rlm@0 179 {
rlm@0 180 oss_data *data = (oss_data*)device->ExtraData;
rlm@0 181
rlm@0 182 close(data->fd);
rlm@0 183 free(data);
rlm@0 184 device->ExtraData = NULL;
rlm@0 185 }
rlm@0 186
rlm@0 187 static ALCboolean oss_reset_playback(ALCdevice *device)
rlm@0 188 {
rlm@0 189 oss_data *data = (oss_data*)device->ExtraData;
rlm@0 190 int numFragmentsLogSize;
rlm@0 191 int log2FragmentSize;
rlm@0 192 unsigned int periods;
rlm@0 193 audio_buf_info info;
rlm@0 194 ALuint frameSize;
rlm@0 195 int numChannels;
rlm@0 196 int ossFormat;
rlm@0 197 int ossSpeed;
rlm@0 198 char *err;
rlm@0 199
rlm@0 200 switch(device->FmtType)
rlm@0 201 {
rlm@0 202 case DevFmtByte:
rlm@0 203 ossFormat = AFMT_S8;
rlm@0 204 break;
rlm@0 205 case DevFmtUByte:
rlm@0 206 ossFormat = AFMT_U8;
rlm@0 207 break;
rlm@0 208 case DevFmtUShort:
rlm@0 209 case DevFmtFloat:
rlm@0 210 device->FmtType = DevFmtShort;
rlm@0 211 /* fall-through */
rlm@0 212 case DevFmtShort:
rlm@0 213 ossFormat = AFMT_S16_NE;
rlm@0 214 break;
rlm@0 215 }
rlm@0 216
rlm@0 217 periods = device->NumUpdates;
rlm@0 218 numChannels = ChannelsFromDevFmt(device->FmtChans);
rlm@0 219 frameSize = numChannels * BytesFromDevFmt(device->FmtType);
rlm@0 220
rlm@0 221 ossSpeed = device->Frequency;
rlm@0 222 log2FragmentSize = log2i(device->UpdateSize * frameSize);
rlm@0 223
rlm@0 224 /* according to the OSS spec, 16 bytes are the minimum */
rlm@0 225 if (log2FragmentSize < 4)
rlm@0 226 log2FragmentSize = 4;
rlm@0 227 /* Subtract one period since the temp mixing buffer counts as one. Still
rlm@0 228 * need at least two on the card, though. */
rlm@0 229 if(periods > 2) periods--;
rlm@0 230 numFragmentsLogSize = (periods << 16) | log2FragmentSize;
rlm@0 231
rlm@0 232 #define CHECKERR(func) if((func) < 0) { \
rlm@0 233 err = #func; \
rlm@0 234 goto err; \
rlm@0 235 }
rlm@0 236 /* Don't fail if SETFRAGMENT fails. We can handle just about anything
rlm@0 237 * that's reported back via GETOSPACE */
rlm@0 238 ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize);
rlm@0 239 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat));
rlm@0 240 CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels));
rlm@0 241 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed));
rlm@0 242 CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info));
rlm@0 243 if(0)
rlm@0 244 {
rlm@0 245 err:
rlm@0 246 ERR("%s failed: %s\n", err, strerror(errno));
rlm@0 247 return ALC_FALSE;
rlm@0 248 }
rlm@0 249 #undef CHECKERR
rlm@0 250
rlm@0 251 if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
rlm@0 252 {
rlm@0 253 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
rlm@0 254 return ALC_FALSE;
rlm@0 255 }
rlm@0 256
rlm@0 257 if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
rlm@0 258 (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
rlm@0 259 (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
rlm@0 260 {
rlm@0 261 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
rlm@0 262 return ALC_FALSE;
rlm@0 263 }
rlm@0 264
rlm@0 265 if(device->Frequency != (ALuint)ossSpeed)
rlm@0 266 {
rlm@0 267 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 268 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, ossSpeed);
rlm@0 269 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
rlm@0 270 device->Frequency = ossSpeed;
rlm@0 271 }
rlm@0 272 device->UpdateSize = info.fragsize / frameSize;
rlm@0 273 device->NumUpdates = info.fragments + 1;
rlm@0 274
rlm@0 275 data->data_size = device->UpdateSize * frameSize;
rlm@0 276 data->mix_data = calloc(1, data->data_size);
rlm@0 277
rlm@0 278 SetDefaultChannelOrder(device);
rlm@0 279
rlm@0 280 data->thread = StartThread(OSSProc, device);
rlm@0 281 if(data->thread == NULL)
rlm@0 282 {
rlm@0 283 free(data->mix_data);
rlm@0 284 data->mix_data = NULL;
rlm@0 285 return ALC_FALSE;
rlm@0 286 }
rlm@0 287
rlm@0 288 return ALC_TRUE;
rlm@0 289 }
rlm@0 290
rlm@0 291 static void oss_stop_playback(ALCdevice *device)
rlm@0 292 {
rlm@0 293 oss_data *data = (oss_data*)device->ExtraData;
rlm@0 294
rlm@0 295 if(!data->thread)
rlm@0 296 return;
rlm@0 297
rlm@0 298 data->killNow = 1;
rlm@0 299 StopThread(data->thread);
rlm@0 300 data->thread = NULL;
rlm@0 301
rlm@0 302 data->killNow = 0;
rlm@0 303 if(ioctl(data->fd, SNDCTL_DSP_RESET) != 0)
rlm@0 304 ERR("Error resetting device: %s\n", strerror(errno));
rlm@0 305
rlm@0 306 free(data->mix_data);
rlm@0 307 data->mix_data = NULL;
rlm@0 308 }
rlm@0 309
rlm@0 310
rlm@0 311 static ALCboolean oss_open_capture(ALCdevice *device, const ALCchar *deviceName)
rlm@0 312 {
rlm@0 313 int numFragmentsLogSize;
rlm@0 314 int log2FragmentSize;
rlm@0 315 unsigned int periods;
rlm@0 316 audio_buf_info info;
rlm@0 317 ALuint frameSize;
rlm@0 318 int numChannels;
rlm@0 319 char driver[64];
rlm@0 320 oss_data *data;
rlm@0 321 int ossFormat;
rlm@0 322 int ossSpeed;
rlm@0 323 char *err;
rlm@0 324
rlm@0 325 strncpy(driver, GetConfigValue("oss", "capture", "/dev/dsp"), sizeof(driver)-1);
rlm@0 326 driver[sizeof(driver)-1] = 0;
rlm@0 327 if(!deviceName)
rlm@0 328 deviceName = oss_device;
rlm@0 329 else if(strcmp(deviceName, oss_device) != 0)
rlm@0 330 return ALC_FALSE;
rlm@0 331
rlm@0 332 data = (oss_data*)calloc(1, sizeof(oss_data));
rlm@0 333 data->killNow = 0;
rlm@0 334
rlm@0 335 data->fd = open(driver, O_RDONLY);
rlm@0 336 if(data->fd == -1)
rlm@0 337 {
rlm@0 338 free(data);
rlm@0 339 ERR("Could not open %s: %s\n", driver, strerror(errno));
rlm@0 340 return ALC_FALSE;
rlm@0 341 }
rlm@0 342
rlm@0 343 switch(device->FmtType)
rlm@0 344 {
rlm@0 345 case DevFmtByte:
rlm@0 346 ossFormat = AFMT_S8;
rlm@0 347 break;
rlm@0 348 case DevFmtUByte:
rlm@0 349 ossFormat = AFMT_U8;
rlm@0 350 break;
rlm@0 351 case DevFmtShort:
rlm@0 352 ossFormat = AFMT_S16_NE;
rlm@0 353 break;
rlm@0 354 case DevFmtUShort:
rlm@0 355 case DevFmtFloat:
rlm@0 356 free(data);
rlm@0 357 ERR("%s capture samples not supported on OSS\n", DevFmtTypeString(device->FmtType));
rlm@0 358 return ALC_FALSE;
rlm@0 359 }
rlm@0 360
rlm@0 361 periods = 4;
rlm@0 362 numChannels = ChannelsFromDevFmt(device->FmtChans);
rlm@0 363 frameSize = numChannels * BytesFromDevFmt(device->FmtType);
rlm@0 364 ossSpeed = device->Frequency;
rlm@0 365 log2FragmentSize = log2i(device->UpdateSize * device->NumUpdates *
rlm@0 366 frameSize / periods);
rlm@0 367
rlm@0 368 /* according to the OSS spec, 16 bytes are the minimum */
rlm@0 369 if (log2FragmentSize < 4)
rlm@0 370 log2FragmentSize = 4;
rlm@0 371 numFragmentsLogSize = (periods << 16) | log2FragmentSize;
rlm@0 372
rlm@0 373 #define CHECKERR(func) if((func) < 0) { \
rlm@0 374 err = #func; \
rlm@0 375 goto err; \
rlm@0 376 }
rlm@0 377 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFRAGMENT, &numFragmentsLogSize));
rlm@0 378 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SETFMT, &ossFormat));
rlm@0 379 CHECKERR(ioctl(data->fd, SNDCTL_DSP_CHANNELS, &numChannels));
rlm@0 380 CHECKERR(ioctl(data->fd, SNDCTL_DSP_SPEED, &ossSpeed));
rlm@0 381 CHECKERR(ioctl(data->fd, SNDCTL_DSP_GETISPACE, &info));
rlm@0 382 if(0)
rlm@0 383 {
rlm@0 384 err:
rlm@0 385 ERR("%s failed: %s\n", err, strerror(errno));
rlm@0 386 close(data->fd);
rlm@0 387 free(data);
rlm@0 388 return ALC_FALSE;
rlm@0 389 }
rlm@0 390 #undef CHECKERR
rlm@0 391
rlm@0 392 if((int)ChannelsFromDevFmt(device->FmtChans) != numChannels)
rlm@0 393 {
rlm@0 394 ERR("Failed to set %s, got %d channels instead\n", DevFmtChannelsString(device->FmtChans), numChannels);
rlm@0 395 close(data->fd);
rlm@0 396 free(data);
rlm@0 397 return ALC_FALSE;
rlm@0 398 }
rlm@0 399
rlm@0 400 if(!((ossFormat == AFMT_S8 && device->FmtType == DevFmtByte) ||
rlm@0 401 (ossFormat == AFMT_U8 && device->FmtType == DevFmtUByte) ||
rlm@0 402 (ossFormat == AFMT_S16_NE && device->FmtType == DevFmtShort)))
rlm@0 403 {
rlm@0 404 ERR("Failed to set %s samples, got OSS format %#x\n", DevFmtTypeString(device->FmtType), ossFormat);
rlm@0 405 close(data->fd);
rlm@0 406 free(data);
rlm@0 407 return ALC_FALSE;
rlm@0 408 }
rlm@0 409
rlm@0 410 data->ring = CreateRingBuffer(frameSize, device->UpdateSize * device->NumUpdates);
rlm@0 411 if(!data->ring)
rlm@0 412 {
rlm@0 413 ERR("Ring buffer create failed\n");
rlm@0 414 close(data->fd);
rlm@0 415 free(data);
rlm@0 416 return ALC_FALSE;
rlm@0 417 }
rlm@0 418
rlm@0 419 data->data_size = info.fragsize;
rlm@0 420 data->mix_data = calloc(1, data->data_size);
rlm@0 421
rlm@0 422 device->ExtraData = data;
rlm@0 423 data->thread = StartThread(OSSCaptureProc, device);
rlm@0 424 if(data->thread == NULL)
rlm@0 425 {
rlm@0 426 device->ExtraData = NULL;
rlm@0 427 free(data->mix_data);
rlm@0 428 free(data);
rlm@0 429 return ALC_FALSE;
rlm@0 430 }
rlm@0 431
rlm@0 432 device->szDeviceName = strdup(deviceName);
rlm@0 433 return ALC_TRUE;
rlm@0 434 }
rlm@0 435
rlm@0 436 static void oss_close_capture(ALCdevice *device)
rlm@0 437 {
rlm@0 438 oss_data *data = (oss_data*)device->ExtraData;
rlm@0 439 data->killNow = 1;
rlm@0 440 StopThread(data->thread);
rlm@0 441
rlm@0 442 close(data->fd);
rlm@0 443
rlm@0 444 DestroyRingBuffer(data->ring);
rlm@0 445
rlm@0 446 free(data->mix_data);
rlm@0 447 free(data);
rlm@0 448 device->ExtraData = NULL;
rlm@0 449 }
rlm@0 450
rlm@0 451 static void oss_start_capture(ALCdevice *pDevice)
rlm@0 452 {
rlm@0 453 oss_data *data = (oss_data*)pDevice->ExtraData;
rlm@0 454 data->doCapture = 1;
rlm@0 455 }
rlm@0 456
rlm@0 457 static void oss_stop_capture(ALCdevice *pDevice)
rlm@0 458 {
rlm@0 459 oss_data *data = (oss_data*)pDevice->ExtraData;
rlm@0 460 data->doCapture = 0;
rlm@0 461 }
rlm@0 462
rlm@0 463 static void oss_capture_samples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
rlm@0 464 {
rlm@0 465 oss_data *data = (oss_data*)pDevice->ExtraData;
rlm@0 466 if(lSamples <= (ALCuint)RingBufferSize(data->ring))
rlm@0 467 ReadRingBuffer(data->ring, pBuffer, lSamples);
rlm@0 468 else
rlm@0 469 alcSetError(pDevice, ALC_INVALID_VALUE);
rlm@0 470 }
rlm@0 471
rlm@0 472 static ALCuint oss_available_samples(ALCdevice *pDevice)
rlm@0 473 {
rlm@0 474 oss_data *data = (oss_data*)pDevice->ExtraData;
rlm@0 475 return RingBufferSize(data->ring);
rlm@0 476 }
rlm@0 477
rlm@0 478
rlm@0 479 static const BackendFuncs oss_funcs = {
rlm@0 480 oss_open_playback,
rlm@0 481 oss_close_playback,
rlm@0 482 oss_reset_playback,
rlm@0 483 oss_stop_playback,
rlm@0 484 oss_open_capture,
rlm@0 485 oss_close_capture,
rlm@0 486 oss_start_capture,
rlm@0 487 oss_stop_capture,
rlm@0 488 oss_capture_samples,
rlm@0 489 oss_available_samples
rlm@0 490 };
rlm@0 491
rlm@0 492 ALCboolean alc_oss_init(BackendFuncs *func_list)
rlm@0 493 {
rlm@0 494 *func_list = oss_funcs;
rlm@0 495 return ALC_TRUE;
rlm@0 496 }
rlm@0 497
rlm@0 498 void alc_oss_deinit(void)
rlm@0 499 {
rlm@0 500 }
rlm@0 501
rlm@0 502 void alc_oss_probe(enum DevProbe type)
rlm@0 503 {
rlm@0 504 switch(type)
rlm@0 505 {
rlm@0 506 case DEVICE_PROBE:
rlm@0 507 {
rlm@0 508 #ifdef HAVE_STAT
rlm@0 509 struct stat buf;
rlm@0 510 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0)
rlm@0 511 #endif
rlm@0 512 AppendDeviceList(oss_device);
rlm@0 513 }
rlm@0 514 break;
rlm@0 515
rlm@0 516 case ALL_DEVICE_PROBE:
rlm@0 517 {
rlm@0 518 #ifdef HAVE_STAT
rlm@0 519 struct stat buf;
rlm@0 520 if(stat(GetConfigValue("oss", "device", "/dev/dsp"), &buf) == 0)
rlm@0 521 #endif
rlm@0 522 AppendAllDeviceList(oss_device);
rlm@0 523 }
rlm@0 524 break;
rlm@0 525
rlm@0 526 case CAPTURE_DEVICE_PROBE:
rlm@0 527 {
rlm@0 528 #ifdef HAVE_STAT
rlm@0 529 struct stat buf;
rlm@0 530 if(stat(GetConfigValue("oss", "capture", "/dev/dsp"), &buf) == 0)
rlm@0 531 #endif
rlm@0 532 AppendCaptureDeviceList(oss_device);
rlm@0 533 }
rlm@0 534 break;
rlm@0 535 }
rlm@0 536 }