annotate 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
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 <stdlib.h>
rlm@0 24 #include <stdio.h>
rlm@0 25 #include <memory.h>
rlm@0 26
rlm@0 27 #include "alMain.h"
rlm@0 28
rlm@0 29 #include <alsa/asoundlib.h>
rlm@0 30
rlm@0 31
rlm@0 32 static const ALCchar alsaDevice[] = "ALSA Default";
rlm@0 33
rlm@0 34
rlm@0 35 static void *alsa_handle;
rlm@0 36 #ifdef HAVE_DYNLOAD
rlm@0 37 #define MAKE_FUNC(f) static typeof(f) * p##f
rlm@0 38 MAKE_FUNC(snd_strerror);
rlm@0 39 MAKE_FUNC(snd_pcm_open);
rlm@0 40 MAKE_FUNC(snd_pcm_close);
rlm@0 41 MAKE_FUNC(snd_pcm_nonblock);
rlm@0 42 MAKE_FUNC(snd_pcm_frames_to_bytes);
rlm@0 43 MAKE_FUNC(snd_pcm_bytes_to_frames);
rlm@0 44 MAKE_FUNC(snd_pcm_hw_params_malloc);
rlm@0 45 MAKE_FUNC(snd_pcm_hw_params_free);
rlm@0 46 MAKE_FUNC(snd_pcm_hw_params_any);
rlm@0 47 MAKE_FUNC(snd_pcm_hw_params_set_access);
rlm@0 48 MAKE_FUNC(snd_pcm_hw_params_set_format);
rlm@0 49 MAKE_FUNC(snd_pcm_hw_params_set_channels);
rlm@0 50 MAKE_FUNC(snd_pcm_hw_params_set_periods_near);
rlm@0 51 MAKE_FUNC(snd_pcm_hw_params_set_rate_near);
rlm@0 52 MAKE_FUNC(snd_pcm_hw_params_set_rate);
rlm@0 53 MAKE_FUNC(snd_pcm_hw_params_set_rate_resample);
rlm@0 54 MAKE_FUNC(snd_pcm_hw_params_set_buffer_time_near);
rlm@0 55 MAKE_FUNC(snd_pcm_hw_params_set_period_time_near);
rlm@0 56 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_near);
rlm@0 57 MAKE_FUNC(snd_pcm_hw_params_set_period_size_near);
rlm@0 58 MAKE_FUNC(snd_pcm_hw_params_set_buffer_size_min);
rlm@0 59 MAKE_FUNC(snd_pcm_hw_params_get_buffer_size);
rlm@0 60 MAKE_FUNC(snd_pcm_hw_params_get_period_size);
rlm@0 61 MAKE_FUNC(snd_pcm_hw_params_get_access);
rlm@0 62 MAKE_FUNC(snd_pcm_hw_params_get_periods);
rlm@0 63 MAKE_FUNC(snd_pcm_hw_params);
rlm@0 64 MAKE_FUNC(snd_pcm_sw_params_malloc);
rlm@0 65 MAKE_FUNC(snd_pcm_sw_params_current);
rlm@0 66 MAKE_FUNC(snd_pcm_sw_params_set_avail_min);
rlm@0 67 MAKE_FUNC(snd_pcm_sw_params);
rlm@0 68 MAKE_FUNC(snd_pcm_sw_params_free);
rlm@0 69 MAKE_FUNC(snd_pcm_prepare);
rlm@0 70 MAKE_FUNC(snd_pcm_start);
rlm@0 71 MAKE_FUNC(snd_pcm_resume);
rlm@0 72 MAKE_FUNC(snd_pcm_wait);
rlm@0 73 MAKE_FUNC(snd_pcm_state);
rlm@0 74 MAKE_FUNC(snd_pcm_avail_update);
rlm@0 75 MAKE_FUNC(snd_pcm_areas_silence);
rlm@0 76 MAKE_FUNC(snd_pcm_mmap_begin);
rlm@0 77 MAKE_FUNC(snd_pcm_mmap_commit);
rlm@0 78 MAKE_FUNC(snd_pcm_readi);
rlm@0 79 MAKE_FUNC(snd_pcm_writei);
rlm@0 80 MAKE_FUNC(snd_pcm_drain);
rlm@0 81 MAKE_FUNC(snd_pcm_recover);
rlm@0 82 MAKE_FUNC(snd_pcm_info_malloc);
rlm@0 83 MAKE_FUNC(snd_pcm_info_free);
rlm@0 84 MAKE_FUNC(snd_pcm_info_set_device);
rlm@0 85 MAKE_FUNC(snd_pcm_info_set_subdevice);
rlm@0 86 MAKE_FUNC(snd_pcm_info_set_stream);
rlm@0 87 MAKE_FUNC(snd_pcm_info_get_name);
rlm@0 88 MAKE_FUNC(snd_ctl_pcm_next_device);
rlm@0 89 MAKE_FUNC(snd_ctl_pcm_info);
rlm@0 90 MAKE_FUNC(snd_ctl_open);
rlm@0 91 MAKE_FUNC(snd_ctl_close);
rlm@0 92 MAKE_FUNC(snd_ctl_card_info_malloc);
rlm@0 93 MAKE_FUNC(snd_ctl_card_info_free);
rlm@0 94 MAKE_FUNC(snd_ctl_card_info);
rlm@0 95 MAKE_FUNC(snd_ctl_card_info_get_name);
rlm@0 96 MAKE_FUNC(snd_ctl_card_info_get_id);
rlm@0 97 MAKE_FUNC(snd_card_next);
rlm@0 98 #undef MAKE_FUNC
rlm@0 99
rlm@0 100 #define snd_strerror psnd_strerror
rlm@0 101 #define snd_pcm_open psnd_pcm_open
rlm@0 102 #define snd_pcm_close psnd_pcm_close
rlm@0 103 #define snd_pcm_nonblock psnd_pcm_nonblock
rlm@0 104 #define snd_pcm_frames_to_bytes psnd_pcm_frames_to_bytes
rlm@0 105 #define snd_pcm_bytes_to_frames psnd_pcm_bytes_to_frames
rlm@0 106 #define snd_pcm_hw_params_malloc psnd_pcm_hw_params_malloc
rlm@0 107 #define snd_pcm_hw_params_free psnd_pcm_hw_params_free
rlm@0 108 #define snd_pcm_hw_params_any psnd_pcm_hw_params_any
rlm@0 109 #define snd_pcm_hw_params_set_access psnd_pcm_hw_params_set_access
rlm@0 110 #define snd_pcm_hw_params_set_format psnd_pcm_hw_params_set_format
rlm@0 111 #define snd_pcm_hw_params_set_channels psnd_pcm_hw_params_set_channels
rlm@0 112 #define snd_pcm_hw_params_set_periods_near psnd_pcm_hw_params_set_periods_near
rlm@0 113 #define snd_pcm_hw_params_set_rate_near psnd_pcm_hw_params_set_rate_near
rlm@0 114 #define snd_pcm_hw_params_set_rate psnd_pcm_hw_params_set_rate
rlm@0 115 #define snd_pcm_hw_params_set_rate_resample psnd_pcm_hw_params_set_rate_resample
rlm@0 116 #define snd_pcm_hw_params_set_buffer_time_near psnd_pcm_hw_params_set_buffer_time_near
rlm@0 117 #define snd_pcm_hw_params_set_period_time_near psnd_pcm_hw_params_set_period_time_near
rlm@0 118 #define snd_pcm_hw_params_set_buffer_size_near psnd_pcm_hw_params_set_buffer_size_near
rlm@0 119 #define snd_pcm_hw_params_set_period_size_near psnd_pcm_hw_params_set_period_size_near
rlm@0 120 #define snd_pcm_hw_params_set_buffer_size_min psnd_pcm_hw_params_set_buffer_size_min
rlm@0 121 #define snd_pcm_hw_params_get_buffer_size psnd_pcm_hw_params_get_buffer_size
rlm@0 122 #define snd_pcm_hw_params_get_period_size psnd_pcm_hw_params_get_period_size
rlm@0 123 #define snd_pcm_hw_params_get_access psnd_pcm_hw_params_get_access
rlm@0 124 #define snd_pcm_hw_params_get_periods psnd_pcm_hw_params_get_periods
rlm@0 125 #define snd_pcm_hw_params psnd_pcm_hw_params
rlm@0 126 #define snd_pcm_sw_params_malloc psnd_pcm_sw_params_malloc
rlm@0 127 #define snd_pcm_sw_params_current psnd_pcm_sw_params_current
rlm@0 128 #define snd_pcm_sw_params_set_avail_min psnd_pcm_sw_params_set_avail_min
rlm@0 129 #define snd_pcm_sw_params psnd_pcm_sw_params
rlm@0 130 #define snd_pcm_sw_params_free psnd_pcm_sw_params_free
rlm@0 131 #define snd_pcm_prepare psnd_pcm_prepare
rlm@0 132 #define snd_pcm_start psnd_pcm_start
rlm@0 133 #define snd_pcm_resume psnd_pcm_resume
rlm@0 134 #define snd_pcm_wait psnd_pcm_wait
rlm@0 135 #define snd_pcm_state psnd_pcm_state
rlm@0 136 #define snd_pcm_avail_update psnd_pcm_avail_update
rlm@0 137 #define snd_pcm_areas_silence psnd_pcm_areas_silence
rlm@0 138 #define snd_pcm_mmap_begin psnd_pcm_mmap_begin
rlm@0 139 #define snd_pcm_mmap_commit psnd_pcm_mmap_commit
rlm@0 140 #define snd_pcm_readi psnd_pcm_readi
rlm@0 141 #define snd_pcm_writei psnd_pcm_writei
rlm@0 142 #define snd_pcm_drain psnd_pcm_drain
rlm@0 143 #define snd_pcm_recover psnd_pcm_recover
rlm@0 144 #define snd_pcm_info_malloc psnd_pcm_info_malloc
rlm@0 145 #define snd_pcm_info_free psnd_pcm_info_free
rlm@0 146 #define snd_pcm_info_set_device psnd_pcm_info_set_device
rlm@0 147 #define snd_pcm_info_set_subdevice psnd_pcm_info_set_subdevice
rlm@0 148 #define snd_pcm_info_set_stream psnd_pcm_info_set_stream
rlm@0 149 #define snd_pcm_info_get_name psnd_pcm_info_get_name
rlm@0 150 #define snd_ctl_pcm_next_device psnd_ctl_pcm_next_device
rlm@0 151 #define snd_ctl_pcm_info psnd_ctl_pcm_info
rlm@0 152 #define snd_ctl_open psnd_ctl_open
rlm@0 153 #define snd_ctl_close psnd_ctl_close
rlm@0 154 #define snd_ctl_card_info_malloc psnd_ctl_card_info_malloc
rlm@0 155 #define snd_ctl_card_info_free psnd_ctl_card_info_free
rlm@0 156 #define snd_ctl_card_info psnd_ctl_card_info
rlm@0 157 #define snd_ctl_card_info_get_name psnd_ctl_card_info_get_name
rlm@0 158 #define snd_ctl_card_info_get_id psnd_ctl_card_info_get_id
rlm@0 159 #define snd_card_next psnd_card_next
rlm@0 160 #endif
rlm@0 161
rlm@0 162
rlm@0 163 static ALCboolean alsa_load(void)
rlm@0 164 {
rlm@0 165 if(!alsa_handle)
rlm@0 166 {
rlm@0 167 #ifdef HAVE_DYNLOAD
rlm@0 168 alsa_handle = LoadLib("libasound.so.2");
rlm@0 169 if(!alsa_handle)
rlm@0 170 return ALC_FALSE;
rlm@0 171
rlm@0 172 #define LOAD_FUNC(f) do { \
rlm@0 173 p##f = GetSymbol(alsa_handle, #f); \
rlm@0 174 if(p##f == NULL) { \
rlm@0 175 CloseLib(alsa_handle); \
rlm@0 176 alsa_handle = NULL; \
rlm@0 177 return ALC_FALSE; \
rlm@0 178 } \
rlm@0 179 } while(0)
rlm@0 180 LOAD_FUNC(snd_strerror);
rlm@0 181 LOAD_FUNC(snd_pcm_open);
rlm@0 182 LOAD_FUNC(snd_pcm_close);
rlm@0 183 LOAD_FUNC(snd_pcm_nonblock);
rlm@0 184 LOAD_FUNC(snd_pcm_frames_to_bytes);
rlm@0 185 LOAD_FUNC(snd_pcm_bytes_to_frames);
rlm@0 186 LOAD_FUNC(snd_pcm_hw_params_malloc);
rlm@0 187 LOAD_FUNC(snd_pcm_hw_params_free);
rlm@0 188 LOAD_FUNC(snd_pcm_hw_params_any);
rlm@0 189 LOAD_FUNC(snd_pcm_hw_params_set_access);
rlm@0 190 LOAD_FUNC(snd_pcm_hw_params_set_format);
rlm@0 191 LOAD_FUNC(snd_pcm_hw_params_set_channels);
rlm@0 192 LOAD_FUNC(snd_pcm_hw_params_set_periods_near);
rlm@0 193 LOAD_FUNC(snd_pcm_hw_params_set_rate_near);
rlm@0 194 LOAD_FUNC(snd_pcm_hw_params_set_rate);
rlm@0 195 LOAD_FUNC(snd_pcm_hw_params_set_rate_resample);
rlm@0 196 LOAD_FUNC(snd_pcm_hw_params_set_buffer_time_near);
rlm@0 197 LOAD_FUNC(snd_pcm_hw_params_set_period_time_near);
rlm@0 198 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_near);
rlm@0 199 LOAD_FUNC(snd_pcm_hw_params_set_buffer_size_min);
rlm@0 200 LOAD_FUNC(snd_pcm_hw_params_set_period_size_near);
rlm@0 201 LOAD_FUNC(snd_pcm_hw_params_get_buffer_size);
rlm@0 202 LOAD_FUNC(snd_pcm_hw_params_get_period_size);
rlm@0 203 LOAD_FUNC(snd_pcm_hw_params_get_access);
rlm@0 204 LOAD_FUNC(snd_pcm_hw_params_get_periods);
rlm@0 205 LOAD_FUNC(snd_pcm_hw_params);
rlm@0 206 LOAD_FUNC(snd_pcm_sw_params_malloc);
rlm@0 207 LOAD_FUNC(snd_pcm_sw_params_current);
rlm@0 208 LOAD_FUNC(snd_pcm_sw_params_set_avail_min);
rlm@0 209 LOAD_FUNC(snd_pcm_sw_params);
rlm@0 210 LOAD_FUNC(snd_pcm_sw_params_free);
rlm@0 211 LOAD_FUNC(snd_pcm_prepare);
rlm@0 212 LOAD_FUNC(snd_pcm_start);
rlm@0 213 LOAD_FUNC(snd_pcm_resume);
rlm@0 214 LOAD_FUNC(snd_pcm_wait);
rlm@0 215 LOAD_FUNC(snd_pcm_state);
rlm@0 216 LOAD_FUNC(snd_pcm_avail_update);
rlm@0 217 LOAD_FUNC(snd_pcm_areas_silence);
rlm@0 218 LOAD_FUNC(snd_pcm_mmap_begin);
rlm@0 219 LOAD_FUNC(snd_pcm_mmap_commit);
rlm@0 220 LOAD_FUNC(snd_pcm_readi);
rlm@0 221 LOAD_FUNC(snd_pcm_writei);
rlm@0 222 LOAD_FUNC(snd_pcm_drain);
rlm@0 223 LOAD_FUNC(snd_pcm_recover);
rlm@0 224 LOAD_FUNC(snd_pcm_info_malloc);
rlm@0 225 LOAD_FUNC(snd_pcm_info_free);
rlm@0 226 LOAD_FUNC(snd_pcm_info_set_device);
rlm@0 227 LOAD_FUNC(snd_pcm_info_set_subdevice);
rlm@0 228 LOAD_FUNC(snd_pcm_info_set_stream);
rlm@0 229 LOAD_FUNC(snd_pcm_info_get_name);
rlm@0 230 LOAD_FUNC(snd_ctl_pcm_next_device);
rlm@0 231 LOAD_FUNC(snd_ctl_pcm_info);
rlm@0 232 LOAD_FUNC(snd_ctl_open);
rlm@0 233 LOAD_FUNC(snd_ctl_close);
rlm@0 234 LOAD_FUNC(snd_ctl_card_info_malloc);
rlm@0 235 LOAD_FUNC(snd_ctl_card_info_free);
rlm@0 236 LOAD_FUNC(snd_ctl_card_info);
rlm@0 237 LOAD_FUNC(snd_ctl_card_info_get_name);
rlm@0 238 LOAD_FUNC(snd_ctl_card_info_get_id);
rlm@0 239 LOAD_FUNC(snd_card_next);
rlm@0 240 #undef LOAD_FUNC
rlm@0 241 #else
rlm@0 242 alsa_handle = (void*)0xDEADBEEF;
rlm@0 243 #endif
rlm@0 244 }
rlm@0 245 return ALC_TRUE;
rlm@0 246 }
rlm@0 247
rlm@0 248
rlm@0 249 typedef struct {
rlm@0 250 snd_pcm_t *pcmHandle;
rlm@0 251
rlm@0 252 ALvoid *buffer;
rlm@0 253 ALsizei size;
rlm@0 254
rlm@0 255 ALboolean doCapture;
rlm@0 256 RingBuffer *ring;
rlm@0 257
rlm@0 258 volatile int killNow;
rlm@0 259 ALvoid *thread;
rlm@0 260 } alsa_data;
rlm@0 261
rlm@0 262 typedef struct {
rlm@0 263 ALCchar *name;
rlm@0 264 char *card;
rlm@0 265 int dev;
rlm@0 266 } DevMap;
rlm@0 267
rlm@0 268 static DevMap *allDevNameMap;
rlm@0 269 static ALuint numDevNames;
rlm@0 270 static DevMap *allCaptureDevNameMap;
rlm@0 271 static ALuint numCaptureDevNames;
rlm@0 272
rlm@0 273 static const char *device_prefix;
rlm@0 274 static const char *capture_prefix;
rlm@0 275
rlm@0 276
rlm@0 277 static DevMap *probe_devices(snd_pcm_stream_t stream, ALuint *count)
rlm@0 278 {
rlm@0 279 snd_ctl_t *handle;
rlm@0 280 int card, err, dev, idx;
rlm@0 281 snd_ctl_card_info_t *info;
rlm@0 282 snd_pcm_info_t *pcminfo;
rlm@0 283 DevMap *DevList;
rlm@0 284 char name[1024];
rlm@0 285
rlm@0 286 snd_ctl_card_info_malloc(&info);
rlm@0 287 snd_pcm_info_malloc(&pcminfo);
rlm@0 288
rlm@0 289 card = -1;
rlm@0 290 if((err=snd_card_next(&card)) < 0)
rlm@0 291 ERR("Failed to find a card: %s\n", snd_strerror(err));
rlm@0 292
rlm@0 293 DevList = malloc(sizeof(DevMap) * 1);
rlm@0 294 DevList[0].name = strdup("ALSA Default");
rlm@0 295 DevList[0].card = NULL;
rlm@0 296 DevList[0].dev = 0;
rlm@0 297 idx = 1;
rlm@0 298 while(card >= 0)
rlm@0 299 {
rlm@0 300 sprintf(name, "hw:%d", card);
rlm@0 301 if((err = snd_ctl_open(&handle, name, 0)) < 0)
rlm@0 302 {
rlm@0 303 ERR("control open (%i): %s\n", card, snd_strerror(err));
rlm@0 304 goto next_card;
rlm@0 305 }
rlm@0 306 if((err = snd_ctl_card_info(handle, info)) < 0)
rlm@0 307 {
rlm@0 308 ERR("control hardware info (%i): %s\n", card, snd_strerror(err));
rlm@0 309 snd_ctl_close(handle);
rlm@0 310 goto next_card;
rlm@0 311 }
rlm@0 312
rlm@0 313 dev = -1;
rlm@0 314 while(1)
rlm@0 315 {
rlm@0 316 const char *cname, *dname, *cid;
rlm@0 317 void *temp;
rlm@0 318
rlm@0 319 if(snd_ctl_pcm_next_device(handle, &dev) < 0)
rlm@0 320 ERR("snd_ctl_pcm_next_device failed\n");
rlm@0 321 if(dev < 0)
rlm@0 322 break;
rlm@0 323
rlm@0 324 snd_pcm_info_set_device(pcminfo, dev);
rlm@0 325 snd_pcm_info_set_subdevice(pcminfo, 0);
rlm@0 326 snd_pcm_info_set_stream(pcminfo, stream);
rlm@0 327 if((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
rlm@0 328 if(err != -ENOENT)
rlm@0 329 ERR("control digital audio info (%i): %s\n", card, snd_strerror(err));
rlm@0 330 continue;
rlm@0 331 }
rlm@0 332
rlm@0 333 temp = realloc(DevList, sizeof(DevMap) * (idx+1));
rlm@0 334 if(temp)
rlm@0 335 {
rlm@0 336 DevList = temp;
rlm@0 337 cname = snd_ctl_card_info_get_name(info);
rlm@0 338 dname = snd_pcm_info_get_name(pcminfo);
rlm@0 339 cid = snd_ctl_card_info_get_id(info);
rlm@0 340 snprintf(name, sizeof(name), "%s, %s (CARD=%s,DEV=%d)",
rlm@0 341 cname, dname, cid, dev);
rlm@0 342 DevList[idx].name = strdup(name);
rlm@0 343 DevList[idx].card = strdup(cid);
rlm@0 344 DevList[idx].dev = dev;
rlm@0 345 idx++;
rlm@0 346 }
rlm@0 347 }
rlm@0 348 snd_ctl_close(handle);
rlm@0 349 next_card:
rlm@0 350 if(snd_card_next(&card) < 0) {
rlm@0 351 ERR("snd_card_next failed\n");
rlm@0 352 break;
rlm@0 353 }
rlm@0 354 }
rlm@0 355
rlm@0 356 snd_pcm_info_free(pcminfo);
rlm@0 357 snd_ctl_card_info_free(info);
rlm@0 358
rlm@0 359 *count = idx;
rlm@0 360 return DevList;
rlm@0 361 }
rlm@0 362
rlm@0 363
rlm@0 364 static int xrun_recovery(snd_pcm_t *handle, int err)
rlm@0 365 {
rlm@0 366 err = snd_pcm_recover(handle, err, 1);
rlm@0 367 if(err < 0)
rlm@0 368 ERR("recover failed: %s\n", snd_strerror(err));
rlm@0 369 return err;
rlm@0 370 }
rlm@0 371
rlm@0 372 static int verify_state(snd_pcm_t *handle)
rlm@0 373 {
rlm@0 374 snd_pcm_state_t state = snd_pcm_state(handle);
rlm@0 375 if(state == SND_PCM_STATE_DISCONNECTED)
rlm@0 376 return -ENODEV;
rlm@0 377 if(state == SND_PCM_STATE_XRUN)
rlm@0 378 {
rlm@0 379 int err = xrun_recovery(handle, -EPIPE);
rlm@0 380 if(err < 0) return err;
rlm@0 381 }
rlm@0 382 else if(state == SND_PCM_STATE_SUSPENDED)
rlm@0 383 {
rlm@0 384 int err = xrun_recovery(handle, -ESTRPIPE);
rlm@0 385 if(err < 0) return err;
rlm@0 386 }
rlm@0 387
rlm@0 388 return state;
rlm@0 389 }
rlm@0 390
rlm@0 391
rlm@0 392 static ALuint ALSAProc(ALvoid *ptr)
rlm@0 393 {
rlm@0 394 ALCdevice *pDevice = (ALCdevice*)ptr;
rlm@0 395 alsa_data *data = (alsa_data*)pDevice->ExtraData;
rlm@0 396 const snd_pcm_channel_area_t *areas = NULL;
rlm@0 397 snd_pcm_sframes_t avail, commitres;
rlm@0 398 snd_pcm_uframes_t offset, frames;
rlm@0 399 char *WritePtr;
rlm@0 400 int err;
rlm@0 401
rlm@0 402 SetRTPriority();
rlm@0 403
rlm@0 404 while(!data->killNow)
rlm@0 405 {
rlm@0 406 int state = verify_state(data->pcmHandle);
rlm@0 407 if(state < 0)
rlm@0 408 {
rlm@0 409 ERR("Invalid state detected: %s\n", snd_strerror(state));
rlm@0 410 aluHandleDisconnect(pDevice);
rlm@0 411 break;
rlm@0 412 }
rlm@0 413
rlm@0 414 avail = snd_pcm_avail_update(data->pcmHandle);
rlm@0 415 if(avail < 0)
rlm@0 416 {
rlm@0 417 ERR("available update failed: %s\n", snd_strerror(avail));
rlm@0 418 continue;
rlm@0 419 }
rlm@0 420
rlm@0 421 // make sure there's frames to process
rlm@0 422 if((snd_pcm_uframes_t)avail < pDevice->UpdateSize)
rlm@0 423 {
rlm@0 424 if(state != SND_PCM_STATE_RUNNING)
rlm@0 425 {
rlm@0 426 err = snd_pcm_start(data->pcmHandle);
rlm@0 427 if(err < 0)
rlm@0 428 {
rlm@0 429 ERR("start failed: %s\n", snd_strerror(err));
rlm@0 430 continue;
rlm@0 431 }
rlm@0 432 }
rlm@0 433 if(snd_pcm_wait(data->pcmHandle, 1000) == 0)
rlm@0 434 ERR("Wait timeout... buffer size too low?\n");
rlm@0 435 continue;
rlm@0 436 }
rlm@0 437 avail -= avail%pDevice->UpdateSize;
rlm@0 438
rlm@0 439 // it is possible that contiguous areas are smaller, thus we use a loop
rlm@0 440 while(avail > 0)
rlm@0 441 {
rlm@0 442 frames = avail;
rlm@0 443
rlm@0 444 err = snd_pcm_mmap_begin(data->pcmHandle, &areas, &offset, &frames);
rlm@0 445 if(err < 0)
rlm@0 446 {
rlm@0 447 ERR("mmap begin error: %s\n", snd_strerror(err));
rlm@0 448 break;
rlm@0 449 }
rlm@0 450
rlm@0 451 WritePtr = (char*)areas->addr + (offset * areas->step / 8);
rlm@0 452 aluMixData(pDevice, WritePtr, frames);
rlm@0 453
rlm@0 454 commitres = snd_pcm_mmap_commit(data->pcmHandle, offset, frames);
rlm@0 455 if(commitres < 0 || (commitres-frames) != 0)
rlm@0 456 {
rlm@0 457 ERR("mmap commit error: %s\n",
rlm@0 458 snd_strerror(commitres >= 0 ? -EPIPE : commitres));
rlm@0 459 break;
rlm@0 460 }
rlm@0 461
rlm@0 462 avail -= frames;
rlm@0 463 }
rlm@0 464 }
rlm@0 465
rlm@0 466 return 0;
rlm@0 467 }
rlm@0 468
rlm@0 469 static ALuint ALSANoMMapProc(ALvoid *ptr)
rlm@0 470 {
rlm@0 471 ALCdevice *pDevice = (ALCdevice*)ptr;
rlm@0 472 alsa_data *data = (alsa_data*)pDevice->ExtraData;
rlm@0 473 snd_pcm_sframes_t avail;
rlm@0 474 char *WritePtr;
rlm@0 475
rlm@0 476 SetRTPriority();
rlm@0 477
rlm@0 478 while(!data->killNow)
rlm@0 479 {
rlm@0 480 int state = verify_state(data->pcmHandle);
rlm@0 481 if(state < 0)
rlm@0 482 {
rlm@0 483 ERR("Invalid state detected: %s\n", snd_strerror(state));
rlm@0 484 aluHandleDisconnect(pDevice);
rlm@0 485 break;
rlm@0 486 }
rlm@0 487
rlm@0 488 WritePtr = data->buffer;
rlm@0 489 avail = data->size / snd_pcm_frames_to_bytes(data->pcmHandle, 1);
rlm@0 490 aluMixData(pDevice, WritePtr, avail);
rlm@0 491
rlm@0 492 while(avail > 0)
rlm@0 493 {
rlm@0 494 int ret = snd_pcm_writei(data->pcmHandle, WritePtr, avail);
rlm@0 495 switch (ret)
rlm@0 496 {
rlm@0 497 case -EAGAIN:
rlm@0 498 continue;
rlm@0 499 case -ESTRPIPE:
rlm@0 500 case -EPIPE:
rlm@0 501 case -EINTR:
rlm@0 502 ret = snd_pcm_recover(data->pcmHandle, ret, 1);
rlm@0 503 if(ret < 0)
rlm@0 504 avail = 0;
rlm@0 505 break;
rlm@0 506 default:
rlm@0 507 if (ret >= 0)
rlm@0 508 {
rlm@0 509 WritePtr += snd_pcm_frames_to_bytes(data->pcmHandle, ret);
rlm@0 510 avail -= ret;
rlm@0 511 }
rlm@0 512 break;
rlm@0 513 }
rlm@0 514 if (ret < 0)
rlm@0 515 {
rlm@0 516 ret = snd_pcm_prepare(data->pcmHandle);
rlm@0 517 if(ret < 0)
rlm@0 518 break;
rlm@0 519 }
rlm@0 520 }
rlm@0 521 }
rlm@0 522
rlm@0 523 return 0;
rlm@0 524 }
rlm@0 525
rlm@0 526 static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName)
rlm@0 527 {
rlm@0 528 alsa_data *data;
rlm@0 529 char driver[128];
rlm@0 530 int i;
rlm@0 531
rlm@0 532 strncpy(driver, GetConfigValue("alsa", "device", "default"), sizeof(driver)-1);
rlm@0 533 driver[sizeof(driver)-1] = 0;
rlm@0 534
rlm@0 535 if(!deviceName)
rlm@0 536 deviceName = alsaDevice;
rlm@0 537 else if(strcmp(deviceName, alsaDevice) != 0)
rlm@0 538 {
rlm@0 539 size_t idx;
rlm@0 540
rlm@0 541 if(!allDevNameMap)
rlm@0 542 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);
rlm@0 543
rlm@0 544 for(idx = 0;idx < numDevNames;idx++)
rlm@0 545 {
rlm@0 546 if(allDevNameMap[idx].name &&
rlm@0 547 strcmp(deviceName, allDevNameMap[idx].name) == 0)
rlm@0 548 {
rlm@0 549 if(idx > 0)
rlm@0 550 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", device_prefix,
rlm@0 551 allDevNameMap[idx].card, allDevNameMap[idx].dev);
rlm@0 552 break;
rlm@0 553 }
rlm@0 554 }
rlm@0 555 if(idx == numDevNames)
rlm@0 556 return ALC_FALSE;
rlm@0 557 }
rlm@0 558
rlm@0 559 data = (alsa_data*)calloc(1, sizeof(alsa_data));
rlm@0 560
rlm@0 561 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
rlm@0 562 if(i >= 0)
rlm@0 563 {
rlm@0 564 i = snd_pcm_nonblock(data->pcmHandle, 0);
rlm@0 565 if(i < 0)
rlm@0 566 snd_pcm_close(data->pcmHandle);
rlm@0 567 }
rlm@0 568 if(i < 0)
rlm@0 569 {
rlm@0 570 free(data);
rlm@0 571 ERR("Could not open playback device '%s': %s\n", driver, snd_strerror(i));
rlm@0 572 return ALC_FALSE;
rlm@0 573 }
rlm@0 574
rlm@0 575 device->szDeviceName = strdup(deviceName);
rlm@0 576 device->ExtraData = data;
rlm@0 577 return ALC_TRUE;
rlm@0 578 }
rlm@0 579
rlm@0 580 static void alsa_close_playback(ALCdevice *device)
rlm@0 581 {
rlm@0 582 alsa_data *data = (alsa_data*)device->ExtraData;
rlm@0 583
rlm@0 584 snd_pcm_close(data->pcmHandle);
rlm@0 585 free(data);
rlm@0 586 device->ExtraData = NULL;
rlm@0 587 }
rlm@0 588
rlm@0 589 static ALCboolean alsa_reset_playback(ALCdevice *device)
rlm@0 590 {
rlm@0 591 alsa_data *data = (alsa_data*)device->ExtraData;
rlm@0 592 snd_pcm_uframes_t periodSizeInFrames;
rlm@0 593 unsigned int periodLen, bufferLen;
rlm@0 594 snd_pcm_sw_params_t *sp = NULL;
rlm@0 595 snd_pcm_hw_params_t *p = NULL;
rlm@0 596 snd_pcm_access_t access;
rlm@0 597 snd_pcm_format_t format;
rlm@0 598 unsigned int periods;
rlm@0 599 unsigned int rate;
rlm@0 600 int allowmmap;
rlm@0 601 char *err;
rlm@0 602 int i;
rlm@0 603
rlm@0 604
rlm@0 605 format = -1;
rlm@0 606 switch(device->FmtType)
rlm@0 607 {
rlm@0 608 case DevFmtByte:
rlm@0 609 format = SND_PCM_FORMAT_S8;
rlm@0 610 break;
rlm@0 611 case DevFmtUByte:
rlm@0 612 format = SND_PCM_FORMAT_U8;
rlm@0 613 break;
rlm@0 614 case DevFmtShort:
rlm@0 615 format = SND_PCM_FORMAT_S16;
rlm@0 616 break;
rlm@0 617 case DevFmtUShort:
rlm@0 618 format = SND_PCM_FORMAT_U16;
rlm@0 619 break;
rlm@0 620 case DevFmtFloat:
rlm@0 621 format = SND_PCM_FORMAT_FLOAT;
rlm@0 622 break;
rlm@0 623 }
rlm@0 624
rlm@0 625 allowmmap = GetConfigValueBool("alsa", "mmap", 1);
rlm@0 626 periods = device->NumUpdates;
rlm@0 627 periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
rlm@0 628 bufferLen = periodLen * periods;
rlm@0 629 rate = device->Frequency;
rlm@0 630
rlm@0 631 err = NULL;
rlm@0 632 snd_pcm_hw_params_malloc(&p);
rlm@0 633
rlm@0 634 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
rlm@0 635 err = "any";
rlm@0 636 /* set interleaved access */
rlm@0 637 if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))
rlm@0 638 {
rlm@0 639 if(periods > 2)
rlm@0 640 {
rlm@0 641 periods--;
rlm@0 642 bufferLen = periodLen * periods;
rlm@0 643 }
rlm@0 644 if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
rlm@0 645 err = "set access";
rlm@0 646 }
rlm@0 647 /* set format (implicitly sets sample bits) */
rlm@0 648 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
rlm@0 649 {
rlm@0 650 device->FmtType = DevFmtFloat;
rlm@0 651 if(format == SND_PCM_FORMAT_FLOAT ||
rlm@0 652 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0)
rlm@0 653 {
rlm@0 654 device->FmtType = DevFmtShort;
rlm@0 655 if(format == SND_PCM_FORMAT_S16 ||
rlm@0 656 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0)
rlm@0 657 {
rlm@0 658 device->FmtType = DevFmtUByte;
rlm@0 659 if(format == SND_PCM_FORMAT_U8 ||
rlm@0 660 (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0)
rlm@0 661 err = "set format";
rlm@0 662 }
rlm@0 663 }
rlm@0 664 }
rlm@0 665 /* set channels (implicitly sets frame bits) */
rlm@0 666 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0)
rlm@0 667 {
rlm@0 668 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0)
rlm@0 669 {
rlm@0 670 if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0)
rlm@0 671 err = "set channels";
rlm@0 672 else
rlm@0 673 {
rlm@0 674 if((device->Flags&DEVICE_CHANNELS_REQUEST))
rlm@0 675 ERR("Failed to set %s, got Mono instead\n", DevFmtChannelsString(device->FmtChans));
rlm@0 676 device->FmtChans = DevFmtMono;
rlm@0 677 }
rlm@0 678 }
rlm@0 679 else
rlm@0 680 {
rlm@0 681 if((device->Flags&DEVICE_CHANNELS_REQUEST))
rlm@0 682 ERR("Failed to set %s, got Stereo instead\n", DevFmtChannelsString(device->FmtChans));
rlm@0 683 device->FmtChans = DevFmtStereo;
rlm@0 684 }
rlm@0 685 device->Flags &= ~DEVICE_CHANNELS_REQUEST;
rlm@0 686 }
rlm@0 687 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0)
rlm@0 688 {
rlm@0 689 ERR("Failed to disable ALSA resampler\n");
rlm@0 690 i = 0;
rlm@0 691 }
rlm@0 692 /* set rate (implicitly constrains period/buffer parameters) */
rlm@0 693 if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)
rlm@0 694 err = "set rate near";
rlm@0 695 /* set buffer time (implicitly constrains period/buffer parameters) */
rlm@0 696 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0)
rlm@0 697 err = "set buffer time near";
rlm@0 698 /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */
rlm@0 699 if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0)
rlm@0 700 err = "set period time near";
rlm@0 701 /* install and prepare hardware configuration */
rlm@0 702 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
rlm@0 703 err = "set params";
rlm@0 704 if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0)
rlm@0 705 err = "get access";
rlm@0 706 if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)
rlm@0 707 err = "get period size";
rlm@0 708 if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)
rlm@0 709 err = "get periods";
rlm@0 710 if(i < 0)
rlm@0 711 {
rlm@0 712 ERR("%s failed: %s\n", err, snd_strerror(i));
rlm@0 713 snd_pcm_hw_params_free(p);
rlm@0 714 return ALC_FALSE;
rlm@0 715 }
rlm@0 716
rlm@0 717 snd_pcm_hw_params_free(p);
rlm@0 718
rlm@0 719 err = NULL;
rlm@0 720 snd_pcm_sw_params_malloc(&sp);
rlm@0 721
rlm@0 722 if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)
rlm@0 723 err = "sw current";
rlm@0 724 if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)
rlm@0 725 err = "sw set avail min";
rlm@0 726 if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0)
rlm@0 727 err = "sw set params";
rlm@0 728 if(i != 0)
rlm@0 729 {
rlm@0 730 ERR("%s failed: %s\n", err, snd_strerror(i));
rlm@0 731 snd_pcm_sw_params_free(sp);
rlm@0 732 return ALC_FALSE;
rlm@0 733 }
rlm@0 734
rlm@0 735 snd_pcm_sw_params_free(sp);
rlm@0 736
rlm@0 737 if(device->Frequency != rate)
rlm@0 738 {
rlm@0 739 if((device->Flags&DEVICE_FREQUENCY_REQUEST))
rlm@0 740 ERR("Failed to set %dhz, got %dhz instead\n", device->Frequency, rate);
rlm@0 741 device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
rlm@0 742 device->Frequency = rate;
rlm@0 743 }
rlm@0 744
rlm@0 745 SetDefaultChannelOrder(device);
rlm@0 746
rlm@0 747 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
rlm@0 748 if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
rlm@0 749 {
rlm@0 750 /* Increase periods by one, since the temp buffer counts as an extra
rlm@0 751 * period */
rlm@0 752 periods++;
rlm@0 753 data->buffer = malloc(data->size);
rlm@0 754 if(!data->buffer)
rlm@0 755 {
rlm@0 756 ERR("buffer malloc failed\n");
rlm@0 757 return ALC_FALSE;
rlm@0 758 }
rlm@0 759 device->UpdateSize = periodSizeInFrames;
rlm@0 760 device->NumUpdates = periods;
rlm@0 761 data->thread = StartThread(ALSANoMMapProc, device);
rlm@0 762 }
rlm@0 763 else
rlm@0 764 {
rlm@0 765 i = snd_pcm_prepare(data->pcmHandle);
rlm@0 766 if(i < 0)
rlm@0 767 {
rlm@0 768 ERR("prepare error: %s\n", snd_strerror(i));
rlm@0 769 return ALC_FALSE;
rlm@0 770 }
rlm@0 771 device->UpdateSize = periodSizeInFrames;
rlm@0 772 device->NumUpdates = periods;
rlm@0 773 data->thread = StartThread(ALSAProc, device);
rlm@0 774 }
rlm@0 775 if(data->thread == NULL)
rlm@0 776 {
rlm@0 777 ERR("Could not create playback thread\n");
rlm@0 778 free(data->buffer);
rlm@0 779 data->buffer = NULL;
rlm@0 780 return ALC_FALSE;
rlm@0 781 }
rlm@0 782
rlm@0 783 return ALC_TRUE;
rlm@0 784 }
rlm@0 785
rlm@0 786 static void alsa_stop_playback(ALCdevice *device)
rlm@0 787 {
rlm@0 788 alsa_data *data = (alsa_data*)device->ExtraData;
rlm@0 789
rlm@0 790 if(data->thread)
rlm@0 791 {
rlm@0 792 data->killNow = 1;
rlm@0 793 StopThread(data->thread);
rlm@0 794 data->thread = NULL;
rlm@0 795 }
rlm@0 796 data->killNow = 0;
rlm@0 797 free(data->buffer);
rlm@0 798 data->buffer = NULL;
rlm@0 799 }
rlm@0 800
rlm@0 801
rlm@0 802 static ALCboolean alsa_open_capture(ALCdevice *pDevice, const ALCchar *deviceName)
rlm@0 803 {
rlm@0 804 snd_pcm_hw_params_t *p;
rlm@0 805 snd_pcm_uframes_t bufferSizeInFrames;
rlm@0 806 snd_pcm_format_t format;
rlm@0 807 ALuint frameSize;
rlm@0 808 alsa_data *data;
rlm@0 809 char driver[128];
rlm@0 810 char *err;
rlm@0 811 int i;
rlm@0 812
rlm@0 813 strncpy(driver, GetConfigValue("alsa", "capture", "default"), sizeof(driver)-1);
rlm@0 814 driver[sizeof(driver)-1] = 0;
rlm@0 815
rlm@0 816 if(!allCaptureDevNameMap)
rlm@0 817 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);
rlm@0 818
rlm@0 819 if(!deviceName)
rlm@0 820 deviceName = allCaptureDevNameMap[0].name;
rlm@0 821 else
rlm@0 822 {
rlm@0 823 size_t idx;
rlm@0 824
rlm@0 825 for(idx = 0;idx < numCaptureDevNames;idx++)
rlm@0 826 {
rlm@0 827 if(allCaptureDevNameMap[idx].name &&
rlm@0 828 strcmp(deviceName, allCaptureDevNameMap[idx].name) == 0)
rlm@0 829 {
rlm@0 830 if(idx > 0)
rlm@0 831 snprintf(driver, sizeof(driver), "%sCARD=%s,DEV=%d", capture_prefix,
rlm@0 832 allCaptureDevNameMap[idx].card, allCaptureDevNameMap[idx].dev);
rlm@0 833 break;
rlm@0 834 }
rlm@0 835 }
rlm@0 836 if(idx == numCaptureDevNames)
rlm@0 837 return ALC_FALSE;
rlm@0 838 }
rlm@0 839
rlm@0 840 data = (alsa_data*)calloc(1, sizeof(alsa_data));
rlm@0 841
rlm@0 842 i = snd_pcm_open(&data->pcmHandle, driver, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
rlm@0 843 if(i < 0)
rlm@0 844 {
rlm@0 845 ERR("Could not open capture device '%s': %s\n", driver, snd_strerror(i));
rlm@0 846 free(data);
rlm@0 847 return ALC_FALSE;
rlm@0 848 }
rlm@0 849
rlm@0 850 format = -1;
rlm@0 851 switch(pDevice->FmtType)
rlm@0 852 {
rlm@0 853 case DevFmtByte:
rlm@0 854 format = SND_PCM_FORMAT_S8;
rlm@0 855 break;
rlm@0 856 case DevFmtUByte:
rlm@0 857 format = SND_PCM_FORMAT_U8;
rlm@0 858 break;
rlm@0 859 case DevFmtShort:
rlm@0 860 format = SND_PCM_FORMAT_S16;
rlm@0 861 break;
rlm@0 862 case DevFmtUShort:
rlm@0 863 format = SND_PCM_FORMAT_U16;
rlm@0 864 break;
rlm@0 865 case DevFmtFloat:
rlm@0 866 format = SND_PCM_FORMAT_FLOAT;
rlm@0 867 break;
rlm@0 868 }
rlm@0 869
rlm@0 870 err = NULL;
rlm@0 871 bufferSizeInFrames = pDevice->UpdateSize * pDevice->NumUpdates;
rlm@0 872 snd_pcm_hw_params_malloc(&p);
rlm@0 873
rlm@0 874 if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
rlm@0 875 err = "any";
rlm@0 876 /* set interleaved access */
rlm@0 877 if(i >= 0 && (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
rlm@0 878 err = "set access";
rlm@0 879 /* set format (implicitly sets sample bits) */
rlm@0 880 if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
rlm@0 881 err = "set format";
rlm@0 882 /* set channels (implicitly sets frame bits) */
rlm@0 883 if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(pDevice->FmtChans))) < 0)
rlm@0 884 err = "set channels";
rlm@0 885 /* set rate (implicitly constrains period/buffer parameters) */
rlm@0 886 if(i >= 0 && (i=snd_pcm_hw_params_set_rate(data->pcmHandle, p, pDevice->Frequency, 0)) < 0)
rlm@0 887 err = "set rate near";
rlm@0 888 /* set buffer size in frame units (implicitly sets period size/bytes/time and buffer time/bytes) */
rlm@0 889 if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_size_near(data->pcmHandle, p, &bufferSizeInFrames)) < 0)
rlm@0 890 err = "set buffer size near";
rlm@0 891 /* install and prepare hardware configuration */
rlm@0 892 if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
rlm@0 893 err = "set params";
rlm@0 894 if(i < 0)
rlm@0 895 {
rlm@0 896 ERR("%s failed: %s\n", err, snd_strerror(i));
rlm@0 897 snd_pcm_hw_params_free(p);
rlm@0 898 goto error;
rlm@0 899 }
rlm@0 900
rlm@0 901 if((i=snd_pcm_hw_params_get_period_size(p, &bufferSizeInFrames, NULL)) < 0)
rlm@0 902 {
rlm@0 903 ERR("get size failed: %s\n", snd_strerror(i));
rlm@0 904 snd_pcm_hw_params_free(p);
rlm@0 905 goto error;
rlm@0 906 }
rlm@0 907
rlm@0 908 snd_pcm_hw_params_free(p);
rlm@0 909
rlm@0 910 frameSize = FrameSizeFromDevFmt(pDevice->FmtChans, pDevice->FmtType);
rlm@0 911
rlm@0 912 data->ring = CreateRingBuffer(frameSize, pDevice->UpdateSize*pDevice->NumUpdates);
rlm@0 913 if(!data->ring)
rlm@0 914 {
rlm@0 915 ERR("ring buffer create failed\n");
rlm@0 916 goto error;
rlm@0 917 }
rlm@0 918
rlm@0 919 data->size = snd_pcm_frames_to_bytes(data->pcmHandle, bufferSizeInFrames);
rlm@0 920 data->buffer = malloc(data->size);
rlm@0 921 if(!data->buffer)
rlm@0 922 {
rlm@0 923 ERR("buffer malloc failed\n");
rlm@0 924 goto error;
rlm@0 925 }
rlm@0 926
rlm@0 927 pDevice->szDeviceName = strdup(deviceName);
rlm@0 928
rlm@0 929 pDevice->ExtraData = data;
rlm@0 930 return ALC_TRUE;
rlm@0 931
rlm@0 932 error:
rlm@0 933 free(data->buffer);
rlm@0 934 DestroyRingBuffer(data->ring);
rlm@0 935 snd_pcm_close(data->pcmHandle);
rlm@0 936 free(data);
rlm@0 937
rlm@0 938 pDevice->ExtraData = NULL;
rlm@0 939 return ALC_FALSE;
rlm@0 940 }
rlm@0 941
rlm@0 942 static void alsa_close_capture(ALCdevice *pDevice)
rlm@0 943 {
rlm@0 944 alsa_data *data = (alsa_data*)pDevice->ExtraData;
rlm@0 945
rlm@0 946 snd_pcm_close(data->pcmHandle);
rlm@0 947 DestroyRingBuffer(data->ring);
rlm@0 948
rlm@0 949 free(data->buffer);
rlm@0 950 free(data);
rlm@0 951 pDevice->ExtraData = NULL;
rlm@0 952 }
rlm@0 953
rlm@0 954 static void alsa_start_capture(ALCdevice *Device)
rlm@0 955 {
rlm@0 956 alsa_data *data = (alsa_data*)Device->ExtraData;
rlm@0 957 int err;
rlm@0 958
rlm@0 959 err = snd_pcm_start(data->pcmHandle);
rlm@0 960 if(err < 0)
rlm@0 961 {
rlm@0 962 ERR("start failed: %s\n", snd_strerror(err));
rlm@0 963 aluHandleDisconnect(Device);
rlm@0 964 }
rlm@0 965 else
rlm@0 966 data->doCapture = AL_TRUE;
rlm@0 967 }
rlm@0 968
rlm@0 969 static void alsa_stop_capture(ALCdevice *Device)
rlm@0 970 {
rlm@0 971 alsa_data *data = (alsa_data*)Device->ExtraData;
rlm@0 972 snd_pcm_drain(data->pcmHandle);
rlm@0 973 data->doCapture = AL_FALSE;
rlm@0 974 }
rlm@0 975
rlm@0 976 static ALCuint alsa_available_samples(ALCdevice *Device)
rlm@0 977 {
rlm@0 978 alsa_data *data = (alsa_data*)Device->ExtraData;
rlm@0 979 snd_pcm_sframes_t avail;
rlm@0 980
rlm@0 981 avail = (Device->Connected ? snd_pcm_avail_update(data->pcmHandle) : 0);
rlm@0 982 if(avail < 0)
rlm@0 983 {
rlm@0 984 ERR("avail update failed: %s\n", snd_strerror(avail));
rlm@0 985
rlm@0 986 if((avail=snd_pcm_recover(data->pcmHandle, avail, 1)) >= 0)
rlm@0 987 {
rlm@0 988 if(data->doCapture)
rlm@0 989 avail = snd_pcm_start(data->pcmHandle);
rlm@0 990 if(avail >= 0)
rlm@0 991 avail = snd_pcm_avail_update(data->pcmHandle);
rlm@0 992 }
rlm@0 993 if(avail < 0)
rlm@0 994 {
rlm@0 995 ERR("restore error: %s\n", snd_strerror(avail));
rlm@0 996 aluHandleDisconnect(Device);
rlm@0 997 }
rlm@0 998 }
rlm@0 999 while(avail > 0)
rlm@0 1000 {
rlm@0 1001 snd_pcm_sframes_t amt;
rlm@0 1002
rlm@0 1003 amt = snd_pcm_bytes_to_frames(data->pcmHandle, data->size);
rlm@0 1004 if(avail < amt) amt = avail;
rlm@0 1005
rlm@0 1006 amt = snd_pcm_readi(data->pcmHandle, data->buffer, amt);
rlm@0 1007 if(amt < 0)
rlm@0 1008 {
rlm@0 1009 ERR("read error: %s\n", snd_strerror(amt));
rlm@0 1010
rlm@0 1011 if(amt == -EAGAIN)
rlm@0 1012 continue;
rlm@0 1013 if((amt=snd_pcm_recover(data->pcmHandle, amt, 1)) >= 0)
rlm@0 1014 {
rlm@0 1015 if(data->doCapture)
rlm@0 1016 amt = snd_pcm_start(data->pcmHandle);
rlm@0 1017 if(amt >= 0)
rlm@0 1018 amt = snd_pcm_avail_update(data->pcmHandle);
rlm@0 1019 }
rlm@0 1020 if(amt < 0)
rlm@0 1021 {
rlm@0 1022 ERR("restore error: %s\n", snd_strerror(amt));
rlm@0 1023 aluHandleDisconnect(Device);
rlm@0 1024 break;
rlm@0 1025 }
rlm@0 1026 avail = amt;
rlm@0 1027 continue;
rlm@0 1028 }
rlm@0 1029
rlm@0 1030 WriteRingBuffer(data->ring, data->buffer, amt);
rlm@0 1031 avail -= amt;
rlm@0 1032 }
rlm@0 1033
rlm@0 1034 return RingBufferSize(data->ring);
rlm@0 1035 }
rlm@0 1036
rlm@0 1037 static void alsa_capture_samples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
rlm@0 1038 {
rlm@0 1039 alsa_data *data = (alsa_data*)Device->ExtraData;
rlm@0 1040
rlm@0 1041 if(Samples <= alsa_available_samples(Device))
rlm@0 1042 ReadRingBuffer(data->ring, Buffer, Samples);
rlm@0 1043 else
rlm@0 1044 alcSetError(Device, ALC_INVALID_VALUE);
rlm@0 1045 }
rlm@0 1046
rlm@0 1047
rlm@0 1048 static const BackendFuncs alsa_funcs = {
rlm@0 1049 alsa_open_playback,
rlm@0 1050 alsa_close_playback,
rlm@0 1051 alsa_reset_playback,
rlm@0 1052 alsa_stop_playback,
rlm@0 1053 alsa_open_capture,
rlm@0 1054 alsa_close_capture,
rlm@0 1055 alsa_start_capture,
rlm@0 1056 alsa_stop_capture,
rlm@0 1057 alsa_capture_samples,
rlm@0 1058 alsa_available_samples
rlm@0 1059 };
rlm@0 1060
rlm@0 1061 ALCboolean alc_alsa_init(BackendFuncs *func_list)
rlm@0 1062 {
rlm@0 1063 if(!alsa_load())
rlm@0 1064 return ALC_FALSE;
rlm@0 1065 device_prefix = GetConfigValue("alsa", "device-prefix", "plughw:");
rlm@0 1066 capture_prefix = GetConfigValue("alsa", "capture-prefix", "plughw:");
rlm@0 1067 *func_list = alsa_funcs;
rlm@0 1068 return ALC_TRUE;
rlm@0 1069 }
rlm@0 1070
rlm@0 1071 void alc_alsa_deinit(void)
rlm@0 1072 {
rlm@0 1073 ALuint i;
rlm@0 1074
rlm@0 1075 for(i = 0;i < numDevNames;++i)
rlm@0 1076 {
rlm@0 1077 free(allDevNameMap[i].name);
rlm@0 1078 free(allDevNameMap[i].card);
rlm@0 1079 }
rlm@0 1080 free(allDevNameMap);
rlm@0 1081 allDevNameMap = NULL;
rlm@0 1082 numDevNames = 0;
rlm@0 1083
rlm@0 1084 for(i = 0;i < numCaptureDevNames;++i)
rlm@0 1085 {
rlm@0 1086 free(allCaptureDevNameMap[i].name);
rlm@0 1087 free(allCaptureDevNameMap[i].card);
rlm@0 1088 }
rlm@0 1089 free(allCaptureDevNameMap);
rlm@0 1090 allCaptureDevNameMap = NULL;
rlm@0 1091 numCaptureDevNames = 0;
rlm@0 1092
rlm@0 1093 #ifdef HAVE_DYNLOAD
rlm@0 1094 if(alsa_handle)
rlm@0 1095 CloseLib(alsa_handle);
rlm@0 1096 alsa_handle = NULL;
rlm@0 1097 #endif
rlm@0 1098 }
rlm@0 1099
rlm@0 1100 void alc_alsa_probe(enum DevProbe type)
rlm@0 1101 {
rlm@0 1102 ALuint i;
rlm@0 1103
rlm@0 1104 switch(type)
rlm@0 1105 {
rlm@0 1106 case DEVICE_PROBE:
rlm@0 1107 AppendDeviceList(alsaDevice);
rlm@0 1108 break;
rlm@0 1109
rlm@0 1110 case ALL_DEVICE_PROBE:
rlm@0 1111 for(i = 0;i < numDevNames;++i)
rlm@0 1112 {
rlm@0 1113 free(allDevNameMap[i].name);
rlm@0 1114 free(allDevNameMap[i].card);
rlm@0 1115 }
rlm@0 1116
rlm@0 1117 free(allDevNameMap);
rlm@0 1118 allDevNameMap = probe_devices(SND_PCM_STREAM_PLAYBACK, &numDevNames);
rlm@0 1119
rlm@0 1120 for(i = 0;i < numDevNames;++i)
rlm@0 1121 AppendAllDeviceList(allDevNameMap[i].name);
rlm@0 1122 break;
rlm@0 1123
rlm@0 1124 case CAPTURE_DEVICE_PROBE:
rlm@0 1125 for(i = 0;i < numCaptureDevNames;++i)
rlm@0 1126 {
rlm@0 1127 free(allCaptureDevNameMap[i].name);
rlm@0 1128 free(allCaptureDevNameMap[i].card);
rlm@0 1129 }
rlm@0 1130
rlm@0 1131 free(allCaptureDevNameMap);
rlm@0 1132 allCaptureDevNameMap = probe_devices(SND_PCM_STREAM_CAPTURE, &numCaptureDevNames);
rlm@0 1133
rlm@0 1134 for(i = 0;i < numCaptureDevNames;++i)
rlm@0 1135 AppendCaptureDeviceList(allCaptureDevNameMap[i].name);
rlm@0 1136 break;
rlm@0 1137 }
rlm@0 1138 }