Gen 1: Add support for 1st Gen stereo elements
Move routing src/snk creation into alsa.c from window-routing.c. Move port_category and port_num from struct routing_snk to struct alsa_elem. Handle ALSA elements with two values. Handle controls labelled as 1L and 1R instead of 1 and 2.
This commit is contained in:
@@ -508,5 +508,8 @@ void create_sim_from_file(GtkWindow *w, char *fn) {
|
||||
|
||||
snd_config_delete(config);
|
||||
|
||||
alsa_set_lr_nums(card);
|
||||
alsa_get_routing_controls(card);
|
||||
|
||||
create_card_window(card);
|
||||
}
|
||||
|
||||
455
src/alsa.c
455
src/alsa.c
@@ -19,6 +19,13 @@ const char *port_category_names[PC_COUNT] = {
|
||||
"PCM Inputs"
|
||||
};
|
||||
|
||||
// names for the hardware types
|
||||
const char *hw_type_names[HW_TYPE_COUNT] = {
|
||||
"Analogue",
|
||||
"S/PDIF",
|
||||
"ADAT"
|
||||
};
|
||||
|
||||
// global array of cards
|
||||
static GArray *alsa_cards;
|
||||
|
||||
@@ -126,25 +133,6 @@ int get_max_elem_by_name(
|
||||
return max;
|
||||
}
|
||||
|
||||
// return true if the element is an routing sink enum, e.g.:
|
||||
// PCM xx Capture Enum
|
||||
// Mixer Input xx Capture Enum
|
||||
// Analogue Output xx Playback Enum
|
||||
// S/PDIF Output xx Playback Enum
|
||||
// ADAT Output xx Playback Enum
|
||||
int is_elem_routing_snk(struct alsa_elem *elem) {
|
||||
if (strstr(elem->name, "Capture Enum") && (
|
||||
strncmp(elem->name, "PCM ", 4) == 0 ||
|
||||
strncmp(elem->name, "Mixer Input ", 12) == 0 ||
|
||||
strncmp(elem->name, "DSP Input ", 10) == 0
|
||||
))
|
||||
return 1;
|
||||
if (strstr(elem->name, "Output") &&
|
||||
strstr(elem->name, "Playback Enum"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// add a callback to the list of callbacks for this element
|
||||
void alsa_elem_add_callback(
|
||||
struct alsa_elem *elem,
|
||||
@@ -202,11 +190,11 @@ long alsa_get_elem_value(struct alsa_elem *elem) {
|
||||
|
||||
int type = elem->type;
|
||||
if (type == SND_CTL_ELEM_TYPE_BOOLEAN) {
|
||||
return snd_ctl_elem_value_get_boolean(elem_value, 0);
|
||||
return snd_ctl_elem_value_get_boolean(elem_value, elem->index);
|
||||
} else if (type == SND_CTL_ELEM_TYPE_ENUMERATED) {
|
||||
return snd_ctl_elem_value_get_enumerated(elem_value, 0);
|
||||
return snd_ctl_elem_value_get_enumerated(elem_value, elem->index);
|
||||
} else if (type == SND_CTL_ELEM_TYPE_INTEGER) {
|
||||
return snd_ctl_elem_value_get_integer(elem_value, 0);
|
||||
return snd_ctl_elem_value_get_integer(elem_value, elem->index);
|
||||
} else {
|
||||
fprintf(
|
||||
stderr,
|
||||
@@ -257,14 +245,15 @@ void alsa_set_elem_value(struct alsa_elem *elem, long value) {
|
||||
|
||||
snd_ctl_elem_value_alloca(&elem_value);
|
||||
snd_ctl_elem_value_set_numid(elem_value, elem->numid);
|
||||
snd_ctl_elem_read(elem->card->handle, elem_value);
|
||||
|
||||
int type = elem->type;
|
||||
if (type == SND_CTL_ELEM_TYPE_BOOLEAN) {
|
||||
snd_ctl_elem_value_set_boolean(elem_value, 0, value);
|
||||
snd_ctl_elem_value_set_boolean(elem_value, elem->index, value);
|
||||
} else if (type == SND_CTL_ELEM_TYPE_ENUMERATED) {
|
||||
snd_ctl_elem_value_set_enumerated(elem_value, 0, value);
|
||||
snd_ctl_elem_value_set_enumerated(elem_value, elem->index, value);
|
||||
} else if (type == SND_CTL_ELEM_TYPE_INTEGER) {
|
||||
snd_ctl_elem_value_set_integer(elem_value, 0, value);
|
||||
snd_ctl_elem_value_set_integer(elem_value, elem->index, value);
|
||||
} else {
|
||||
fprintf(
|
||||
stderr,
|
||||
@@ -354,6 +343,111 @@ char *alsa_get_item_name(struct alsa_elem *elem, int i) {
|
||||
// create/destroy alsa cards
|
||||
//
|
||||
|
||||
static void alsa_get_elem_tlv(struct alsa_elem *elem) {
|
||||
if (elem->type != SND_CTL_ELEM_TYPE_INTEGER)
|
||||
return;
|
||||
|
||||
snd_ctl_elem_info_t *elem_info;
|
||||
|
||||
snd_ctl_elem_info_alloca(&elem_info);
|
||||
snd_ctl_elem_info_set_numid(elem_info, elem->numid);
|
||||
snd_ctl_elem_info(elem->card->handle, elem_info);
|
||||
|
||||
if (!snd_ctl_elem_info_is_tlv_readable(elem_info))
|
||||
return;
|
||||
|
||||
snd_ctl_elem_id_t *elem_id;
|
||||
unsigned int tlv[MAX_TLV_RANGE_SIZE];
|
||||
unsigned int *dbrec;
|
||||
int ret;
|
||||
long min_dB, max_dB;
|
||||
|
||||
snd_ctl_elem_id_alloca(&elem_id);
|
||||
snd_ctl_elem_id_set_numid(elem_id, elem->numid);
|
||||
|
||||
ret = snd_ctl_elem_tlv_read(
|
||||
elem->card->handle, elem_id, tlv, sizeof(tlv)
|
||||
);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "TLV read error %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &dbrec);
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr, "TLV parse error %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
int min_val = snd_ctl_elem_info_get_min(elem_info);
|
||||
int max_val = snd_ctl_elem_info_get_max(elem_info);
|
||||
|
||||
ret = snd_tlv_get_dB_range(tlv, min_val, max_val, &min_dB, &max_dB);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "TLV range error %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
elem->min_val = min_val;
|
||||
elem->max_val = max_val;
|
||||
elem->min_dB = min_dB / 100;
|
||||
elem->max_dB = max_dB / 100;
|
||||
}
|
||||
|
||||
static void alsa_get_elem(struct alsa_card *card, int numid) {
|
||||
// allocate a temporary struct alsa_elem (will be copied later if
|
||||
// we want to keep it)
|
||||
struct alsa_elem alsa_elem = {};
|
||||
|
||||
// keep a reference to the card in the element
|
||||
alsa_elem.card = card;
|
||||
|
||||
// get the control's numeric identifier (different to the index
|
||||
// into this array)
|
||||
alsa_elem.numid = numid;
|
||||
|
||||
// get the control's info
|
||||
alsa_elem.type = alsa_get_elem_type(&alsa_elem);
|
||||
alsa_elem.name = alsa_get_elem_name(&alsa_elem);
|
||||
alsa_elem.count = alsa_get_elem_count(&alsa_elem);
|
||||
|
||||
switch (alsa_elem.type) {
|
||||
case SND_CTL_ELEM_TYPE_BOOLEAN:
|
||||
case SND_CTL_ELEM_TYPE_ENUMERATED:
|
||||
case SND_CTL_ELEM_TYPE_INTEGER:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (strstr(alsa_elem.name, "Validity"))
|
||||
return;
|
||||
if (strstr(alsa_elem.name, "Channel Map"))
|
||||
return;
|
||||
|
||||
alsa_get_elem_tlv(&alsa_elem);
|
||||
|
||||
// Scarlett 1st Gen driver puts two volume controls/mutes in the
|
||||
// same element, so split them out to match the other series
|
||||
int count = alsa_elem.count;
|
||||
|
||||
if (strcmp(alsa_elem.name, "Level Meter") == 0)
|
||||
count = 1;
|
||||
|
||||
if (count > 2) {
|
||||
fprintf(stderr, "element %s has count %d\n", alsa_elem.name, count);
|
||||
count = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++, alsa_elem.lr_num++) {
|
||||
alsa_elem.index = i;
|
||||
|
||||
int array_len = card->elems->len;
|
||||
g_array_set_size(card->elems, array_len + 1);
|
||||
g_array_index(card->elems, struct alsa_elem, array_len) = alsa_elem;
|
||||
}
|
||||
}
|
||||
|
||||
// scan the ALSA ctl element list container and put the useful
|
||||
// elements into the cards->elems array of struct alsa_elem
|
||||
static void alsa_get_elem_list(struct alsa_card *card) {
|
||||
@@ -369,88 +463,8 @@ static void alsa_get_elem_list(struct alsa_card *card) {
|
||||
|
||||
// for each element in the list
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
// allocate a temporary struct alsa_elem (will be copied later if
|
||||
// we want to keep it)
|
||||
struct alsa_elem alsa_elem = {};
|
||||
|
||||
// keep a reference to the card in the element
|
||||
alsa_elem.card = card;
|
||||
|
||||
// get the control's numeric identifier (different to the index
|
||||
// into this array)
|
||||
alsa_elem.numid = snd_ctl_elem_list_get_numid(list, i);
|
||||
|
||||
// get the control's info
|
||||
alsa_elem.type = alsa_get_elem_type(&alsa_elem);
|
||||
alsa_elem.name = alsa_get_elem_name(&alsa_elem);
|
||||
alsa_elem.count = alsa_get_elem_count(&alsa_elem);
|
||||
|
||||
switch (alsa_elem.type) {
|
||||
case SND_CTL_ELEM_TYPE_BOOLEAN:
|
||||
case SND_CTL_ELEM_TYPE_ENUMERATED:
|
||||
case SND_CTL_ELEM_TYPE_INTEGER:
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strstr(alsa_elem.name, "Validity"))
|
||||
continue;
|
||||
if (strstr(alsa_elem.name, "Channel Map"))
|
||||
continue;
|
||||
|
||||
// get TLV info if it's a volume control
|
||||
if (alsa_elem.type == SND_CTL_ELEM_TYPE_INTEGER) {
|
||||
snd_ctl_elem_info_t *elem_info;
|
||||
|
||||
snd_ctl_elem_info_alloca(&elem_info);
|
||||
snd_ctl_elem_info_set_numid(elem_info, alsa_elem.numid);
|
||||
snd_ctl_elem_info(card->handle, elem_info);
|
||||
|
||||
if (snd_ctl_elem_info_is_tlv_readable(elem_info)) {
|
||||
snd_ctl_elem_id_t *elem_id;
|
||||
unsigned int tlv[MAX_TLV_RANGE_SIZE];
|
||||
unsigned int *dbrec;
|
||||
int ret;
|
||||
long min_dB, max_dB;
|
||||
|
||||
snd_ctl_elem_id_alloca(&elem_id);
|
||||
snd_ctl_elem_id_set_numid(elem_id, alsa_elem.numid);
|
||||
|
||||
ret = snd_ctl_elem_tlv_read(
|
||||
card->handle, elem_id, tlv, sizeof(tlv)
|
||||
);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "TLV read error %d\n", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = snd_tlv_parse_dB_info(tlv, sizeof(tlv), &dbrec);
|
||||
if (ret <= 0) {
|
||||
fprintf(stderr, "TLV parse error %d\n", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
int min_val = snd_ctl_elem_info_get_min(elem_info);
|
||||
int max_val = snd_ctl_elem_info_get_max(elem_info);
|
||||
|
||||
ret = snd_tlv_get_dB_range(tlv, min_val, max_val, &min_dB, &max_dB);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "TLV range error %d\n", ret);
|
||||
continue;
|
||||
}
|
||||
|
||||
alsa_elem.min_val = min_val;
|
||||
alsa_elem.max_val = max_val;
|
||||
alsa_elem.min_dB = min_dB / 100;
|
||||
alsa_elem.max_dB = max_dB / 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (card->elems->len <= alsa_elem.numid)
|
||||
g_array_set_size(card->elems, alsa_elem.numid + 1);
|
||||
g_array_index(card->elems, struct alsa_elem, alsa_elem.numid) = alsa_elem;
|
||||
int numid = snd_ctl_elem_list_get_numid(list, i);
|
||||
alsa_get_elem(card, numid);
|
||||
}
|
||||
|
||||
// free the ALSA list
|
||||
@@ -458,6 +472,210 @@ static void alsa_get_elem_list(struct alsa_card *card) {
|
||||
snd_ctl_elem_list_free(list);
|
||||
}
|
||||
|
||||
static void alsa_set_elem_lr_num(struct alsa_elem *elem) {
|
||||
const char *name = elem->name;
|
||||
char side;
|
||||
|
||||
if (strncmp(name, "Master Playback", 15) == 0 ||
|
||||
strncmp(name, "Master HW Playback", 18) == 0)
|
||||
elem->lr_num = 0;
|
||||
|
||||
else if (strncmp(name, "Master", 6) == 0)
|
||||
if (sscanf(name, "Master %d%c", &elem->lr_num, &side) != 2)
|
||||
printf("can't parse Master '%s'\n", name);
|
||||
else
|
||||
elem->lr_num = elem->lr_num * 2
|
||||
- (side == 'L' || side == ' ')
|
||||
+ elem->index;
|
||||
|
||||
else
|
||||
elem->lr_num = get_num_from_string(name);
|
||||
}
|
||||
|
||||
void alsa_set_lr_nums(struct alsa_card *card) {
|
||||
for (int i = 0; i < card->elems->len; i++) {
|
||||
struct alsa_elem *elem = &g_array_index(card->elems, struct alsa_elem, i);
|
||||
|
||||
alsa_set_elem_lr_num(elem);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_routing_srcs(struct alsa_card *card) {
|
||||
struct alsa_elem *elem = card->sample_capture_elem;
|
||||
|
||||
int count = alsa_get_item_count(elem);
|
||||
card->routing_srcs = g_array_new(
|
||||
FALSE, TRUE, sizeof(struct routing_src)
|
||||
);
|
||||
g_array_set_size(card->routing_srcs, count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
char *name = alsa_get_item_name(elem, i);
|
||||
|
||||
struct routing_src *r = &g_array_index(
|
||||
card->routing_srcs, struct routing_src, i
|
||||
);
|
||||
r->card = card;
|
||||
r->id = i;
|
||||
|
||||
if (strcmp(name, "Off") == 0)
|
||||
r->port_category = PC_OFF;
|
||||
else if (strncmp(name, "Mix", 3) == 0)
|
||||
r->port_category = PC_MIX;
|
||||
else if (strncmp(name, "DSP", 3) == 0)
|
||||
r->port_category = PC_DSP;
|
||||
else if (strncmp(name, "PCM", 3) == 0)
|
||||
r->port_category = PC_PCM;
|
||||
else {
|
||||
r->port_category = PC_HW;
|
||||
|
||||
if (strncmp(name, "Analog", 6) == 0)
|
||||
r->hw_type = HW_TYPE_ANALOGUE;
|
||||
else if (strncmp(name, "S/PDIF", 6) == 0)
|
||||
r->hw_type = HW_TYPE_SPDIF;
|
||||
else if (strncmp(name, "SPDIF", 5) == 0)
|
||||
r->hw_type = HW_TYPE_SPDIF;
|
||||
else if (strncmp(name, "ADAT", 4) == 0)
|
||||
r->hw_type = HW_TYPE_ADAT;
|
||||
}
|
||||
|
||||
r->name = name;
|
||||
r->lr_num =
|
||||
r->port_category == PC_MIX
|
||||
? name[4] - 'A' + 1
|
||||
: get_num_from_string(name);
|
||||
|
||||
r->port_num = card->routing_in_count[r->port_category]++;
|
||||
}
|
||||
|
||||
assert(card->routing_in_count[PC_MIX] <= MAX_MIX_OUT);
|
||||
}
|
||||
|
||||
// return true if the element is an routing sink enum, e.g.:
|
||||
// PCM xx Capture Enum
|
||||
// Mixer Input xx Capture Enum
|
||||
// Analogue Output xx Playback Enum
|
||||
// S/PDIF Output xx Playback Enum
|
||||
// ADAT Output xx Playback Enum
|
||||
static int is_elem_routing_snk(struct alsa_elem *elem) {
|
||||
if (strstr(elem->name, "Capture Route") ||
|
||||
strstr(elem->name, "Input Playback Route") ||
|
||||
strstr(elem->name, "Source Playback Enu"))
|
||||
return 1;
|
||||
|
||||
if (strstr(elem->name, "Capture Enum") && (
|
||||
strncmp(elem->name, "PCM ", 4) == 0 ||
|
||||
strncmp(elem->name, "Mixer Input ", 12) == 0 ||
|
||||
strncmp(elem->name, "DSP Input ", 10) == 0
|
||||
))
|
||||
return 1;
|
||||
|
||||
if (strstr(elem->name, "Output") &&
|
||||
strstr(elem->name, "Playback Enum"))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_routing_snks(struct alsa_card *card) {
|
||||
GArray *elems = card->elems;
|
||||
|
||||
int count = 0;
|
||||
|
||||
// count and label routing snks
|
||||
for (int i = 0; i < elems->len; i++) {
|
||||
struct alsa_elem *elem = &g_array_index(elems, struct alsa_elem, i);
|
||||
|
||||
if (!elem->card)
|
||||
continue;
|
||||
|
||||
if (!is_elem_routing_snk(elem))
|
||||
continue;
|
||||
|
||||
elem->is_routing_snk = 1;
|
||||
|
||||
if (strncmp(elem->name, "Mixer Input", 11) == 0 ||
|
||||
strncmp(elem->name, "Matrix", 6) == 0) {
|
||||
elem->port_category = PC_MIX;
|
||||
} else if (strncmp(elem->name, "DSP Input", 9) == 0) {
|
||||
elem->port_category = PC_DSP;
|
||||
} else if (strncmp(elem->name, "PCM", 3) == 0 ||
|
||||
strncmp(elem->name, "Input Source", 12) == 0) {
|
||||
elem->port_category = PC_PCM;
|
||||
} else if (strstr(elem->name, "Playback Enu")) {
|
||||
elem->port_category = PC_HW;
|
||||
|
||||
if (strncmp(elem->name, "Analog", 6) == 0)
|
||||
elem->hw_type = HW_TYPE_ANALOGUE;
|
||||
else if (strncmp(elem->name, "S/PDIF", 6) == 0 ||
|
||||
strstr(elem->name, "SPDIF"))
|
||||
elem->hw_type = HW_TYPE_SPDIF;
|
||||
else if (strstr(elem->name, "ADAT"))
|
||||
elem->hw_type = HW_TYPE_ADAT;
|
||||
} else {
|
||||
printf("unknown mixer routing elem %s\n", elem->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (elem->lr_num <= 0) {
|
||||
fprintf(stderr, "routing sink %s had no number\n", elem->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
// create an array of routing snks pointing to those elements
|
||||
card->routing_snks = g_array_new(
|
||||
FALSE, TRUE, sizeof(struct routing_snk)
|
||||
);
|
||||
g_array_set_size(card->routing_snks, count);
|
||||
|
||||
// count through card->routing_snks
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < elems->len; i++) {
|
||||
struct alsa_elem *elem = &g_array_index(elems, struct alsa_elem, i);
|
||||
|
||||
if (!elem->is_routing_snk)
|
||||
continue;
|
||||
|
||||
struct routing_snk *r = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, j
|
||||
);
|
||||
r->idx = j;
|
||||
j++;
|
||||
r->elem = elem;
|
||||
elem->port_num = card->routing_out_count[elem->port_category]++;
|
||||
}
|
||||
|
||||
assert(j == count);
|
||||
}
|
||||
|
||||
void alsa_get_routing_controls(struct alsa_card *card) {
|
||||
|
||||
// check that we can find a routing control
|
||||
card->sample_capture_elem =
|
||||
get_elem_by_name(card->elems, "PCM 01 Capture Enum");
|
||||
if (!card->sample_capture_elem) {
|
||||
card->sample_capture_elem =
|
||||
get_elem_by_name(card->elems, "Input Source 01 Capture Route");
|
||||
}
|
||||
|
||||
if (!card->sample_capture_elem) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"can't find routing control PCM 01 Capture Enum or "
|
||||
"Input Source 01 Capture Route\n"
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
get_routing_srcs(card);
|
||||
get_routing_snks(card);
|
||||
}
|
||||
|
||||
static void alsa_elem_change(struct alsa_elem *elem) {
|
||||
if (!elem || !elem->callbacks)
|
||||
return;
|
||||
@@ -479,16 +697,13 @@ static gboolean alsa_card_callback(
|
||||
) {
|
||||
struct alsa_card *card = data;
|
||||
snd_ctl_event_t *event;
|
||||
unsigned int mask;
|
||||
int err, numid;
|
||||
struct alsa_elem *elem;
|
||||
|
||||
snd_ctl_event_alloca(&event);
|
||||
if (!card->handle) {
|
||||
printf("oops, no card handle??\n");
|
||||
return 0;
|
||||
}
|
||||
err = snd_ctl_read(card->handle, event);
|
||||
int err = snd_ctl_read(card->handle, event);
|
||||
if (err == 0) {
|
||||
printf("alsa_card_callback nothing to read??\n");
|
||||
return 0;
|
||||
@@ -502,15 +717,18 @@ static gboolean alsa_card_callback(
|
||||
if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
|
||||
return 1;
|
||||
|
||||
numid = snd_ctl_event_elem_get_numid(event);
|
||||
elem = &g_array_index(card->elems, struct alsa_elem, numid);
|
||||
if (elem->numid != numid)
|
||||
int numid = snd_ctl_event_elem_get_numid(event);
|
||||
unsigned int mask = snd_ctl_event_elem_get_mask(event);
|
||||
|
||||
if (!(mask & (SND_CTL_EVENT_MASK_VALUE | SND_CTL_EVENT_MASK_INFO)))
|
||||
return 1;
|
||||
|
||||
mask = snd_ctl_event_elem_get_mask(event);
|
||||
for (int i = 0; i < card->elems->len; i++) {
|
||||
struct alsa_elem *elem = &g_array_index(card->elems, struct alsa_elem, i);
|
||||
|
||||
if (mask & (SND_CTL_EVENT_MASK_VALUE | SND_CTL_EVENT_MASK_INFO))
|
||||
alsa_elem_change(elem);
|
||||
if (elem->numid == numid)
|
||||
alsa_elem_change(elem);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -811,6 +1029,9 @@ static void alsa_scan_cards(void) {
|
||||
card->handle = ctl;
|
||||
|
||||
alsa_get_elem_list(card);
|
||||
alsa_set_lr_nums(card);
|
||||
alsa_get_routing_controls(card);
|
||||
|
||||
alsa_subscribe(card);
|
||||
alsa_get_usbid(card);
|
||||
alsa_get_serial_number(card);
|
||||
|
||||
37
src/alsa.h
37
src/alsa.h
@@ -33,6 +33,17 @@ enum {
|
||||
// names for the port categories
|
||||
extern const char *port_category_names[PC_COUNT];
|
||||
|
||||
// hardware types
|
||||
enum {
|
||||
HW_TYPE_ANALOGUE,
|
||||
HW_TYPE_SPDIF,
|
||||
HW_TYPE_ADAT,
|
||||
HW_TYPE_COUNT
|
||||
};
|
||||
|
||||
// names for the hardware types
|
||||
extern const char *hw_type_names[HW_TYPE_COUNT];
|
||||
|
||||
// is a drag active, and whether dragging from a routing source or a
|
||||
// routing sink
|
||||
enum {
|
||||
@@ -51,7 +62,7 @@ struct routing_src {
|
||||
// the enum id of the alsa item
|
||||
int id;
|
||||
|
||||
// PC_DSP, PC_MIX, PC_PCM, or PC_HW
|
||||
// PC_OFF, PC_DSP, PC_MIX, PC_PCM, or PC_HW
|
||||
int port_category;
|
||||
|
||||
// 0-based count within port_category
|
||||
@@ -60,6 +71,9 @@ struct routing_src {
|
||||
// the alsa item name
|
||||
char *name;
|
||||
|
||||
// for PC_HW, the hardware type
|
||||
int hw_type;
|
||||
|
||||
// the number (or translated letter; A = 1) in the item name
|
||||
int lr_num;
|
||||
|
||||
@@ -74,8 +88,6 @@ struct routing_src {
|
||||
// entry in alsa_card routing_snks (routing sinks) array for alsa
|
||||
// elements that are routing sinks like Analogue Output 01 Playback
|
||||
// Enum
|
||||
// port_category is set to PC_DSP, PC_MIX, PC_PCM, PC_HW
|
||||
// port_num is a count (0-based) within that category
|
||||
struct routing_snk {
|
||||
|
||||
// location within the array
|
||||
@@ -90,12 +102,6 @@ struct routing_snk {
|
||||
// socket widget on the routing page
|
||||
GtkWidget *socket_widget;
|
||||
|
||||
// PC_DSP, PC_MIX, PC_PCM, or PC_HW
|
||||
int port_category;
|
||||
|
||||
// 0-based count within port_category
|
||||
int port_num;
|
||||
|
||||
// the mixer label widgets for this sink
|
||||
GtkWidget *mixer_label_top;
|
||||
GtkWidget *mixer_label_bottom;
|
||||
@@ -118,6 +124,7 @@ struct alsa_elem {
|
||||
const char *name;
|
||||
int type;
|
||||
int count;
|
||||
int index;
|
||||
|
||||
// for gain/volume elements, the dB range and step
|
||||
int min_val;
|
||||
@@ -125,8 +132,11 @@ struct alsa_elem {
|
||||
int min_dB;
|
||||
int max_dB;
|
||||
|
||||
// for the number (or translated letter; A = 1) in the item name
|
||||
// TODO: move this to struct routing_snk?
|
||||
// for routing sinks
|
||||
int is_routing_snk;
|
||||
int port_category;
|
||||
int port_num;
|
||||
int hw_type;
|
||||
int lr_num;
|
||||
|
||||
// the callback functions for this ALSA control element
|
||||
@@ -198,7 +208,6 @@ int get_max_elem_by_name(
|
||||
const char *prefix,
|
||||
const char *needle
|
||||
);
|
||||
int is_elem_routing_snk(struct alsa_elem *elem);
|
||||
|
||||
// add callback to alsa_elem callback list
|
||||
void alsa_elem_add_callback(
|
||||
@@ -222,6 +231,10 @@ char *alsa_get_item_name(struct alsa_elem *elem, int i);
|
||||
// add to alsa_cards array
|
||||
struct alsa_card *card_create(int card_num);
|
||||
|
||||
// parse elements (used by alsa-sim.c)
|
||||
void alsa_set_lr_nums(struct alsa_card *card);
|
||||
void alsa_get_routing_controls(struct alsa_card *card);
|
||||
|
||||
// init
|
||||
void alsa_init(void);
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ static void get_snk_center(
|
||||
double *y
|
||||
) {
|
||||
get_widget_center(r_snk->socket_widget, parent, x, y);
|
||||
if (IS_MIXER(r_snk->port_category))
|
||||
if (IS_MIXER(r_snk->elem->port_category))
|
||||
(*y)++;
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ void draw_routing_lines(
|
||||
draw_connection(
|
||||
cr,
|
||||
x1, y1, r_src->port_category,
|
||||
x2, y2, r_snk->port_category,
|
||||
x2, y2, r_snk->elem->port_category,
|
||||
r, g, b, 2
|
||||
);
|
||||
}
|
||||
@@ -362,7 +362,7 @@ void draw_drag_line(
|
||||
draw_connection(
|
||||
cr,
|
||||
x1, y1, card->src_drag->port_category,
|
||||
x2, y2, card->snk_drag->port_category,
|
||||
x2, y2, card->snk_drag->elem->port_category,
|
||||
1, 1, 1, 2
|
||||
);
|
||||
|
||||
|
||||
@@ -16,9 +16,12 @@ static struct routing_snk *get_mixer_r_snk(
|
||||
struct routing_snk *r_snk = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, i
|
||||
);
|
||||
if (r_snk->port_category != PC_MIX)
|
||||
struct alsa_elem *elem = r_snk->elem;
|
||||
|
||||
if (elem->port_category != PC_MIX)
|
||||
continue;
|
||||
if (r_snk->elem->lr_num == input_num)
|
||||
|
||||
if (elem->lr_num == input_num)
|
||||
return r_snk;
|
||||
}
|
||||
return NULL;
|
||||
@@ -122,12 +125,11 @@ void update_mixer_labels(struct alsa_card *card) {
|
||||
struct routing_snk *r_snk = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, i
|
||||
);
|
||||
|
||||
if (r_snk->port_category != PC_MIX)
|
||||
continue;
|
||||
|
||||
struct alsa_elem *elem = r_snk->elem;
|
||||
|
||||
if (elem->port_category != PC_MIX)
|
||||
continue;
|
||||
|
||||
int routing_src_idx = alsa_get_elem_value(elem);
|
||||
|
||||
struct routing_src *r_src = &g_array_index(
|
||||
|
||||
@@ -10,107 +10,6 @@
|
||||
#include "window-mixer.h"
|
||||
#include "window-routing.h"
|
||||
|
||||
static void get_routing_srcs(struct alsa_card *card) {
|
||||
struct alsa_elem *elem = card->sample_capture_elem;
|
||||
|
||||
int count = alsa_get_item_count(elem);
|
||||
card->routing_srcs = g_array_new(
|
||||
FALSE, TRUE, sizeof(struct routing_src)
|
||||
);
|
||||
g_array_set_size(card->routing_srcs, count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
char *name = alsa_get_item_name(elem, i);
|
||||
|
||||
struct routing_src *r = &g_array_index(
|
||||
card->routing_srcs, struct routing_src, i
|
||||
);
|
||||
r->card = card;
|
||||
r->id = i;
|
||||
|
||||
if (strncmp(name, "Mix", 3) == 0)
|
||||
r->port_category = PC_MIX;
|
||||
else if (strncmp(name, "DSP", 3) == 0)
|
||||
r->port_category = PC_DSP;
|
||||
else if (strncmp(name, "PCM", 3) == 0)
|
||||
r->port_category = PC_PCM;
|
||||
else
|
||||
r->port_category = PC_HW;
|
||||
|
||||
r->name = name;
|
||||
r->lr_num =
|
||||
r->port_category == PC_MIX
|
||||
? name[4] - 'A' + 1
|
||||
: get_num_from_string(name);
|
||||
|
||||
r->port_num = card->routing_in_count[r->port_category]++;
|
||||
}
|
||||
|
||||
assert(card->routing_in_count[PC_MIX] <= MAX_MIX_OUT);
|
||||
}
|
||||
|
||||
static void get_routing_snks(struct alsa_card *card) {
|
||||
GArray *elems = card->elems;
|
||||
|
||||
int count = 0;
|
||||
|
||||
// count and label routing snks
|
||||
for (int i = 0; i < elems->len; i++) {
|
||||
struct alsa_elem *elem = &g_array_index(elems, struct alsa_elem, i);
|
||||
|
||||
if (!elem->card)
|
||||
continue;
|
||||
|
||||
if (!is_elem_routing_snk(elem))
|
||||
continue;
|
||||
|
||||
int i = get_num_from_string(elem->name);
|
||||
if (i < 0)
|
||||
continue;
|
||||
|
||||
elem->lr_num = i;
|
||||
count++;
|
||||
}
|
||||
|
||||
// create an array of routing snks pointing to those elements
|
||||
card->routing_snks = g_array_new(
|
||||
FALSE, TRUE, sizeof(struct routing_snk)
|
||||
);
|
||||
g_array_set_size(card->routing_snks, count);
|
||||
|
||||
// count through card->routing_snks
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < elems->len; i++) {
|
||||
struct alsa_elem *elem = &g_array_index(elems, struct alsa_elem, i);
|
||||
|
||||
if (!elem->lr_num)
|
||||
continue;
|
||||
|
||||
struct routing_snk *r = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, j
|
||||
);
|
||||
r->idx = j;
|
||||
j++;
|
||||
r->elem = elem;
|
||||
if (strncmp(elem->name, "Mixer Input", 11) == 0) {
|
||||
r->port_category = PC_MIX;
|
||||
} else if (strncmp(elem->name, "DSP Input", 9) == 0) {
|
||||
r->port_category = PC_DSP;
|
||||
} else if (strncmp(elem->name, "PCM", 3) == 0) {
|
||||
r->port_category = PC_PCM;
|
||||
} else if (strstr(elem->name, "Playback Enum")) {
|
||||
r->port_category = PC_HW;
|
||||
} else {
|
||||
printf("unknown mixer routing elem %s\n", elem->name);
|
||||
continue;
|
||||
}
|
||||
r->port_num = card->routing_out_count[r->port_category]++;
|
||||
}
|
||||
|
||||
assert(j == count);
|
||||
}
|
||||
|
||||
// clear all the routing sinks
|
||||
static void routing_preset_clear(struct alsa_card *card) {
|
||||
for (int i = 0; i < card->routing_snks->len; i++) {
|
||||
@@ -150,8 +49,9 @@ static void routing_preset_link(
|
||||
struct routing_snk *r_snk = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, snk_idx
|
||||
);
|
||||
struct alsa_elem *elem = r_snk->elem;
|
||||
|
||||
if (r_snk->port_category == snk_port_category)
|
||||
if (elem->port_category == snk_port_category)
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -172,11 +72,13 @@ static void routing_preset_link(
|
||||
struct routing_snk *r_snk = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, snk_idx
|
||||
);
|
||||
if (r_snk->port_category != snk_port_category)
|
||||
struct alsa_elem *elem = r_snk->elem;
|
||||
|
||||
if (elem->port_category != snk_port_category)
|
||||
break;
|
||||
|
||||
// do the assignment
|
||||
alsa_set_elem_value(r_snk->elem, r_src->id);
|
||||
alsa_set_elem_value(elem, r_src->id);
|
||||
|
||||
// get the next index
|
||||
src_idx++;
|
||||
@@ -467,11 +369,12 @@ static void src_routing_clicked(
|
||||
struct routing_snk *r_snk = &g_array_index(
|
||||
card->routing_snks, struct routing_snk, i
|
||||
);
|
||||
struct alsa_elem *elem = r_snk->elem;
|
||||
|
||||
int r_src_idx = alsa_get_elem_value(r_snk->elem);
|
||||
int r_src_idx = alsa_get_elem_value(elem);
|
||||
|
||||
if (r_src_idx == r_src->id)
|
||||
alsa_set_elem_value(r_snk->elem, 0);
|
||||
alsa_set_elem_value(elem, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,7 +705,7 @@ static void make_routing_alsa_elem(struct routing_snk *r_snk) {
|
||||
|
||||
// "DSP Input X Capture Enum" controls (DSP Inputs) go along
|
||||
// the top, in card->routing_mixer_in_grid
|
||||
if (r_snk->port_category == PC_DSP) {
|
||||
if (elem->port_category == PC_DSP) {
|
||||
|
||||
char name[10];
|
||||
|
||||
@@ -810,12 +713,12 @@ static void make_routing_alsa_elem(struct routing_snk *r_snk) {
|
||||
make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_VERTICAL);
|
||||
gtk_grid_attach(
|
||||
GTK_GRID(card->routing_dsp_in_grid), r_snk->box_widget,
|
||||
r_snk->port_num + 1, 0, 1, 1
|
||||
elem->port_num + 1, 0, 1, 1
|
||||
);
|
||||
|
||||
// "Mixer Input X Capture Enum" controls (Mixer Inputs) go along
|
||||
// the top, in card->routing_mixer_in_grid after the DSP Inputs
|
||||
} else if (r_snk->port_category == PC_MIX) {
|
||||
} else if (elem->port_category == PC_MIX) {
|
||||
|
||||
char name[10];
|
||||
|
||||
@@ -823,48 +726,37 @@ static void make_routing_alsa_elem(struct routing_snk *r_snk) {
|
||||
make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_VERTICAL);
|
||||
gtk_grid_attach(
|
||||
GTK_GRID(card->routing_mixer_in_grid), r_snk->box_widget,
|
||||
r_snk->port_num + 1, 0, 1, 1
|
||||
elem->port_num + 1, 0, 1, 1
|
||||
);
|
||||
|
||||
// "PCM X Capture Enum" controls (PCM Inputs) go along the right,
|
||||
// in card->routing_pcm_out_grid
|
||||
} else if (r_snk->port_category == PC_PCM) {
|
||||
char *name = strdup(elem->name);
|
||||
char *name_end = strchr(name, ' ');
|
||||
|
||||
// in case the number is zero-padded
|
||||
if (name_end)
|
||||
snprintf(name_end, strlen(name_end) + 1, " %d", elem->lr_num);
|
||||
|
||||
} else if (elem->port_category == PC_PCM) {
|
||||
char *name = g_strdup_printf("PCM %d", elem->lr_num);
|
||||
make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_HORIZONTAL);
|
||||
free(name);
|
||||
g_free(name);
|
||||
|
||||
gtk_grid_attach(
|
||||
GTK_GRID(card->routing_pcm_out_grid), r_snk->box_widget,
|
||||
0, r_snk->port_num + 1, 1, 1
|
||||
0, elem->port_num + 1, 1, 1
|
||||
);
|
||||
|
||||
// "* Output X Playback Enum" controls go along the right, in
|
||||
// card->routing_hw_out_grid
|
||||
} else if (r_snk->port_category == PC_HW) {
|
||||
|
||||
// Convert "Analogue 01 Output Playback Enum" to "Analogue 1"
|
||||
char *name = strdup(elem->name);
|
||||
char *name_end = strstr(name, " Output ");
|
||||
|
||||
// in case the number is zero-padded
|
||||
if (name_end)
|
||||
snprintf(name_end, strlen(name_end) + 1, " %d", elem->lr_num);
|
||||
} else if (elem->port_category == PC_HW) {
|
||||
|
||||
char *name = g_strdup_printf(
|
||||
"%s %d", hw_type_names[elem->hw_type], elem->lr_num
|
||||
);
|
||||
make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_HORIZONTAL);
|
||||
free(name);
|
||||
g_free(name);
|
||||
|
||||
gtk_grid_attach(
|
||||
GTK_GRID(card->routing_hw_out_grid), r_snk->box_widget,
|
||||
0, r_snk->port_num + 1, 1, 1
|
||||
0, elem->port_num + 1, 1, 1
|
||||
);
|
||||
} else {
|
||||
printf("invalid port category %d\n", r_snk->port_category);
|
||||
printf("invalid port category %d\n", elem->port_category);
|
||||
}
|
||||
|
||||
alsa_elem_add_callback(elem, routing_updated, NULL);
|
||||
@@ -925,17 +817,25 @@ static void add_routing_widgets(
|
||||
);
|
||||
}
|
||||
} else if (r_src->port_category == PC_PCM) {
|
||||
char *name = g_strdup_printf("PCM %d", r_src->lr_num);
|
||||
make_src_routing_widget(
|
||||
card, r_src, r_src->name, GTK_ORIENTATION_HORIZONTAL
|
||||
card, r_src, name, GTK_ORIENTATION_HORIZONTAL
|
||||
);
|
||||
g_free(name);
|
||||
gtk_grid_attach(
|
||||
GTK_GRID(card->routing_pcm_in_grid), r_src->widget,
|
||||
0, r_src->port_num + 1, 1, 1
|
||||
);
|
||||
} else if (r_src->port_category == PC_HW) {
|
||||
make_src_routing_widget(
|
||||
card, r_src, r_src->name, GTK_ORIENTATION_HORIZONTAL
|
||||
char *name = g_strdup_printf(
|
||||
"%s %d",
|
||||
hw_type_names[r_src->hw_type],
|
||||
r_src->lr_num
|
||||
);
|
||||
make_src_routing_widget(
|
||||
card, r_src, name, GTK_ORIENTATION_HORIZONTAL
|
||||
);
|
||||
g_free(name);
|
||||
gtk_grid_attach(
|
||||
GTK_GRID(card->routing_hw_in_grid), r_src->widget,
|
||||
0, r_src->port_num + 1, 1, 1
|
||||
@@ -973,17 +873,11 @@ static void add_routing_widgets(
|
||||
|
||||
GtkWidget *create_routing_controls(struct alsa_card *card) {
|
||||
|
||||
// check that we can find a routing control
|
||||
card->sample_capture_elem =
|
||||
get_elem_by_name(card->elems, "PCM 01 Capture Enum");
|
||||
if (!card->sample_capture_elem) {
|
||||
printf("couldn't find PCM 01 Capture Enum control; can't create GUI\n");
|
||||
printf("couldn't find sample capture control; can't create GUI\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
get_routing_srcs(card);
|
||||
get_routing_snks(card);
|
||||
|
||||
create_routing_grid(card);
|
||||
|
||||
GtkWidget *top = gtk_frame_new(NULL);
|
||||
|
||||
Reference in New Issue
Block a user