Mercurial > audio-send
view Alc/backends/alsa.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 <stdlib.h>24 #include <stdio.h>25 #include <memory.h>27 #include "alMain.h"29 #include <alsa/asoundlib.h>32 static const ALCchar alsaDevice[] = "ALSA Default";35 static void *alsa_handle;36 #ifdef HAVE_DYNLOAD37 #define MAKE_FUNC(f) static typeof(f) * p##f38 MAKE_FUNC(snd_strerror);39 MAKE_FUNC(snd_pcm_open);40 MAKE_FUNC(snd_pcm_close);41 MAKE_FUNC(snd_pcm_nonblock);42 MAKE_FUNC(snd_pcm_frames_to_bytes);43 MAKE_FUNC(snd_pcm_bytes_to_frames);44 MAKE_FUNC(snd_pcm_hw_params_malloc);45 MAKE_FUNC(snd_pcm_hw_params_free);46 MAKE_FUNC(snd_pcm_hw_params_any);47 MAKE_FUNC(snd_pcm_hw_params_set_access);48 MAKE_FUNC(snd_pcm_hw_params_set_format);49 MAKE_FUNC(snd_pcm_hw_params_set_channels);50 MAKE_FUNC(snd_pcm_hw_params_set_periods_near);51 MAKE_FUNC(snd_pcm_hw_params_set_rate_near);52 MAKE_FUNC(snd_pcm_hw_params_set_rate);53 MAKE_FUNC(snd_pcm_hw_params_set_rate_resample);54 MAKE_FUNC(snd_pcm_hw_params_set_buffer_time_near);55 MAKE_FUNC(snd_pcm_hw_params_set_period_time_near);56 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);57 MAKE_FUNC(snd_pcm_hw_params_set_period_size_near);58 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);59 MAKE_FUNC(snd_pcm_hw_params_get_buffer_size);60 MAKE_FUNC(snd_pcm_hw_params_get_period_size);61 MAKE_FUNC(snd_pcm_hw_params_get_access);62 MAKE_FUNC(snd_pcm_hw_params_get_periods);63 MAKE_FUNC(snd_pcm_hw_params);64 MAKE_FUNC(snd_pcm_sw_params_malloc);65 MAKE_FUNC(snd_pcm_sw_params_current);66 MAKE_FUNC(snd_pcm_sw_params_set_avail_min);67 MAKE_FUNC(snd_pcm_sw_params);68 MAKE_FUNC(snd_pcm_sw_params_free);69 MAKE_FUNC(snd_pcm_prepare);70 MAKE_FUNC(snd_pcm_start);71 MAKE_FUNC(snd_pcm_resume);72 MAKE_FUNC(snd_pcm_wait);73 MAKE_FUNC(snd_pcm_state);74 MAKE_FUNC(snd_pcm_avail_update);75 MAKE_FUNC(snd_pcm_areas_silence);76 MAKE_FUNC(snd_pcm_mmap_begin);77 MAKE_FUNC(snd_pcm_mmap_commit);78 MAKE_FUNC(snd_pcm_readi);79 MAKE_FUNC(snd_pcm_writei);80 MAKE_FUNC(snd_pcm_drain);81 MAKE_FUNC(snd_pcm_recover);82 MAKE_FUNC(snd_pcm_info_malloc);83 MAKE_FUNC(snd_pcm_info_free);84 MAKE_FUNC(snd_pcm_info_set_device);85 MAKE_FUNC(snd_pcm_info_set_subdevice);86 MAKE_FUNC(snd_pcm_info_set_stream);87 MAKE_FUNC(snd_pcm_info_get_name);88 MAKE_FUNC(snd_ctl_pcm_next_device);89 MAKE_FUNC(snd_ctl_pcm_info);90 MAKE_FUNC(snd_ctl_open);91 MAKE_FUNC(snd_ctl_close);92 MAKE_FUNC(snd_ctl_card_info_malloc);93 MAKE_FUNC(snd_ctl_card_info_free);94 MAKE_FUNC(snd_ctl_card_info);95 MAKE_FUNC(snd_ctl_card_info_get_name);96 MAKE_FUNC(snd_ctl_card_info_get_id);97 MAKE_FUNC(snd_card_next);98 #undef MAKE_FUNC100 #define snd_strerror psnd_strerror101 #define snd_pcm_open psnd_pcm_open102 #define snd_pcm_close psnd_pcm_close103 #define snd_pcm_nonblock psnd_pcm_nonblock104 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes105 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames106 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc107 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free108 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any109 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access110 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format111 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels112 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near113 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near114 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate115 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample116 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near117 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near118 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near119 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near120 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min121 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size122 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size123 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access124 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods125 #define snd_pcm_hw_params psnd_pcm_hw_params126 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc127 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current128 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min129 #define snd_pcm_sw_params psnd_pcm_sw_params130 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free131 #define snd_pcm_prepare psnd_pcm_prepare132 #define snd_pcm_start psnd_pcm_start133 #define snd_pcm_resume psnd_pcm_resume134 #define snd_pcm_wait psnd_pcm_wait135 #define snd_pcm_state psnd_pcm_state136 #define snd_pcm_avail_update psnd_pcm_avail_update137 #define snd_pcm_areas_silence psnd_pcm_areas_silence138 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin139 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit140 #define snd_pcm_readi psnd_pcm_readi141 #define snd_pcm_writei psnd_pcm_writei142 #define snd_pcm_drain psnd_pcm_drain143 #define snd_pcm_recover psnd_pcm_recover144 #define snd_pcm_info_malloc psnd_pcm_info_malloc145 #define snd_pcm_info_free psnd_pcm_info_free146 #define snd_pcm_info_set_device psnd_pcm_info_set_device147 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice148 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream149 #define snd_pcm_info_get_name psnd_pcm_info_get_name150 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device151 #define snd_ctl_pcm_info psnd_ctl_pcm_info152 #define snd_ctl_open psnd_ctl_open153 #define snd_ctl_close psnd_ctl_close154 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc155 #define snd_ctl_card_info_free psnd_ctl_card_info_free156 #define snd_ctl_card_info psnd_ctl_card_info157 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name158 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id159 #define snd_card_next psnd_card_next160 #endif163 static ALCboolean alsa_load(void)164 {165 if(!alsa_handle)166 {167 #ifdef HAVE_DYNLOAD168 alsa_handle = LoadLib("libasound.so.2");169 if(!alsa_handle)170 return ALC_FALSE;172 #define LOAD_FUNC(f) do { \173 p##f = GetSymbol(alsa_handle, #f); \174 if(p##f == NULL) { \175 CloseLib(alsa_handle); \176 alsa_handle = NULL; \177 return ALC_FALSE; \178 } \179 } while(0)180 LOAD_FUNC(snd_strerror);181 LOAD_FUNC(snd_pcm_open);182 LOAD_FUNC(snd_pcm_close);183 LOAD_FUNC(snd_pcm_nonblock);184 LOAD_FUNC(snd_pcm_frames_to_bytes);185 LOAD_FUNC(snd_pcm_bytes_to_frames);186 LOAD_FUNC(snd_pcm_hw_params_malloc);187 LOAD_FUNC(snd_pcm_hw_params_free);188 LOAD_FUNC(snd_pcm_hw_params_any);189 LOAD_FUNC(snd_pcm_hw_params_set_access);190 LOAD_FUNC(snd_pcm_hw_params_set_format);191 LOAD_FUNC(snd_pcm_hw_params_set_channels);192 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);193 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);194 LOAD_FUNC(snd_pcm_hw_params_set_rate);195 LOAD_FUNC(snd_pcm_hw_params_set_rate_resample);196 LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near);197 LOAD_FUNC(snd_pcm_hw_params_set_period_time_near);198 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);199 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);200 LOAD_FUNC(snd_pcm_hw_params_set_period_size_near);201 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);202 LOAD_FUNC(snd_pcm_hw_params_get_period_size);203 LOAD_FUNC(snd_pcm_hw_params_get_access);204 LOAD_FUNC(snd_pcm_hw_params_get_periods);205 LOAD_FUNC(snd_pcm_hw_params);206 LOAD_FUNC(snd_pcm_sw_params_malloc);207 LOAD_FUNC(snd_pcm_sw_params_current);208 LOAD_FUNC(snd_pcm_sw_params_set_avail_min);209 LOAD_FUNC(snd_pcm_sw_params);210 LOAD_FUNC(snd_pcm_sw_params_free);211 LOAD_FUNC(snd_pcm_prepare);212 LOAD_FUNC(snd_pcm_start);213 LOAD_FUNC(snd_pcm_resume);214 LOAD_FUNC(snd_pcm_wait);215 LOAD_FUNC(snd_pcm_state);216 LOAD_FUNC(snd_pcm_avail_update);217 LOAD_FUNC(snd_pcm_areas_silence);218 LOAD_FUNC(snd_pcm_mmap_begin);219 LOAD_FUNC(snd_pcm_mmap_commit);220 LOAD_FUNC(snd_pcm_readi);221 LOAD_FUNC(snd_pcm_writei);222 LOAD_FUNC(snd_pcm_drain);223 LOAD_FUNC(snd_pcm_recover);224 LOAD_FUNC(snd_pcm_info_malloc);225 LOAD_FUNC(snd_pcm_info_free);226 LOAD_FUNC(snd_pcm_info_set_device);227 LOAD_FUNC(snd_pcm_info_set_subdevice);228 LOAD_FUNC(snd_pcm_info_set_stream);229 LOAD_FUNC(snd_pcm_info_get_name);230 LOAD_FUNC(snd_ctl_pcm_next_device);231 LOAD_FUNC(snd_ctl_pcm_info);232 LOAD_FUNC(snd_ctl_open);233 LOAD_FUNC(snd_ctl_close);234 LOAD_FUNC(snd_ctl_card_info_malloc);235 LOAD_FUNC(snd_ctl_card_info_free);236 LOAD_FUNC(snd_ctl_card_info);237 LOAD_FUNC(snd_ctl_card_info_get_name);238 LOAD_FUNC(snd_ctl_card_info_get_id);239 LOAD_FUNC(snd_card_next);240 #undef LOAD_FUNC241 #else242 alsa_handle = (void*)0xDEADBEEF;243 #endif244 }245 return ALC_TRUE;246 }249 typedef struct {250 snd_pcm_t *pcmHandle;252 ALvoid *buffer;253 ALsizei size;255 ALboolean doCapture;256 RingBuffer *ring;258 volatile int killNow;259 ALvoid *thread;260 } alsa_data;262 typedef struct {263 ALCchar *name;264 char *card;265 int dev;266 } DevMap;268 static DevMap *allDevNameMap;269 static ALuint numDevNames;270 static DevMap *allCaptureDevNameMap;271 static ALuint numCaptureDevNames;273 static const char *device_prefix;274 static const char *capture_prefix;277 static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count)278 {279 snd_ctl_t *handle;280 int card, err, dev, idx;281 snd_ctl_card_info_t *info;282 snd_pcm_info_t *pcminfo;283 DevMap *DevList;284 char name[1024];286 snd_ctl_card_info_malloc(&info);287 snd_pcm_info_malloc(&pcminfo);289 card = -1;290 if((err=snd_card_next(&card)) < 0)291 ERR("Failed to find a card: %s\n", snd_strerror(err));293 DevList = malloc(sizeof(DevMap) * 1);294 DevList[0].name = strdup("ALSA Default");295 DevList[0].card = NULL;296 DevList[0].dev = 0;297 idx = 1;298 while(card >= 0)299 {300 sprintf(name, "hw:%d", card);301 if((err = snd_ctl_open(&handle, name, 0)) < 0)302 {303 ERR("control open (%i): %s\n", card, snd_strerror(err));304 goto next_card;305 }306 if((err = snd_ctl_card_info(handle, info)) < 0)307 {308 ERR("control hardware info (%i): %s\n", card, snd_strerror(err));309 snd_ctl_close(handle);310 goto next_card;311 }313 dev = -1;314 while(1)315 {316 const char *cname, *dname, *cid;317 void *temp;319 if(snd_ctl_pcm_next_device(handle, &dev) < 0)320 ERR("snd_ctl_pcm_next_device failed\n");321 if(dev < 0)322 break;324 snd_pcm_info_set_device(pcminfo, dev);325 snd_pcm_info_set_subdevice(pcminfo, 0);326 snd_pcm_info_set_stream(pcminfo, stream);327 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {328 if(err != -ENOENT)329 ERR("control digital audio info (%i): %s\n", card, snd_strerror(err));330 continue;331 }333 temp = realloc(DevList, sizeof(DevMap) * (idx+1));334 if(temp)335 {336 DevList = temp;337 cname = snd_ctl_card_info_get_name(info);338 dname = snd_pcm_info_get_name(pcminfo);339 cid = snd_ctl_card_info_get_id(info);340 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",341 cname, dname, cid, dev);342 DevList[idx].name = strdup(name);343 DevList[idx].card = strdup(cid);344 DevList[idx].dev = dev;345 idx++;346 }347 }348 snd_ctl_close(handle);349 next_card:350 if(snd_card_next(&card) < 0) {351 ERR("snd_card_next failed\n");352 break;353 }354 }356 snd_pcm_info_free(pcminfo);357 snd_ctl_card_info_free(info);359 *count = idx;360 return DevList;361 }364 static int xrun_recovery(snd_pcm_t *handle, int err)365 {366 err = snd_pcm_recover(handle, err, 1);367 if(err < 0)368 ERR("recover failed: %s\n", snd_strerror(err));369 return err;370 }372 static int verify_state(snd_pcm_t *handle)373 {374 snd_pcm_state_t state = snd_pcm_state(handle);375 if(state == SND_PCM_STATE_DISCONNECTED)376 return -ENODEV;377 if(state == SND_PCM_STATE_XRUN)378 {379 int err = xrun_recovery(handle, -EPIPE);380 if(err < 0) return err;381 }382 else if(state == SND_PCM_STATE_SUSPENDED)383 {384 int err = xrun_recovery(handle, -ESTRPIPE);385 if(err < 0) return err;386 }388 return state;389 }392 static ALuint ALSAProc(ALvoid *ptr)393 {394 ALCdevice *pDevice = (ALCdevice*)ptr;395 alsa_data *data = (alsa_data*)pDevice->ExtraData;396 const snd_pcm_channel_area_t *areas = NULL;397 snd_pcm_sframes_t avail, commitres;398 snd_pcm_uframes_t offset, frames;399 char *WritePtr;400 int err;402 SetRTPriority();404 while(!data->killNow)405 {406 int state = verify_state(data->pcmHandle);407 if(state < 0)408 {409 ERR("Invalid state detected: %s\n", snd_strerror(state));410 aluHandleDisconnect(pDevice);411 break;412 }414 avail = snd_pcm_avail_update(data->pcmHandle);415 if(avail < 0)416 {417 ERR("available update failed: %s\n", snd_strerror(avail));418 continue;419 }421 // make sure there's frames to process422 if((snd_pcm_uframes_t)avail < pDevice->UpdateSize)423 {424 if(state != SND_PCM_STATE_RUNNING)425 {426 err = snd_pcm_start(data->pcmHandle);427 if(err < 0)428 {429 ERR("start failed: %s\n", snd_strerror(err));430 continue;431 }432 }433 if(snd_pcm_wait(data->pcmHandle, 1000) == 0)434 ERR("Wait timeout... buffer size too low?\n");435 continue;436 }437 avail -= avail%pDevice->UpdateSize;439 // it is possible that contiguous areas are smaller, thus we use a loop440 while(avail > 0)441 {442 frames = avail;444 err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);445 if(err < 0)446 {447 ERR("mmap begin error: %s\n", snd_strerror(err));448 break;449 }451 WritePtr = (char*)areas->addr + (offset * areas->step / 8);452 aluMixData(pDevice, WritePtr, frames);454 commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames);455 if(commitres < 0 || (commitres-frames) != 0)456 {457 ERR("mmap commit error: %s\n",458 snd_strerror(commitres >= 0 ? -EPIPE : commitres));459 break;460 }462 avail -= frames;463 }464 }466 return 0;467 }469 static ALuint ALSANoMMapProc(ALvoid *ptr)470 {471 ALCdevice *pDevice = (ALCdevice*)ptr;472 alsa_data *data = (alsa_data*)pDevice->ExtraData;473 snd_pcm_sframes_t avail;474 char *WritePtr;476 SetRTPriority();478 while(!data->killNow)479 {480 int state = verify_state(data->pcmHandle);481 if(state < 0)482 {483 ERR("Invalid state detected: %s\n", snd_strerror(state));484 aluHandleDisconnect(pDevice);485 break;486 }488 WritePtr = data->buffer;489 avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1);490 aluMixData(pDevice, WritePtr, avail);492 while(avail > 0)493 {494 int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail);495 switch (ret)496 {497 case -EAGAIN:498 continue;499 case -ESTRPIPE:500 case -EPIPE:501 case -EINTR:502 ret = snd_pcm_recover(data->pcmHandle, ret, 1);503 if(ret < 0)504 avail = 0;505 break;506 default:507 if (ret >= 0)508 {509 WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret);510 avail -= ret;511 }512 break;513 }514 if (ret < 0)515 {516 ret = snd_pcm_prepare(data->pcmHandle);517 if(ret < 0)518 break;519 }520 }521 }523 return 0;524 }526 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)527 {528 alsa_data *data;529 char driver[128];530 int i;532 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1);533 driver[sizeof(driver)-1] = 0;535 if(!deviceName)536 deviceName = alsaDevice;537 else if(strcmp(deviceName, alsaDevice) != 0)538 {539 size_t idx;541 if(!allDevNameMap)542 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);544 for(idx = 0;idx < numDevNames;idx++)545 {546 if(allDevNameMap[idx].name &&547 strcmp(deviceName, allDevNameMap[idx].name) == 0)548 {549 if(idx > 0)550 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix,551 allDevNameMap[idx].card, allDevNameMap[idx].dev);552 break;553 }554 }555 if(idx == numDevNames)556 return ALC_FALSE;557 }559 data = (alsa_data*)calloc(1, sizeof(alsa_data));561 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);562 if(i >= 0)563 {564 i = snd_pcm_nonblock(data->pcmHandle, 0);565 if(i < 0)566 snd_pcm_close(data->pcmHandle);567 }568 if(i < 0)569 {570 free(data);571 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i));572 return ALC_FALSE;573 }575 device->szDeviceName = strdup(deviceName);576 device->ExtraData = data;577 return ALC_TRUE;578 }580 static void alsa_close_playback(ALCdevice *device)581 {582 alsa_data *data = (alsa_data*)device->ExtraData;584 snd_pcm_close(data->pcmHandle);585 free(data);586 device->ExtraData = NULL;587 }589 static ALCboolean alsa_reset_playback(ALCdevice *device)590 {591 alsa_data *data = (alsa_data*)device->ExtraData;592 snd_pcm_uframes_t periodSizeInFrames;593 unsigned int periodLen, bufferLen;594 snd_pcm_sw_params_t *sp = NULL;595 snd_pcm_hw_params_t *p = NULL;596 snd_pcm_access_t access;597 snd_pcm_format_t format;598 unsigned int periods;599 unsigned int rate;600 int allowmmap;601 char *err;602 int i;605 format = -1;606 switch(device->FmtType)607 {608 case DevFmtByte:609 format = SND_PCM_FORMAT_S8;610 break;611 case DevFmtUByte:612 format = SND_PCM_FORMAT_U8;613 break;614 case DevFmtShort:615 format = SND_PCM_FORMAT_S16;616 break;617 case DevFmtUShort:618 format = SND_PCM_FORMAT_U16;619 break;620 case DevFmtFloat:621 format = SND_PCM_FORMAT_FLOAT;622 break;623 }625 allowmmap = GetConfigValueBool("alsa", "mmap", 1);626 periods = device->NumUpdates;627 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;628 bufferLen = periodLen * periods;629 rate = device->Frequency;631 err = NULL;632 snd_pcm_hw_params_malloc(&p);634 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)635 err = "any";636 /* set interleaved access */637 if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))638 {639 if(periods > 2)640 {641 periods--;642 bufferLen = periodLen * periods;643 }644 if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)645 err = "set access";646 }647 /* set format (implicitly sets sample bits) */648 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)649 {650 device->FmtType = DevFmtFloat;651 if(format == SND_PCM_FORMAT_FLOAT ||652 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0)653 {654 device->FmtType = DevFmtShort;655 if(format == SND_PCM_FORMAT_S16 ||656 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0)657 {658 device->FmtType = DevFmtUByte;659 if(format == SND_PCM_FORMAT_U8 ||660 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0)661 err = "set format";662 }663 }664 }665 /* set channels (implicitly sets frame bits) */666 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0)667 {668 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0)669 {670 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0)671 err = "set channels";672 else673 {674 if((device->Flags&DEVICE_CHANNELS_REQUEST))675 ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans));676 device->FmtChans = DevFmtMono;677 }678 }679 else680 {681 if((device->Flags&DEVICE_CHANNELS_REQUEST))682 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans));683 device->FmtChans = DevFmtStereo;684 }685 device->Flags &= ~DEVICE_CHANNELS_REQUEST;686 }687 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0)688 {689 ERR("Failed to disable ALSA resampler\n");690 i = 0;691 }692 /* set rate (implicitly constrains period/buffer parameters) */693 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)694 err = "set rate near";695 /* set buffer time (implicitly constrains period/buffer parameters) */696 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0)697 err = "set buffer time near";698 /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */699 if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0)700 err = "set period time near";701 /* install and prepare hardware configuration */702 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)703 err = "set params";704 if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0)705 err = "get access";706 if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)707 err = "get period size";708 if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)709 err = "get periods";710 if(i < 0)711 {712 ERR("%s failed: %s\n", err, snd_strerror(i));713 snd_pcm_hw_params_free(p);714 return ALC_FALSE;715 }717 snd_pcm_hw_params_free(p);719 err = NULL;720 snd_pcm_sw_params_malloc(&sp);722 if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)723 err = "sw current";724 if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)725 err = "sw set avail min";726 if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0)727 err = "sw set params";728 if(i != 0)729 {730 ERR("%s failed: %s\n", err, snd_strerror(i));731 snd_pcm_sw_params_free(sp);732 return ALC_FALSE;733 }735 snd_pcm_sw_params_free(sp);737 if(device->Frequency != rate)738 {739 if((device->Flags&DEVICE_FREQUENCY_REQUEST))740 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate);741 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;742 device->Frequency = rate;743 }745 SetDefaultChannelOrder(device);747 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);748 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)749 {750 /* Increase periods by one, since the temp buffer counts as an extra751 * period */752 periods++;753 data->buffer = malloc(data->size);754 if(!data->buffer)755 {756 ERR("buffer malloc failed\n");757 return ALC_FALSE;758 }759 device->UpdateSize = periodSizeInFrames;760 device->NumUpdates = periods;761 data->thread = StartThread(ALSANoMMapProc, device);762 }763 else764 {765 i = snd_pcm_prepare(data->pcmHandle);766 if(i < 0)767 {768 ERR("prepare error: %s\n", snd_strerror(i));769 return ALC_FALSE;770 }771 device->UpdateSize = periodSizeInFrames;772 device->NumUpdates = periods;773 data->thread = StartThread(ALSAProc, device);774 }775 if(data->thread == NULL)776 {777 ERR("Could not create playback thread\n");778 free(data->buffer);779 data->buffer = NULL;780 return ALC_FALSE;781 }783 return ALC_TRUE;784 }786 static void alsa_stop_playback(ALCdevice *device)787 {788 alsa_data *data = (alsa_data*)device->ExtraData;790 if(data->thread)791 {792 data->killNow = 1;793 StopThread(data->thread);794 data->thread = NULL;795 }796 data->killNow = 0;797 free(data->buffer);798 data->buffer = NULL;799 }802 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)803 {804 snd_pcm_hw_params_t *p;805 snd_pcm_uframes_t bufferSizeInFrames;806 snd_pcm_format_t format;807 ALuint frameSize;808 alsa_data *data;809 char driver[128];810 char *err;811 int i;813 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1);814 driver[sizeof(driver)-1] = 0;816 if(!allCaptureDevNameMap)817 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);819 if(!deviceName)820 deviceName = allCaptureDevNameMap[0].name;821 else822 {823 size_t idx;825 for(idx = 0;idx < numCaptureDevNames;idx++)826 {827 if(allCaptureDevNameMap[idx].name &&828 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)829 {830 if(idx > 0)831 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix,832 allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);833 break;834 }835 }836 if(idx == numCaptureDevNames)837 return ALC_FALSE;838 }840 data = (alsa_data*)calloc(1, sizeof(alsa_data));842 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);843 if(i < 0)844 {845 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i));846 free(data);847 return ALC_FALSE;848 }850 format = -1;851 switch(pDevice->FmtType)852 {853 case DevFmtByte:854 format = SND_PCM_FORMAT_S8;855 break;856 case DevFmtUByte:857 format = SND_PCM_FORMAT_U8;858 break;859 case DevFmtShort:860 format = SND_PCM_FORMAT_S16;861 break;862 case DevFmtUShort:863 format = SND_PCM_FORMAT_U16;864 break;865 case DevFmtFloat:866 format = SND_PCM_FORMAT_FLOAT;867 break;868 }870 err = NULL;871 bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates;872 snd_pcm_hw_params_malloc(&p);874 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)875 err = "any";876 /* set interleaved access */877 if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)878 err = "set access";879 /* set format (implicitly sets sample bits) */880 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)881 err = "set format";882 /* set channels (implicitly sets frame bits) */883 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0)884 err = "set channels";885 /* set rate (implicitly constrains period/buffer parameters) */886 if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0)887 err = "set rate near";888 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */889 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0)890 err = "set buffer size near";891 /* install and prepare hardware configuration */892 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)893 err = "set params";894 if(i < 0)895 {896 ERR("%s failed: %s\n", err, snd_strerror(i));897 snd_pcm_hw_params_free(p);898 goto error;899 }901 if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)902 {903 ERR("get size failed: %s\n", snd_strerror(i));904 snd_pcm_hw_params_free(p);905 goto error;906 }908 snd_pcm_hw_params_free(p);910 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);912 data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates);913 if(!data->ring)914 {915 ERR("ring buffer create failed\n");916 goto error;917 }919 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);920 data->buffer = malloc(data->size);921 if(!data->buffer)922 {923 ERR("buffer malloc failed\n");924 goto error;925 }927 pDevice->szDeviceName = strdup(deviceName);929 pDevice->ExtraData = data;930 return ALC_TRUE;932 error:933 free(data->buffer);934 DestroyRingBuffer(data->ring);935 snd_pcm_close(data->pcmHandle);936 free(data);938 pDevice->ExtraData = NULL;939 return ALC_FALSE;940 }942 static void alsa_close_capture(ALCdevice *pDevice)943 {944 alsa_data *data = (alsa_data*)pDevice->ExtraData;946 snd_pcm_close(data->pcmHandle);947 DestroyRingBuffer(data->ring);949 free(data->buffer);950 free(data);951 pDevice->ExtraData = NULL;952 }954 static void alsa_start_capture(ALCdevice *Device)955 {956 alsa_data *data = (alsa_data*)Device->ExtraData;957 int err;959 err = snd_pcm_start(data->pcmHandle);960 if(err < 0)961 {962 ERR("start failed: %s\n", snd_strerror(err));963 aluHandleDisconnect(Device);964 }965 else966 data->doCapture = AL_TRUE;967 }969 static void alsa_stop_capture(ALCdevice *Device)970 {971 alsa_data *data = (alsa_data*)Device->ExtraData;972 snd_pcm_drain(data->pcmHandle);973 data->doCapture = AL_FALSE;974 }976 static ALCuint alsa_available_samples(ALCdevice *Device)977 {978 alsa_data *data = (alsa_data*)Device->ExtraData;979 snd_pcm_sframes_t avail;981 avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0);982 if(avail < 0)983 {984 ERR("avail update failed: %s\n", snd_strerror(avail));986 if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0)987 {988 if(data->doCapture)989 avail = snd_pcm_start(data->pcmHandle);990 if(avail >= 0)991 avail = snd_pcm_avail_update(data->pcmHandle);992 }993 if(avail < 0)994 {995 ERR("restore error: %s\n", snd_strerror(avail));996 aluHandleDisconnect(Device);997 }998 }999 while(avail > 0)1000 {1001 snd_pcm_sframes_t amt;1003 amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);1004 if(avail < amt) amt = avail;1006 amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt);1007 if(amt < 0)1008 {1009 ERR("read error: %s\n", snd_strerror(amt));1011 if(amt == -EAGAIN)1012 continue;1013 if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0)1014 {1015 if(data->doCapture)1016 amt = snd_pcm_start(data->pcmHandle);1017 if(amt >= 0)1018 amt = snd_pcm_avail_update(data->pcmHandle);1019 }1020 if(amt < 0)1021 {1022 ERR("restore error: %s\n", snd_strerror(amt));1023 aluHandleDisconnect(Device);1024 break;1025 }1026 avail = amt;1027 continue;1028 }1030 WriteRingBuffer(data->ring, data->buffer, amt);1031 avail -= amt;1032 }1034 return RingBufferSize(data->ring);1035 }1037 static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)1038 {1039 alsa_data *data = (alsa_data*)Device->ExtraData;1041 if(Samples <= alsa_available_samples(Device))1042 ReadRingBuffer(data->ring, Buffer, Samples);1043 else1044 alcSetError(Device, ALC_INVALID_VALUE);1045 }1048 static const BackendFuncs alsa_funcs = {1049 alsa_open_playback,1050 alsa_close_playback,1051 alsa_reset_playback,1052 alsa_stop_playback,1053 alsa_open_capture,1054 alsa_close_capture,1055 alsa_start_capture,1056 alsa_stop_capture,1057 alsa_capture_samples,1058 alsa_available_samples1059 };1061 ALCboolean alc_alsa_init(BackendFuncs *func_list)1062 {1063 if(!alsa_load())1064 return ALC_FALSE;1065 device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:");1066 capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:");1067 *func_list = alsa_funcs;1068 return ALC_TRUE;1069 }1071 void alc_alsa_deinit(void)1072 {1073 ALuint i;1075 for(i = 0;i < numDevNames;++i)1076 {1077 free(allDevNameMap[i].name);1078 free(allDevNameMap[i].card);1079 }1080 free(allDevNameMap);1081 allDevNameMap = NULL;1082 numDevNames = 0;1084 for(i = 0;i < numCaptureDevNames;++i)1085 {1086 free(allCaptureDevNameMap[i].name);1087 free(allCaptureDevNameMap[i].card);1088 }1089 free(allCaptureDevNameMap);1090 allCaptureDevNameMap = NULL;1091 numCaptureDevNames = 0;1093 #ifdef HAVE_DYNLOAD1094 if(alsa_handle)1095 CloseLib(alsa_handle);1096 alsa_handle = NULL;1097 #endif1098 }1100 void alc_alsa_probe(enum DevProbe type)1101 {1102 ALuint i;1104 switch(type)1105 {1106 case DEVICE_PROBE:1107 AppendDeviceList(alsaDevice);1108 break;1110 case ALL_DEVICE_PROBE:1111 for(i = 0;i < numDevNames;++i)1112 {1113 free(allDevNameMap[i].name);1114 free(allDevNameMap[i].card);1115 }1117 free(allDevNameMap);1118 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);1120 for(i = 0;i < numDevNames;++i)1121 AppendAllDeviceList(allDevNameMap[i].name);1122 break;1124 case CAPTURE_DEVICE_PROBE:1125 for(i = 0;i < numCaptureDevNames;++i)1126 {1127 free(allCaptureDevNameMap[i].name);1128 free(allCaptureDevNameMap[i].card);1129 }1131 free(allCaptureDevNameMap);1132 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);1134 for(i = 0;i < numCaptureDevNames;++i)1135 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);1136 break;1137 }1138 }