alsa-utils/alsactl/merge.c
2000-05-10 09:36:35 +00:00

520 lines
20 KiB
C

/*
* Advanced Linux Sound Architecture Control Program
* Copyright (c) 1999 by Jaroslav Kysela <perex@suse.cz>
*
*
* 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 "alsactl.h"
static char *sw_id(const char *name, int cardno, int devno, const char *id)
{
static char str[256];
sprintf(str, "%s %s card %i", name, id, cardno);
if (devno >= 0)
sprintf(str + strlen(str)," device %i", devno);
return str;
}
static int merge_one_sw(struct ctl_switch *csw, struct ctl_switch *usw, int cardno, int devno, const char *id)
{
switch (csw->s.type) {
case SND_SW_TYPE_BOOLEAN:
if (usw->s.type != SND_SW_TYPE_BOOLEAN) {
error("A wrong type for the switch %s. The type boolean is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.value.enable != usw->s.value.enable) {
csw->change = 1;
csw->s.value.enable = usw->s.value.enable;
}
if (!strncmp(csw->s.name, SND_MIXER_SW_IEC958_OUTPUT, sizeof(csw->s.name))) {
if (usw->s.value.data32[1] == (('C' << 8) | 'S')) {
if (csw->s.value.data16[4] != usw->s.value.data16[4] ||
csw->s.value.data16[5] != usw->s.value.data16[5]) {
csw->change = 1;
csw->s.value.data16[4] = usw->s.value.data16[4];
csw->s.value.data16[5] = usw->s.value.data16[5];
}
}
}
break;
case SND_SW_TYPE_BYTE:
if (usw->s.type != SND_SW_TYPE_DWORD) {
error("A wrong type for the switch %s. The type byte is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.data32[0] ||
csw->s.high < usw->s.value.data32[0]) {
error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.data8[0] != (unsigned char)usw->s.value.data32[0]) {
csw->change = 1;
csw->s.value.data8[0] = (unsigned char)usw->s.value.data32[0];
}
break;
case SND_SW_TYPE_WORD:
if (usw->s.type != SND_SW_TYPE_DWORD) {
error("A wrong type for the switch %s. The type word is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.data32[0] ||
csw->s.high < usw->s.value.data32[0]) {
error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.data16[0] != (unsigned short)usw->s.value.data32[0]) {
csw->change = 1;
csw->s.value.data16[0] = (unsigned short)usw->s.value.data32[0];
}
break;
case SND_SW_TYPE_DWORD:
if (usw->s.type != SND_SW_TYPE_DWORD) {
error("A wrong type for the switch %s. The type dword is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.data32[0] ||
csw->s.high < usw->s.value.data32[0]) {
error("The value %u for the switch %s is out of range %i-%i.", usw->s.value.data32[0], sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.data32[0] != usw->s.value.data32[0]) {
csw->change = 1;
csw->s.value.data32[0] = usw->s.value.data32[0];
}
break;
case SND_SW_TYPE_LIST:
if (usw->s.type != SND_SW_TYPE_LIST) {
error("A wrong type for the switch %s. The type list is expected. Skipping...", sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (csw->s.low > usw->s.value.item_number ||
csw->s.high < usw->s.value.item_number) {
error("The value %i for the switch %s is out of range %i-%i.", usw->s.value.item_number, sw_id(usw->s.name, cardno, devno, id), csw->s.low, csw->s.high);
return 1;
}
if (csw->s.value.item_number != usw->s.value.item_number) {
csw->change = 1;
csw->s.value.item_number = usw->s.value.item_number;
}
break;
case SND_SW_TYPE_USER_READ_ONLY:
break;
case SND_SW_TYPE_USER:
if (usw->s.type != SND_SW_TYPE_USER) {
error("A wrong type %i for the switch %s. The type user is expected. Skipping...", usw->s.type, sw_id(usw->s.name, cardno, devno, id));
return 1;
}
if (memcmp(csw->s.value.data8, usw->s.value.data8, 32)) {
csw->change = 1;
memcpy(csw->s.value.data8, usw->s.value.data8, 32);
}
break;
default:
error("The switch type %i is not known.", csw->s.type);
}
return 0;
}
static int soundcard_setup_merge_sw(struct ctl_switch *csw, struct ctl_switch *usw, int cardno, int devno, const char *id)
{
struct ctl_switch *csw1;
for ( ; usw; usw = usw->next) {
for (csw1 = csw; csw1; csw1 = csw1->next) {
if (!strncmp(csw1->s.name, usw->s.name, sizeof(csw1->s.name))) {
merge_one_sw(csw1, usw, cardno, devno, id);
break;
}
}
if (!csw1) {
error("Cannot find the switch %s...", sw_id(usw->s.name, cardno, devno, id));
}
}
return 0;
}
int soundcard_setup_merge_switches(int cardno)
{
struct soundcard *soundcard, *rsoundcard;
struct mixer *mixer, *rmixer;
struct pcm *pcm, *rpcm;
struct rawmidi *rawmidi, *rrawmidi;
for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
break;
}
if (!soundcard) {
error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
continue;
}
if (cardno >= 0 && soundcard->no != cardno)
continue;
soundcard_setup_merge_sw(soundcard->control.switches, rsoundcard->control.switches, soundcard->no, -1, "control");
for (rmixer = rsoundcard->mixers; rmixer; rmixer = rmixer->next) {
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
if (!strncmp(mixer->info.name, rmixer->info.name, sizeof(mixer->info.name)))
break;
}
if (!mixer) {
error("The mixer device '%s' from the soundcard %i was not found...\n", rmixer->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_sw(mixer->switches, rmixer->switches, soundcard->no, mixer->no, "mixer");
}
for (rpcm = rsoundcard->pcms; rpcm; rpcm = rpcm->next) {
for (pcm = soundcard->pcms; pcm; pcm = pcm->next) {
if (!strncmp(pcm->info.name, rpcm->info.name, sizeof(pcm->info.name)))
break;
}
if (!rpcm) {
error("The PCM device '%s' from the soundcard %i was not found...\n", rpcm->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_sw(pcm->pswitches, rpcm->pswitches, soundcard->no, pcm->no, "PCM playback");
soundcard_setup_merge_sw(pcm->rswitches, rpcm->rswitches, soundcard->no, pcm->no, "PCM capture");
}
for (rrawmidi = rsoundcard->rawmidis; rrawmidi; rrawmidi = rrawmidi->next) {
for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
if (!strncmp(rawmidi->info.name, rrawmidi->info.name, sizeof(rawmidi->info.name)))
break;
}
if (!rrawmidi) {
error("The rawmidi device '%s' from the soundcard %i was not found...\n", rrawmidi->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_sw(rawmidi->iswitches, rrawmidi->iswitches, soundcard->no, rawmidi->no, "rawmidi input");
soundcard_setup_merge_sw(rawmidi->oswitches, rrawmidi->oswitches, soundcard->no, rawmidi->no, "rawmidi output");
}
}
return 0;
}
static char *element_id(snd_mixer_eid_t *eid, int cardno, int devno, const char *id)
{
static char str[256];
sprintf(str, "%s %s card %i", mixer_element_id(eid), id, cardno);
if (devno >= 0)
sprintf(str + strlen(str)," device %i", devno);
return str;
}
static int merge_one_element(struct mixer_element *celement, struct mixer_element *uelement, int cardno, int devno, const char *id)
{
int tmp;
if (snd_mixer_element_has_control(&celement->element.eid) != 1)
return 0;
switch (celement->element.eid.type) {
case SND_MIXER_ETYPE_SWITCH1:
if (celement->element.data.switch1.sw != uelement->element.data.switch1.sw) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = ((celement->element.data.switch1.sw + 31) / 32) * sizeof(unsigned int);
memcpy(celement->element.data.switch1.psw, uelement->element.data.switch1.psw, tmp);
break;
case SND_MIXER_ETYPE_SWITCH2:
celement->element.data.switch2.sw = uelement->element.data.switch2.sw;
break;
case SND_MIXER_ETYPE_SWITCH3:
if (celement->element.data.switch3.rsw != uelement->element.data.switch3.rsw) {
error("Element %s has got a wrong count of switches.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = ((celement->element.data.switch3.rsw + 31) / 32) * sizeof(unsigned int);
memcpy(celement->element.data.switch3.prsw, uelement->element.data.switch3.prsw, tmp);
break;
case SND_MIXER_ETYPE_VOLUME1:
if (celement->element.data.volume1.voices != uelement->element.data.volume1.voices) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = celement->element.data.volume1.voices * sizeof(int);
memcpy(celement->element.data.volume1.pvoices, uelement->element.data.volume1.pvoices, tmp);
break;
case SND_MIXER_ETYPE_VOLUME2:
if (celement->element.data.volume2.avoices != uelement->element.data.volume2.avoices) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = celement->element.data.volume2.avoices * sizeof(int);
memcpy(celement->element.data.volume2.pavoices, uelement->element.data.volume2.pavoices, tmp);
break;
case SND_MIXER_ETYPE_ACCU3:
if (celement->element.data.accu3.voices != uelement->element.data.accu3.voices) {
error("Element %s has got a wrong count of voices.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
tmp = celement->element.data.accu3.voices * sizeof(int);
memcpy(celement->element.data.accu3.pvoices, uelement->element.data.accu3.pvoices, tmp);
break;
case SND_MIXER_ETYPE_MUX1:
if (celement->element.data.mux1.psel)
free(celement->element.data.mux1.psel);
celement->element.data.mux1.sel_size = 0;
celement->element.data.mux1.sel = 0;
celement->element.data.mux1.sel_over = 0;
tmp = uelement->element.data.mux1.sel * sizeof(snd_mixer_eid_t);
if (tmp > 0) {
celement->element.data.mux1.psel = (snd_mixer_eid_t *)malloc(uelement->element.data.mux1.sel_size * sizeof(snd_mixer_eid_t));
if (!celement->element.data.mux1.psel) {
error("No enough memory...");
return 1;
}
celement->element.data.mux1.sel_size = uelement->element.data.mux1.sel_size;
celement->element.data.mux1.sel = uelement->element.data.mux1.sel;
memcpy(celement->element.data.mux1.psel, uelement->element.data.mux1.psel, tmp);
}
break;
case SND_MIXER_ETYPE_MUX2:
celement->element.data.mux2.sel = uelement->element.data.mux2.sel;
break;
case SND_MIXER_ETYPE_TONE_CONTROL1:
if ((uelement->element.data.tc1.tc & ~celement->info.data.tc1.tc) != 0) {
error("Wrong (unsupported) input for the element %s.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
celement->element.data.tc1.tc = uelement->element.data.tc1.tc;
celement->element.data.tc1.sw = uelement->element.data.tc1.sw;
celement->element.data.tc1.bass = uelement->element.data.tc1.bass;
celement->element.data.tc1.treble = uelement->element.data.tc1.treble;
break;
case SND_MIXER_ETYPE_3D_EFFECT1:
if ((uelement->element.data.teffect1.effect & ~celement->info.data.teffect1.effect) != 0) {
error("Wrong (unsupported) input for the element %s.", element_id(&celement->element.eid, cardno, devno, id));
return 1;
}
celement->element.data.teffect1.effect = uelement->element.data.teffect1.effect;
celement->element.data.teffect1.sw = uelement->element.data.teffect1.sw;
celement->element.data.teffect1.mono_sw = uelement->element.data.teffect1.mono_sw;
celement->element.data.teffect1.wide = uelement->element.data.teffect1.wide;
celement->element.data.teffect1.volume = uelement->element.data.teffect1.volume;
celement->element.data.teffect1.center = uelement->element.data.teffect1.center;
celement->element.data.teffect1.space = uelement->element.data.teffect1.space;
celement->element.data.teffect1.depth = uelement->element.data.teffect1.depth;
celement->element.data.teffect1.delay = uelement->element.data.teffect1.delay;
celement->element.data.teffect1.feedback = uelement->element.data.teffect1.feedback;
celement->element.data.teffect1.depth_rear = uelement->element.data.teffect1.depth_rear;
break;
case SND_MIXER_ETYPE_PRE_EFFECT1:
if (celement->element.data.peffect1.pparameters)
free(celement->element.data.peffect1.pparameters);
celement->element.data.peffect1.parameters_size = 0;
celement->element.data.peffect1.parameters = 0;
celement->element.data.peffect1.parameters_over = 0;
celement->element.data.peffect1.item = uelement->element.data.peffect1.item;
if (celement->element.data.peffect1.item < 0) {
celement->element.data.peffect1.pparameters = (int *)malloc(uelement->element.data.peffect1.parameters_size * sizeof(int));
if (!celement->element.data.peffect1.pparameters) {
error("No enough memory..");
return 1;
}
celement->element.data.peffect1.parameters_size = uelement->element.data.peffect1.parameters_size;
celement->element.data.peffect1.parameters = uelement->element.data.peffect1.parameters;
tmp = celement->element.data.peffect1.parameters * sizeof(int);
memcpy(celement->element.data.peffect1.pparameters, uelement->element.data.peffect1.pparameters, tmp);
}
break;
default:
error("The element type %i for the element %s is not known.", celement->element.eid.type, mixer_element_id(&celement->element.eid));
}
return 0;
}
static int soundcard_setup_merge_element(struct mixer_element *celement, struct mixer_element *uelement, int cardno, int devno, const char *id)
{
struct mixer_element *element;
for ( ; uelement; uelement = uelement->next) {
for (element = celement; element; element = element->next) {
if (!strncmp(element->element.eid.name, uelement->element.eid.name, sizeof(element->element.eid.name)) &&
element->element.eid.index == uelement->element.eid.index &&
element->element.eid.type == uelement->element.eid.type) {
merge_one_element(element, uelement, cardno, devno, id);
break;
}
}
if (!element) {
error("Cannot find the element %s...", element_id(&uelement->element.eid, cardno, devno, id));
}
}
return 0;
}
int soundcard_setup_merge_data(int cardno)
{
struct soundcard *soundcard, *rsoundcard;
struct mixer *mixer, *rmixer;
for (rsoundcard = rsoundcards; rsoundcard; rsoundcard = rsoundcard->next) {
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (!strncmp(soundcard->control.hwinfo.id, rsoundcard->control.hwinfo.id, sizeof(soundcard->control.hwinfo.id)))
break;
}
if (!soundcard) {
error("The soundcard '%s' was not found...\n", rsoundcard->control.hwinfo.id);
continue;
}
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (rmixer = rsoundcard->mixers; rmixer; rmixer = rmixer->next) {
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
if (!strncmp(mixer->info.name, rmixer->info.name, sizeof(mixer->info.name)))
break;
}
if (!mixer) {
error("The mixer device '%s' from the soundcard %i was not found...\n", rmixer->info.name, soundcard->no);
continue;
}
soundcard_setup_merge_element(mixer->elements, rmixer->elements, soundcard->no, mixer->no, "mixer");
}
}
return 0;
}
static int soundcard_open_ctl(snd_ctl_t **ctlhandle, struct soundcard *soundcard)
{
int err;
if (*ctlhandle)
return 0;
if ((err = snd_ctl_open(ctlhandle, soundcard->no)) < 0) {
error("Cannot open control interface for soundcard #%i.", soundcard->no + 1);
return 1;
}
return 0;
}
static int soundcard_open_mix(snd_mixer_t **mixhandle, struct soundcard *soundcard, struct mixer *mixer)
{
int err;
if (*mixhandle)
return 0;
if ((err = snd_mixer_open(mixhandle, soundcard->no, mixer->no)) < 0) {
error("Cannot open mixer interface for soundcard #%i.", soundcard->no + 1);
return 1;
}
return 0;
}
int soundcard_setup_process_switches(int cardno)
{
int err;
snd_ctl_t *ctlhandle = NULL;
struct soundcard *soundcard;
struct ctl_switch *ctlsw;
struct mixer *mixer;
struct pcm *pcm;
struct rawmidi *rawmidi;
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (ctlsw = soundcard->control.switches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("Control switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
for (ctlsw = mixer->switches; ctlsw; ctlsw = ctlsw->next)
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("Mixer switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (pcm = soundcard->pcms; pcm; pcm = pcm->next) {
for (ctlsw = pcm->pswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("PCM playback switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (ctlsw = pcm->rswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("PCM capture switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
}
for (rawmidi = soundcard->rawmidis; rawmidi; rawmidi = rawmidi->next) {
for (ctlsw = rawmidi->oswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("RAWMIDI output switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
for (ctlsw = rawmidi->iswitches; ctlsw; ctlsw = ctlsw->next) {
if (ctlsw->change)
if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
if ((err = snd_ctl_switch_write(ctlhandle, &ctlsw->s)) < 0)
error("RAWMIDI input switch '%s' write error: %s", ctlsw->s.name, snd_strerror(err));
}
}
}
if (ctlhandle) {
snd_ctl_close(ctlhandle);
ctlhandle = NULL;
}
}
return 0;
}
int soundcard_setup_process_data(int cardno)
{
int err;
snd_ctl_t *ctlhandle = NULL;
snd_mixer_t *mixhandle = NULL;
struct soundcard *soundcard;
struct mixer *mixer;
struct mixer_element *element;
for (soundcard = soundcards; soundcard; soundcard = soundcard->next) {
if (cardno >= 0 && soundcard->no != cardno)
continue;
for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
for (element = mixer->elements; element; element = element->next)
if (snd_mixer_element_has_control(&element->element.eid) == 1) {
if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) {
if ((err = snd_mixer_element_write(mixhandle, &element->element)) < 0)
error("Mixer element %s write error: %s", mixer_element_id(&element->element.eid), snd_strerror(err));
}
}
if (mixhandle) {
snd_mixer_close(mixhandle);
mixhandle = NULL;
}
}
if (ctlhandle) {
snd_ctl_close(ctlhandle);
ctlhandle = NULL;
}
}
return 0;
}