openal-alsa-creative/src/alc_device.c

228 lines
5.6 KiB
C

/*
* 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 <sys/types.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <ansidecl.h>
#include <alsa/asoundlib.h>
#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);
}
}