228 lines
5.6 KiB
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);
|
|
}
|
|
}
|