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