mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-14 05:55:41 +01:00
5bf715371d
code to update the mixer interface soon.
386 lines
13 KiB
C
386 lines
13 KiB
C
/*****************************************************************************
|
|
cinit.c - routines to initialize the mixer devices
|
|
Written by Raistlinn (lansdoct@cs.alfred.edu)
|
|
Copyright (C) 1998 by Christopher Lansdown
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
******************************************************************************/
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Begin #include's */
|
|
|
|
#include "main.h"
|
|
|
|
/* End #include's */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* Begin Global Variables */
|
|
|
|
extern Card *card; /* And array of the cards */
|
|
extern int cards; /* The number of cards in the system. */
|
|
extern Config config; /* The system config */
|
|
|
|
/* End Global Variables */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* Begin function prototypes */
|
|
|
|
int init_group(void *handle, Group *group);
|
|
int init_element_route(void *handle, snd_mixer_routes_t *routes, snd_mixer_eid_t *eid);
|
|
int misc_group_hack(Mixer *mixer, int index);
|
|
|
|
/* End function prototypes */
|
|
/*****************************************************************************/
|
|
|
|
int init_cards()
|
|
{
|
|
int i,j,k;
|
|
int err;
|
|
snd_ctl_t *handle;
|
|
|
|
cards = snd_cards();
|
|
card = calloc(cards, sizeof(Card));
|
|
for(i = 0; i < cards; i++) {
|
|
/* Open the hardware */
|
|
if((err = snd_ctl_open(&handle, i)) < 0) {
|
|
printf("Unable to open card #%i!\nError: %s\n", i, snd_strerror(err));
|
|
card[i].mixer = NULL;
|
|
card[i].number = -1;
|
|
continue;
|
|
} else {
|
|
card[i].number = i;
|
|
}
|
|
|
|
|
|
/* Get the hardware info - the primary use of this is to find out how many
|
|
mixer devices the card has, but it's also useful to find out the human-readable
|
|
name of the card. */
|
|
if((err = snd_ctl_hw_info(handle, &card[i].hw_info)) < 0) {
|
|
printf("Unable to get hardware information about card #%i!\nError: %s\n",
|
|
i, snd_strerror(err));
|
|
printf("Trying to guess the appropriate values.\n");
|
|
card[i].hw_info.mixerdevs = 1;
|
|
}
|
|
|
|
card[i].nmixers = card[i].hw_info.mixerdevs;
|
|
|
|
/* Allocate out the mixer array */
|
|
card[i].mixer = calloc(card[i].hw_info.mixerdevs, sizeof(Mixer));
|
|
|
|
for(j = 0; j < card[i].hw_info.mixerdevs; j++) {
|
|
/* Open the mixer to begin with. Isn't it funny how there's all this
|
|
nice generalized code that can handle nearly any situation, and it
|
|
will be necessary in only a very small percentage of the situations.
|
|
Oh well, I guess that that's what distinguishes us from windows. :-) */
|
|
if((err = snd_mixer_open(&card[i].mixer[j].handle, i, j)) < 0) {
|
|
printf("Unable to open mixer #%i on card #%i~\nError: %s\n",
|
|
j, i, snd_strerror(err));
|
|
card[i].mixer[j].number = -1;
|
|
} else {
|
|
card[i].mixer[j].number = j;
|
|
}
|
|
|
|
/* Get the mixer info */
|
|
if((err = snd_mixer_info(card[i].mixer[j].handle, &card[i].mixer[j].info)) < 0) {
|
|
printf("Unable to get the info for mixer #%i on card %i! Error: %s\n", j, i, snd_strerror(err));
|
|
printf("There's not much more I can do on this mixer.");
|
|
printf(" Shutting it down.\n");
|
|
if((err = snd_mixer_close(card[i].mixer[j].handle)) < 0) {
|
|
printf("Oh well. I couldn't even close the mixer. I suspect that something is seriously wrong here. Good luck.\n");
|
|
}
|
|
card[i].mixer[j].number = -1;
|
|
continue;
|
|
}
|
|
|
|
bzero(&card[i].mixer[j].groups, sizeof(snd_mixer_groups_t));
|
|
|
|
if ((err = snd_mixer_groups(card[i].mixer[j].handle,
|
|
&card[i].mixer[j].groups)) < 0) {
|
|
printf("Mixer %i/%i groups error: %s",
|
|
i, j, snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
/* Allocate the space for the group array */
|
|
card[i].mixer[j].groups.pgroups = (snd_mixer_gid_t *)
|
|
calloc(card[i].mixer[j].groups.groups_over,
|
|
sizeof(snd_mixer_gid_t));
|
|
|
|
if (!card[i].mixer[j].groups.pgroups) {
|
|
printf("No enough memory");
|
|
return -1;
|
|
}
|
|
|
|
card[i].mixer[j].groups.groups_size = card[i].mixer[j].info.groups;
|
|
card[i].mixer[j].groups.groups_over = card[i].mixer[j].groups.groups = 0;
|
|
if ((err = snd_mixer_groups(card[i].mixer[j].handle,
|
|
&card[i].mixer[j].groups)) < 0) {
|
|
printf("Mixer %i/%i groups (2) error: %s",
|
|
i, j, snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
/* Allocate the space for the array of the groups - this is more than
|
|
just their gid's, it's got group-specific info in it */
|
|
card[i].mixer[j].group = calloc(card[i].mixer[j].info.groups + 1,
|
|
sizeof(Group));
|
|
|
|
/* get the group structures filled out */
|
|
for(k = 0; k < card[i].mixer[j].info.groups; k++) {
|
|
card[i].mixer[j].group[k].group.gid =
|
|
card[i].mixer[j].groups.pgroups[k];
|
|
|
|
init_group(card[i].mixer[j].handle,
|
|
&card[i].mixer[j].group[k]);
|
|
|
|
}
|
|
misc_group_hack(&card[i].mixer[j], k);
|
|
|
|
}
|
|
|
|
|
|
if((err = snd_ctl_close(handle)) < 0) {
|
|
printf("strange, there was an error closing card #%i!\nError: %s\n",
|
|
i, snd_strerror(err));
|
|
printf("Oh well.\n");
|
|
}
|
|
}
|
|
|
|
|
|
/* return a successful execution. */
|
|
return 0;
|
|
}
|
|
|
|
int misc_group_hack(Mixer *mixer, int index)
|
|
{
|
|
/* This code is largely copied straight from amixer. - God I love the GPL. */
|
|
snd_mixer_elements_t elements;
|
|
snd_mixer_eid_t *element;
|
|
snd_mixer_group_t *group;
|
|
int err, idx, gdx, idx2;
|
|
int flag;
|
|
int count=0; /* The count of elements not in any group */
|
|
snd_mixer_eid_t **array;
|
|
|
|
bzero(&elements, sizeof(elements));
|
|
if ((err = snd_mixer_elements(mixer->handle, &elements)) < 0) {
|
|
printf("Mixer elements error: %s", snd_strerror(err));
|
|
return -1;
|
|
}
|
|
elements.pelements = (snd_mixer_eid_t *)malloc(elements.elements_over *
|
|
sizeof(snd_mixer_eid_t));
|
|
if (!elements.pelements) {
|
|
printf("Not enough memory");
|
|
return -1;
|
|
}
|
|
elements.elements_size = elements.elements_over;
|
|
elements.elements_over = elements.elements = 0;
|
|
if ((err = snd_mixer_elements(mixer->handle, &elements)) < 0) {
|
|
printf("Mixer elements (2) error: %s", snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
/* Allocate the temporary array to hold the mixer ID structs */
|
|
array = malloc(elements.elements * sizeof(snd_mixer_eid_t *));
|
|
if(!array)
|
|
printf("Not enough memory.\n");
|
|
|
|
|
|
for (idx = 0; idx < elements.elements; idx++) {
|
|
element = &elements.pelements[idx];
|
|
flag = 0; /* The flag will be set if the same element name & type
|
|
is encountered */
|
|
for(gdx = 0; gdx < mixer->info.groups; gdx++) {
|
|
group = &mixer->group[gdx].group;
|
|
for(idx2 = 0; idx2 < group->elements; idx2++) {
|
|
if(group && element)
|
|
if(group->pelements[idx2].type == element->type &&
|
|
is_same(group->pelements[idx2].name, element->name))
|
|
flag = 1;
|
|
}
|
|
}
|
|
if(!flag) {
|
|
/* We found a mixer element that's not in a group */
|
|
array[count] = element;
|
|
count++;
|
|
if(count > elements.elements)
|
|
printf("Houston, we have a problem.\n");
|
|
}
|
|
}
|
|
|
|
/* Set up the group member */
|
|
strncpy(mixer->group[index].group.gid.name, "Miscellaneous\0", 24);
|
|
mixer->group[index].group.gid.index = 0;
|
|
mixer->group[index].group.elements_size = 0; /* I hope that this doesn't matter */
|
|
mixer->group[index].group.elements = count;
|
|
mixer->group[index].group.elements_over = 0; /* I hope tha this doesn't matter */
|
|
|
|
|
|
mixer->group[index].group.pelements = (snd_mixer_eid_t *)malloc(count *
|
|
sizeof(snd_mixer_eid_t));
|
|
mixer->group[index].routes = calloc(mixer->group[index].group.elements,
|
|
sizeof(snd_mixer_routes_t));
|
|
mixer->group[index].element = calloc(mixer->group[index].group.elements,
|
|
sizeof(snd_mixer_element_t));
|
|
mixer->group[index].einfo = calloc(mixer->group[index].group.elements,
|
|
sizeof(snd_mixer_element_info_t));
|
|
mixer->group[index].gtk = calloc(mixer->group[index].group.elements,
|
|
sizeof(Gtk_Channel));
|
|
/* Copy the snd_mixer_eid_t structures into the new group structure and init the routes */
|
|
|
|
for(idx = 0; idx < count; idx++) {
|
|
mixer->group[index].group.pelements[idx] = *array[idx];
|
|
|
|
mixer->group[index].einfo[idx].eid = mixer->group[index].group.pelements[idx];
|
|
if(snd_mixer_element_has_info(&mixer->group[index].group.pelements[idx]) == 1)
|
|
if((err =
|
|
snd_mixer_element_info_build(mixer->handle,
|
|
&mixer->group[index].einfo[idx])) < 0) {
|
|
printf("Unable to get element information for element %s! ",
|
|
mixer->group[index].group.pelements[idx].name);
|
|
printf("Error: %s.\n", snd_strerror(err));
|
|
}
|
|
|
|
mixer->group[index].element[idx].eid = mixer->group[index].group.pelements[idx];
|
|
if(snd_mixer_element_has_control(&mixer->group[index].element[idx].eid))
|
|
if((err = snd_mixer_element_build(mixer->handle,
|
|
&mixer->group[index].element[idx])) < 0) {
|
|
printf("Unable to read element %s! ",
|
|
mixer->group[index].group.pelements[idx].name);
|
|
printf("Error: %s.\n", snd_strerror(err));
|
|
}
|
|
|
|
init_element_route(mixer->handle,
|
|
&mixer->group[index].routes[idx],
|
|
&mixer->group[index].group.pelements[idx]);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Increase the number of groups to include the new group */
|
|
mixer->info.groups++;
|
|
|
|
if(elements.pelements)
|
|
free(elements.pelements);
|
|
if(array)
|
|
free(array);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int init_group(void *handle, Group *group)
|
|
{
|
|
/* This is largely a mess copied from amixer that gets the group info in a very strange
|
|
way, I wish that I knew how it really worked. Anyhow, once we get the group into
|
|
and the info about the elements in the group, we'll set up the element array. */
|
|
int idx, err;
|
|
|
|
if((err = snd_mixer_group_read(handle,
|
|
&group->group)) < 0) {
|
|
printf("Unable to get info for group %s! ", group->group.gid.name);
|
|
printf("Error: %s\n", snd_strerror(err));
|
|
printf("elements_size = %i, elements_over=%i, elements=%i\n",
|
|
group->group.elements_size,
|
|
group->group.elements_over,
|
|
group->group.elements);
|
|
return 0;
|
|
}
|
|
group->group.pelements = (snd_mixer_eid_t *)calloc(group->group.elements_over,
|
|
sizeof(snd_mixer_eid_t));
|
|
if (!group->group.pelements) {
|
|
printf("Not enough memory...");
|
|
return 0;
|
|
}
|
|
group->group.elements_size = group->group.elements_over;
|
|
group->group.elements = group->group.elements_over = 0;
|
|
if ((err = snd_mixer_group_read(handle, &group->group)) < 0) {
|
|
printf("Unable to get second group info for group %s. Error: %s\n",
|
|
group->group.gid.name, snd_strerror(err));
|
|
printf("elements_size = %i, elements_over=%i, elements=%i\n",
|
|
group->group.elements_size,
|
|
group->group.elements_over,
|
|
group->group.elements);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Allocate out the arrays for the elements and element info */
|
|
group->element = calloc(group->group.elements, sizeof(snd_mixer_element_t));
|
|
group->einfo = calloc(group->group.elements, sizeof(snd_mixer_element_info_t));
|
|
group->routes = calloc(group->group.elements, sizeof(snd_mixer_routes_t));
|
|
group->gtk = calloc(group->group.elements, sizeof(Gtk_Channel));
|
|
|
|
/* Now go through and get that info */
|
|
for (idx = 0; idx < group->group.elements; idx++) {
|
|
group->einfo[idx].eid = group->group.pelements[idx];
|
|
if(snd_mixer_element_has_info(&group->group.pelements[idx]) == 1)
|
|
if((err = snd_mixer_element_info_build(handle, &group->einfo[idx])) < 0) {
|
|
printf("Unable to get element information for element %s! ",
|
|
group->group.pelements[idx].name);
|
|
printf("Error: %s.\n", snd_strerror(err));
|
|
}
|
|
|
|
group->element[idx].eid = group->group.pelements[idx];
|
|
if((err = snd_mixer_element_build(handle, &group->element[idx])) < 0) {
|
|
printf("Unable to read element %s! ",
|
|
group->group.pelements[idx].name);
|
|
printf("Error: %s.\n", snd_strerror(err));
|
|
}
|
|
|
|
init_element_route(handle, &group->routes[idx], &group->group.pelements[idx]);
|
|
|
|
}
|
|
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
int init_element_route(void *handle, snd_mixer_routes_t *routes, snd_mixer_eid_t *eid)
|
|
{
|
|
int err, idx;
|
|
/* Most of this code is taken straight from amixer as well. */
|
|
/* This just gets the routes for the mixer element and stores them. */
|
|
|
|
bzero(routes, sizeof(snd_mixer_routes_t));
|
|
routes->eid = *eid;
|
|
if ((err = snd_mixer_routes(handle, routes)) < 0) {
|
|
printf("Element %s route error: %s", eid->name, snd_strerror(err));
|
|
return -1;
|
|
}
|
|
if (!routes->routes_over)
|
|
return 0;
|
|
routes->proutes = (snd_mixer_eid_t *)malloc(routes->routes_over *
|
|
sizeof(snd_mixer_eid_t));
|
|
if (!routes->proutes) {
|
|
printf("No enough memory...");
|
|
return -1;
|
|
}
|
|
routes->routes_size = routes->routes_over;
|
|
routes->routes = routes->routes_over = 0;
|
|
if ((err = snd_mixer_routes(handle, routes)) < 0) {
|
|
printf("Element (2) %s route error: %s", eid->name, snd_strerror(err));
|
|
return -1;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|