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:
Geoffrey D. Bennett
2025-02-20 23:02:27 +10:30
parent b1831c137a
commit 45287711a4
6 changed files with 413 additions and 280 deletions

View File

@@ -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);
}

View File

@@ -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))
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);

View File

@@ -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);

View File

@@ -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
);

View File

@@ -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(

View File

@@ -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);