/* * Copyright (C) 2004 Christopher John Purnell * cjp@lost.org.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include "alc_device.h" #include "alc_context.h" #include "alc_error.h" #define _ALC_DEF_FREQ 44100 #define _ALC_DEF_REFRESH 20 #define _ALC_NUM_PERIODS 2 static int live_send_routing[] = { 0, 1, 2, 3, 0, 2, 6, 7, 1, 3, 6, 7 }; static int audigy_send_routing[] = { 8, 9, 2, 3, 6, 7, 14, 15, 8, 9, 2, 3, 6, 7, 14, 15, 8, 9, 2, 3, 6, 7, 14, 15 }; ALCboolean _alcOpenSource(AL_source *src) { snd_pcm_hw_params_t *hw_params; snd_pcm_info_t *info; snd_ctl_elem_value_t *route_ctl; snd_pcm_uframes_t size; unsigned i; AL_context *ctx = src->context; ALCdevice *dev = ctx->device; int *send_routing; if (snd_pcm_open(&src->handle, dev->device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) return ALC_FALSE; snd_pcm_hw_params_alloca(&hw_params); if (snd_pcm_hw_params_any(src->handle, hw_params)) return ALC_FALSE; if (snd_pcm_hw_params_set_access(src->handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED)) return ALC_FALSE; if (snd_pcm_hw_params_set_format(src->handle, hw_params, SND_PCM_FORMAT_S16)) return ALC_FALSE; if (snd_pcm_hw_params_set_channels(src->handle, hw_params, 2)) return ALC_FALSE; src->freq = dev->freq; if (snd_pcm_hw_params_set_rate_near(src->handle, hw_params, &src->freq, 0)) return ALC_FALSE; if (snd_pcm_hw_params_set_periods(src->handle, hw_params, _ALC_NUM_PERIODS, 0)) return ALC_FALSE; size = src->freq * _ALC_NUM_PERIODS / dev->refresh; if (snd_pcm_hw_params_set_buffer_size_near(src->handle, hw_params, &size)) return ALC_FALSE; if (snd_pcm_hw_params(src->handle, hw_params)) return ALC_FALSE; snd_pcm_info_alloca(&info); if (snd_pcm_info(src->handle, info)) return ALC_FALSE; src->subdev = snd_pcm_info_get_subdevice(info); if (snd_ctl_elem_value_malloc(&src->vol_ctl)) return ALC_FALSE; snd_ctl_elem_value_clear(src->vol_ctl); snd_ctl_elem_value_set_interface(src->vol_ctl, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(src->vol_ctl, "EMU10K1 PCM Volume"); snd_ctl_elem_value_set_index(src->vol_ctl, src->subdev); if (snd_ctl_elem_value_malloc(&src->send_ctl)) return ALC_FALSE; snd_ctl_elem_value_clear(src->send_ctl); snd_ctl_elem_value_set_interface(src->send_ctl, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(src->send_ctl, "EMU10K1 PCM Send Volume"); snd_ctl_elem_value_set_index(src->send_ctl, src->subdev); snd_ctl_elem_value_alloca(&route_ctl); snd_ctl_elem_value_clear(route_ctl); snd_ctl_elem_value_set_interface(route_ctl, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_value_set_name(route_ctl, "EMU10K1 PCM Send Routing"); snd_ctl_elem_value_set_index(route_ctl, src->subdev); send_routing = (dev->send_count == 24) ? audigy_send_routing : live_send_routing; for (i = 0; i < dev->send_count; i++) { snd_ctl_elem_value_set_integer(route_ctl, i, send_routing[i]); snd_ctl_elem_value_set_integer(src->send_ctl, i, 0); } if (snd_ctl_elem_write(dev->ctl, route_ctl)) return ALC_FALSE; return ALC_TRUE; } ALCvoid _alcCloseSource(AL_source *src) { if (src->send_ctl) snd_ctl_elem_value_free(src->send_ctl); if (src->vol_ctl) snd_ctl_elem_value_free(src->vol_ctl); if (src->handle) snd_pcm_close(src->handle); } static ALCboolean _alcOpenDevice(ALCdevice *dev) { snd_ctl_elem_info_t *ctl_info; snd_pcm_info_t *pcm_info; int card = -1; snd_ctl_elem_info_alloca(&ctl_info); snd_ctl_elem_info_set_interface(ctl_info, SND_CTL_ELEM_IFACE_MIXER); snd_ctl_elem_info_set_name(ctl_info, "EMU10K1 PCM Send Routing"); snd_ctl_elem_info_set_index(ctl_info, 0); snd_pcm_info_alloca(&pcm_info); while (snd_card_next(&card) >= 0 && card >= 0) { sprintf(dev->device, "hw:%d", card); if (snd_ctl_open(&dev->ctl, dev->device, 0)) break; if (snd_ctl_elem_info(dev->ctl, ctl_info)) continue; dev->send_count = snd_ctl_elem_info_get_count(ctl_info); if ((dev->send_count != 12) && (dev->send_count != 24)) continue; if (snd_ctl_pcm_info(dev->ctl, pcm_info)) break; dev->subdevs = snd_pcm_info_get_subdevices_count(pcm_info); return ALC_TRUE; } return ALC_FALSE; } static ALCvoid _alcCloseDevice(ALCdevice *dev) { if (dev->ctl) snd_ctl_close(dev->ctl); free(dev); } ALCdevice *alcOpenDevice(const ALubyte *spec ATTRIBUTE_UNUSED) { ALCdevice *dev; if (!(dev = malloc(sizeof(ALCdevice)))) { _alcSetError(ALC_OUT_OF_MEMORY); return 0; } dev->ctl = 0; dev->sync = ALC_FALSE; dev->freq = _ALC_DEF_FREQ; dev->refresh = _ALC_DEF_REFRESH; if (_alcOpenDevice(dev)) return dev; _alcCloseDevice(dev); _alcSetError(ALC_INVALID_DEVICE); return 0; } ALCvoid alcCloseDevice(ALCdevice *dev) { if (dev) { _alcCloseDevice(dev); } else { _alcSetError(ALC_INVALID_DEVICE); } }