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); snd_config_delete(config);
alsa_set_lr_nums(card);
alsa_get_routing_controls(card);
create_card_window(card); create_card_window(card);
} }

View File

@@ -19,6 +19,13 @@ const char *port_category_names[PC_COUNT] = {
"PCM Inputs" "PCM Inputs"
}; };
// names for the hardware types
const char *hw_type_names[HW_TYPE_COUNT] = {
"Analogue",
"S/PDIF",
"ADAT"
};
// global array of cards // global array of cards
static GArray *alsa_cards; static GArray *alsa_cards;
@@ -126,25 +133,6 @@ int get_max_elem_by_name(
return max; 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 // add a callback to the list of callbacks for this element
void alsa_elem_add_callback( void alsa_elem_add_callback(
struct alsa_elem *elem, struct alsa_elem *elem,
@@ -202,11 +190,11 @@ long alsa_get_elem_value(struct alsa_elem *elem) {
int type = elem->type; int type = elem->type;
if (type == SND_CTL_ELEM_TYPE_BOOLEAN) { 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) { } 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) { } 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 { } else {
fprintf( fprintf(
stderr, 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_alloca(&elem_value);
snd_ctl_elem_value_set_numid(elem_value, elem->numid); snd_ctl_elem_value_set_numid(elem_value, elem->numid);
snd_ctl_elem_read(elem->card->handle, elem_value);
int type = elem->type; int type = elem->type;
if (type == SND_CTL_ELEM_TYPE_BOOLEAN) { 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) { } 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) { } 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 { } else {
fprintf( fprintf(
stderr, stderr,
@@ -354,6 +343,111 @@ char *alsa_get_item_name(struct alsa_elem *elem, int i) {
// create/destroy alsa cards // 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 // scan the ALSA ctl element list container and put the useful
// elements into the cards->elems array of struct alsa_elem // elements into the cards->elems array of struct alsa_elem
static void alsa_get_elem_list(struct alsa_card *card) { 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 each element in the list
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
int numid = snd_ctl_elem_list_get_numid(list, i);
// allocate a temporary struct alsa_elem (will be copied later if alsa_get_elem(card, numid);
// 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;
} }
// free the ALSA list // free the ALSA list
@@ -458,6 +472,210 @@ static void alsa_get_elem_list(struct alsa_card *card) {
snd_ctl_elem_list_free(list); 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) { static void alsa_elem_change(struct alsa_elem *elem) {
if (!elem || !elem->callbacks) if (!elem || !elem->callbacks)
return; return;
@@ -479,16 +697,13 @@ static gboolean alsa_card_callback(
) { ) {
struct alsa_card *card = data; struct alsa_card *card = data;
snd_ctl_event_t *event; snd_ctl_event_t *event;
unsigned int mask;
int err, numid;
struct alsa_elem *elem;
snd_ctl_event_alloca(&event); snd_ctl_event_alloca(&event);
if (!card->handle) { if (!card->handle) {
printf("oops, no card handle??\n"); printf("oops, no card handle??\n");
return 0; return 0;
} }
err = snd_ctl_read(card->handle, event); int err = snd_ctl_read(card->handle, event);
if (err == 0) { if (err == 0) {
printf("alsa_card_callback nothing to read??\n"); printf("alsa_card_callback nothing to read??\n");
return 0; return 0;
@@ -502,15 +717,18 @@ static gboolean alsa_card_callback(
if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM) if (snd_ctl_event_get_type(event) != SND_CTL_EVENT_ELEM)
return 1; return 1;
numid = snd_ctl_event_elem_get_numid(event); int numid = snd_ctl_event_elem_get_numid(event);
elem = &g_array_index(card->elems, struct alsa_elem, numid); unsigned int mask = snd_ctl_event_elem_get_mask(event);
if (elem->numid != numid)
if (!(mask & (SND_CTL_EVENT_MASK_VALUE | SND_CTL_EVENT_MASK_INFO)))
return 1; 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); alsa_elem_change(elem);
}
return 1; return 1;
} }
@@ -811,6 +1029,9 @@ static void alsa_scan_cards(void) {
card->handle = ctl; card->handle = ctl;
alsa_get_elem_list(card); alsa_get_elem_list(card);
alsa_set_lr_nums(card);
alsa_get_routing_controls(card);
alsa_subscribe(card); alsa_subscribe(card);
alsa_get_usbid(card); alsa_get_usbid(card);
alsa_get_serial_number(card); alsa_get_serial_number(card);

View File

@@ -33,6 +33,17 @@ enum {
// names for the port categories // names for the port categories
extern const char *port_category_names[PC_COUNT]; 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 // is a drag active, and whether dragging from a routing source or a
// routing sink // routing sink
enum { enum {
@@ -51,7 +62,7 @@ struct routing_src {
// the enum id of the alsa item // the enum id of the alsa item
int id; 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; int port_category;
// 0-based count within port_category // 0-based count within port_category
@@ -60,6 +71,9 @@ struct routing_src {
// the alsa item name // the alsa item name
char *name; char *name;
// for PC_HW, the hardware type
int hw_type;
// the number (or translated letter; A = 1) in the item name // the number (or translated letter; A = 1) in the item name
int lr_num; int lr_num;
@@ -74,8 +88,6 @@ struct routing_src {
// entry in alsa_card routing_snks (routing sinks) array for alsa // entry in alsa_card routing_snks (routing sinks) array for alsa
// elements that are routing sinks like Analogue Output 01 Playback // elements that are routing sinks like Analogue Output 01 Playback
// Enum // 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 { struct routing_snk {
// location within the array // location within the array
@@ -90,12 +102,6 @@ struct routing_snk {
// socket widget on the routing page // socket widget on the routing page
GtkWidget *socket_widget; 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 // the mixer label widgets for this sink
GtkWidget *mixer_label_top; GtkWidget *mixer_label_top;
GtkWidget *mixer_label_bottom; GtkWidget *mixer_label_bottom;
@@ -118,6 +124,7 @@ struct alsa_elem {
const char *name; const char *name;
int type; int type;
int count; int count;
int index;
// for gain/volume elements, the dB range and step // for gain/volume elements, the dB range and step
int min_val; int min_val;
@@ -125,8 +132,11 @@ struct alsa_elem {
int min_dB; int min_dB;
int max_dB; int max_dB;
// for the number (or translated letter; A = 1) in the item name // for routing sinks
// TODO: move this to struct routing_snk? int is_routing_snk;
int port_category;
int port_num;
int hw_type;
int lr_num; int lr_num;
// the callback functions for this ALSA control element // the callback functions for this ALSA control element
@@ -198,7 +208,6 @@ int get_max_elem_by_name(
const char *prefix, const char *prefix,
const char *needle const char *needle
); );
int is_elem_routing_snk(struct alsa_elem *elem);
// add callback to alsa_elem callback list // add callback to alsa_elem callback list
void alsa_elem_add_callback( 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 // add to alsa_cards array
struct alsa_card *card_create(int card_num); 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 // init
void alsa_init(void); void alsa_init(void);

View File

@@ -237,7 +237,7 @@ static void get_snk_center(
double *y double *y
) { ) {
get_widget_center(r_snk->socket_widget, parent, x, 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)++; (*y)++;
} }
@@ -300,7 +300,7 @@ void draw_routing_lines(
draw_connection( draw_connection(
cr, cr,
x1, y1, r_src->port_category, x1, y1, r_src->port_category,
x2, y2, r_snk->port_category, x2, y2, r_snk->elem->port_category,
r, g, b, 2 r, g, b, 2
); );
} }
@@ -362,7 +362,7 @@ void draw_drag_line(
draw_connection( draw_connection(
cr, cr,
x1, y1, card->src_drag->port_category, 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 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( struct routing_snk *r_snk = &g_array_index(
card->routing_snks, struct routing_snk, i 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; continue;
if (r_snk->elem->lr_num == input_num)
if (elem->lr_num == input_num)
return r_snk; return r_snk;
} }
return NULL; return NULL;
@@ -122,12 +125,11 @@ void update_mixer_labels(struct alsa_card *card) {
struct routing_snk *r_snk = &g_array_index( struct routing_snk *r_snk = &g_array_index(
card->routing_snks, struct routing_snk, i card->routing_snks, struct routing_snk, i
); );
if (r_snk->port_category != PC_MIX)
continue;
struct alsa_elem *elem = r_snk->elem; struct alsa_elem *elem = r_snk->elem;
if (elem->port_category != PC_MIX)
continue;
int routing_src_idx = alsa_get_elem_value(elem); int routing_src_idx = alsa_get_elem_value(elem);
struct routing_src *r_src = &g_array_index( struct routing_src *r_src = &g_array_index(

View File

@@ -10,107 +10,6 @@
#include "window-mixer.h" #include "window-mixer.h"
#include "window-routing.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 // clear all the routing sinks
static void routing_preset_clear(struct alsa_card *card) { static void routing_preset_clear(struct alsa_card *card) {
for (int i = 0; i < card->routing_snks->len; i++) { 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( struct routing_snk *r_snk = &g_array_index(
card->routing_snks, struct routing_snk, snk_idx 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; break;
} }
@@ -172,11 +72,13 @@ static void routing_preset_link(
struct routing_snk *r_snk = &g_array_index( struct routing_snk *r_snk = &g_array_index(
card->routing_snks, struct routing_snk, snk_idx 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; break;
// do the assignment // 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 // get the next index
src_idx++; src_idx++;
@@ -467,11 +369,12 @@ static void src_routing_clicked(
struct routing_snk *r_snk = &g_array_index( struct routing_snk *r_snk = &g_array_index(
card->routing_snks, struct routing_snk, i 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) 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 // "DSP Input X Capture Enum" controls (DSP Inputs) go along
// the top, in card->routing_mixer_in_grid // the top, in card->routing_mixer_in_grid
if (r_snk->port_category == PC_DSP) { if (elem->port_category == PC_DSP) {
char name[10]; 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); make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_VERTICAL);
gtk_grid_attach( gtk_grid_attach(
GTK_GRID(card->routing_dsp_in_grid), r_snk->box_widget, 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 // "Mixer Input X Capture Enum" controls (Mixer Inputs) go along
// the top, in card->routing_mixer_in_grid after the DSP Inputs // 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]; 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); make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_VERTICAL);
gtk_grid_attach( gtk_grid_attach(
GTK_GRID(card->routing_mixer_in_grid), r_snk->box_widget, 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, // "PCM X Capture Enum" controls (PCM Inputs) go along the right,
// in card->routing_pcm_out_grid // in card->routing_pcm_out_grid
} else if (r_snk->port_category == PC_PCM) { } else if (elem->port_category == PC_PCM) {
char *name = strdup(elem->name); char *name = g_strdup_printf("PCM %d", elem->lr_num);
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);
make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_HORIZONTAL); make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_HORIZONTAL);
free(name); g_free(name);
gtk_grid_attach( gtk_grid_attach(
GTK_GRID(card->routing_pcm_out_grid), r_snk->box_widget, 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 // "* Output X Playback Enum" controls go along the right, in
// card->routing_hw_out_grid // card->routing_hw_out_grid
} else if (r_snk->port_category == PC_HW) { } else if (elem->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);
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); make_snk_routing_widget(r_snk, name, GTK_ORIENTATION_HORIZONTAL);
free(name); g_free(name);
gtk_grid_attach( gtk_grid_attach(
GTK_GRID(card->routing_hw_out_grid), r_snk->box_widget, 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 { } 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); 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) { } else if (r_src->port_category == PC_PCM) {
char *name = g_strdup_printf("PCM %d", r_src->lr_num);
make_src_routing_widget( 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_attach(
GTK_GRID(card->routing_pcm_in_grid), r_src->widget, GTK_GRID(card->routing_pcm_in_grid), r_src->widget,
0, r_src->port_num + 1, 1, 1 0, r_src->port_num + 1, 1, 1
); );
} else if (r_src->port_category == PC_HW) { } else if (r_src->port_category == PC_HW) {
make_src_routing_widget( char *name = g_strdup_printf(
card, r_src, r_src->name, GTK_ORIENTATION_HORIZONTAL "%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_attach(
GTK_GRID(card->routing_hw_in_grid), r_src->widget, GTK_GRID(card->routing_hw_in_grid), r_src->widget,
0, r_src->port_num + 1, 1, 1 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) { 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) { 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; return NULL;
} }
get_routing_srcs(card);
get_routing_snks(card);
create_routing_grid(card); create_routing_grid(card);
GtkWidget *top = gtk_frame_new(NULL); GtkWidget *top = gtk_frame_new(NULL);