[Jack-Devel] RFC: jackd portnames
Hi!
I've forward-ported faberman's portname patch from jackd2-1.9.4 to
1.9.7.
It is supposed to provide stable port names across cards, no matter of
the underlying physical connection.
I see mainly two use cases:
1.) Human-readable portnames on larger cards where "playback_56" is
simply to cumbersome to figure out what's actually connected
2.) People who travel with ADAT-ADCs/DACs, but connect them via
different interfaces in different locations, e.g. a Multiface
when on the road and a RayDat when in the studio. Despite the
different cards, the port names and hence any ardour session
would remain intact.
We would probably want a generic approach including firewire, either by
duplicating the code (bad) or somewhere higher in the higher layers, but
let's get started, first.
Here is how it looks like:
http://adi.loris.tv/jackd2-portnames.png
Right now, I've added a bit of verbosity to point the user to the
correct file location:
Using port names patch v0.1 (07.04.2010)
Trying to load portnames from /home/racl/adi/.config/jack/cards/RME
RayDAT_f1cd85.ss.ports.in
Trying to load portnames from /home/racl/adi/.config/jack/cards/RME
RayDAT_f1cd85.ports.in
Trying to load portnames from /etc/jack/cards/RME RayDAT_f1cd85.ss.ports.in
Trying to load portnames from /etc/jack/cards/RME RayDAT_f1cd85.ports.in
Trying to load portnames from /home/racl/adi/.config/jack/cards/RME
RayDAT_f1cd85.ss.ports.out
Trying to load portnames from /home/racl/adi/.config/jack/cards/RME
RayDAT_f1cd85.ports.out
Trying to load portnames from /etc/jack/cards/RME RayDAT_f1cd85.ss.ports.out
Trying to load portnames from /etc/jack/cards/RME RayDAT_f1cd85.ports.out
The beginning of my ports.in file:
# generated by hdspm
1=Korg-L
2=Korg-R
3=IN-ADAT1.3
4=IN-ADAT1.4
5=IN-ADAT1.5
6=IN-ADAT1.6
7=Guitar
8=Sindy
While the generic portnames were defined by the driver, I had to prepend
an "IN-" to have different portnames for input and output.
How do you feel about system:in:portname? This is easier for the user,
but a little less freedom.
OTOH, I can also image cardname:portname in case users have multiple
cards.
That's the main purpose of this RFC: what's a useful naming convention?
Any requirements from the pro-audio/broadcasting camp?
Note that all the previous names are still available:
$ jack_lsp -A
system:Korg-L
alsa_pcm:hw:1,0:out1
system:capture_1
system:Korg-R
alsa_pcm:hw:1,0:out2
system:capture_2
[...]
This hopefully remains backward compatibility with existing sessions.
Of course, if we agree on something, I'd forward-port faberman's jackd
version, too, but since jackd2 was installed on my workstation, I
started with jackd2.
Note that nobody has to use portnames. If the user does nothing, he'd
end up with his ordinary channel names. The patch hence targets more
advanced users.
Cheers
And here's the patch:
--- /dev/null
+++ b/linux/alsa/port_names.c
@@ -0,0 +1,179 @@
+/* -*- mode: c; c-file-style: "linux"; -*- */
+/*
+ Copyright (C) 2010 Florian Faber, [hidden]
+
+ 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 <math.h>
+#include <stdio.h>
+#include <memory.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <string.h>
+
+#include "alsa_driver.h"
+
+
+static int port_names_load_portfile(alsa_driver_t *driver, const char
*filename, char **buf, const unsigned int offset, const unsigned int num) {
+ int fh, i, ret, lineno, id, res=0;
+ char line[256];
+
+ printf("Trying to load portnames from %s\n", filename);
+ fh = open(filename, O_RDONLY);
+ if (-1!=fh) {
+ res = 1;
+ i = 0;
+ lineno = 1;
+ for (;;) {
+ ret = read(fh, &line[i], 1);
+ if (0==ret) {
+ break;
+ } else if (-1==ret) {
+ sprintf(stderr, "Error while reading \"%s\": %s", filename,
strerror(errno));
+ break;
+ }
+ if (0x0A==line[i]) {
+ /* new line, parse input */
+ line[i] = 0;
+
+ if ('#' != line[0]) {
+ i=0;
+ while ((i<255) && (line[i]!='=')) i++;
+ if (255==i) {
+ sprintf(stderr, "Error while reading \"%s\": Line %d has no
key=value syntax!", filename, lineno);
+ } else {
+ line[i] = 0;
+ id = atoi(line);
+ if ((id>=1) && (id<=num)) {
+ if (NULL==buf[id-1+offset]) {
+ /* don't overwrite existing names */
+ buf[id-1+offset] = strdup(&line[i+1]);
+ }
+ } else {
+ sprintf(stderr, "Error while reading \"%s\": Key %d out of range
in line %d (1..%d)", filename, id, lineno, num);
+ }
+ }
+ }
+
+ i = 0;
+ lineno++;
+ } else {
+ i++;
+ if (i==255) {
+ sprintf(stderr, "Error while reading \"%s\": Line %d is too long",
filename, lineno);
+ break;
+ }
+ }
+ }
+
+ (void) close(fh);
+ }
+
+ return res;
+}
+
+
+static void port_names_default_portnames(char **buf, const unsigned int
offset, const unsigned int num, const char *defaultname) {
+ unsigned int i;
+ char line[256];
+
+ /* Fill in default names */
+ for (i=0; i<num; i++) {
+ if (NULL==buf[i+offset]) {
+ snprintf(line, 255, defaultname, i+1);
+ buf[i+offset] = strdup(line);
+ }
+ }
+}
+
+
+char** port_names_get_portnames(alsa_driver_t *driver) {
+ snd_ctl_card_info_t *card_info;
+ int err;
+ const char *card_name = NULL;
+ char filename[256], *speed;
+ char **buf;
+
+ printf("Using port names patch v0.1 (07.04.2010)\n");
+
+ if (driver->frame_rate > 96000) {
+ speed="qs";
+ } else if (driver->frame_rate > 48000) {
+ speed="ds";
+ } else {
+ speed="ss";
+ }
+
+ snd_ctl_card_info_alloca(&card_info);
+ err = snd_ctl_card_info(driver->ctl_handle, card_info);
+ if (err >= 0) {
+ card_name = snd_ctl_card_info_get_name(card_info);
+ } else {
+ card_name = "noname";
+ }
+
+ buf = malloc(sizeof(char *)*(driver->capture_nchannels +
driver->playback_nchannels));
+ if (NULL==buf) {
+ sprintf(stderr, "ALSA: Not enough memory for %d port names",
driver->capture_nchannels + driver->playback_nchannels);
+ return NULL;
+ }
+ bzero(buf, sizeof(char *)*(driver->capture_nchannels +
driver->playback_nchannels));
+
+ /* Read port names from special to general:
+ * Begin with user and speed specific port names */
+ snprintf(filename, 255, "%s/.config/jack/cards/%s.%s.ports.in",
getenv("HOME"), card_name, speed);
+ (void) port_names_load_portfile(driver, filename, buf, 0,
driver->capture_nchannels);
+
+ /* Now user general */
+ snprintf(filename, 255, "%s/.config/jack/cards/%s.ports.in",
getenv("HOME"), card_name);
+ (void) port_names_load_portfile(driver, filename, buf, 0,
driver->capture_nchannels);
+
+ /* System speed specific */
+ snprintf(filename, 255, "/etc/jack/cards/%s.%s.ports.in", card_name,
speed);
+ (void) port_names_load_portfile(driver, filename, buf, 0,
driver->capture_nchannels);
+
+ /* System general */
+ snprintf(filename, 255, "/etc/jack/cards/%s.ports.in", card_name);
+ (void) port_names_load_portfile(driver, filename, buf, 0,
driver->capture_nchannels);
+
+ /* Fill all still unnamed ports with default names */
+ port_names_default_portnames(buf, 0, driver->capture_nchannels,
"capture_%lu");
+
+
+ /* Same procedure for the playback channels */
+ snprintf(filename, 255, "%s/.config/jack/cards/%s.%s.ports.out",
getenv("HOME"), card_name, speed);
+ (void) port_names_load_portfile(driver, filename, buf,
driver->capture_nchannels, driver->playback_nchannels);
+
+ snprintf(filename, 255, "%s/.config/jack/cards/%s.ports.out",
getenv("HOME"), card_name);
+ (void) port_names_load_portfile(driver, filename, buf,
driver->capture_nchannels, driver->playback_nchannels);
+
+ snprintf(filename, 255, "/etc/jack/cards/%s.%s.ports.out", card_name,
speed);
+ (void) port_names_load_portfile(driver, filename, buf,
driver->capture_nchannels, driver->playback_nchannels);
+
+ snprintf(filename, 255, "/etc/jack/cards/%s.ports.out", card_name);
+ (void) port_names_load_portfile(driver, filename, buf,
driver->capture_nchannels, driver->playback_nchannels);
+
+ port_names_default_portnames(buf, driver->capture_nchannels,
driver->playback_nchannels, "playback_%lu");
+
+ return buf;
+}
--- /dev/null
+++ b/linux/alsa/port_names.h
@@ -0,0 +1,34 @@
+/*
+ Copyright (C) 2010 Florian Faber, [hidden]
+
+ 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.
+
+*/
+
+#ifndef __jack_port_names_h__
+#define __jack_port_names_h__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+char** port_names_get_portnames(alsa_driver_t *driver);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __jack_port_names_h__ */
--- a/linux/alsa/JackAlsaDriver.cpp
+++ b/linux/alsa/JackAlsaDriver.cpp
@@ -43,6 +43,7 @@
#include "JackPosixThread.h"
#include "JackCompilerDeps.h"
#include "JackServerGlobals.h"
+#include "port_names.h"
namespace Jack
{
@@ -72,6 +73,8 @@
unsigned long port_flags = (unsigned long)CaptureDriverFlags;
char name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
char alias[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
+ char old_name[JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE];
+ char **portnames;
jack_latency_range_t range;
assert(fCaptureChannels < DRIVER_PORT_NUM);
@@ -88,15 +91,20 @@
jack_log("JackAlsaDriver::Attach fBufferSize %ld fSampleRate %ld",
fEngineControl->fBufferSize, fEngineControl->fSampleRate);
+ portnames = port_names_get_portnames(alsa_driver);
+
for (int i = 0; i < fCaptureChannels; i++) {
snprintf(alias, sizeof(alias) - 1, "%s:%s:out%d", fAliasName,
fCaptureDriverName, i + 1);
- snprintf(name, sizeof(name) - 1, "%s:capture_%d",
fClientControl.fName, i + 1);
+ snprintf(old_name, sizeof(old_name) - 1, "%s:capture_%d",
fClientControl.fName, i + 1);
+ snprintf(name, sizeof(name) - 1, "%s:%s", fClientControl.fName,
portnames[i]);
if ((port_index =
fGraphManager->AllocatePort(fClientControl.fRefNum, name,
JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags,
fEngineControl->fBufferSize)) == NO_PORT) {
jack_error("driver: cannot register port for %s", name);
return -1;
}
+ free(portnames[i]);
port = fGraphManager->GetPort(port_index);
port->SetAlias(alias);
+ port->SetAlias(old_name);
range.min = range.max = alsa_driver->frames_per_cycle +
alsa_driver->capture_frame_latency;
port->SetLatencyRange(JackCaptureLatency, &range);
fCapturePortList[i] = port_index;
@@ -107,13 +115,16 @@
for (int i = 0; i < fPlaybackChannels; i++) {
snprintf(alias, sizeof(alias) - 1, "%s:%s:in%d", fAliasName,
fPlaybackDriverName, i + 1);
- snprintf(name, sizeof(name) - 1, "%s:playback_%d",
fClientControl.fName, i + 1);
+ snprintf(old_name, sizeof(old_name) - 1, "%s:playback_%d",
fClientControl.fName, i + 1);
+ snprintf(name, sizeof(name) - 1, "%s:%s", fClientControl.fName,
portnames[i+fCaptureChannels]);
if ((port_index =
fGraphManager->AllocatePort(fClientControl.fRefNum, name,
JACK_DEFAULT_AUDIO_TYPE, (JackPortFlags)port_flags,
fEngineControl->fBufferSize)) == NO_PORT) {
jack_error("driver: cannot register port for %s", name);
return -1;
}
+ free(portnames[i+fCaptureChannels]);
port = fGraphManager->GetPort(port_index);
port->SetAlias(alias);
+ port->SetAlias(old_name);
// Add one buffer more latency if "async" mode is used...
range.min = range.max = (alsa_driver->frames_per_cycle *
(alsa_driver->user_nperiods - 1)) +
((fEngineControl->fSyncMode) ? 0 :
fEngineControl->fBufferSize) + alsa_driver->playback_frame_latency;
@@ -137,6 +148,8 @@
}
}
+ free(portnames);
+
if (alsa_driver->midi) {
int err = (alsa_driver->midi->attach)(alsa_driver->midi);
if (err)
--- a/linux/wscript
+++ b/linux/wscript
@@ -54,6 +54,7 @@
'alsa/hdsp.c',
'alsa/alsa_driver.c',
'alsa/hammerfall.c',
+ 'alsa/port_names.c',
'alsa/ice1712.c'
]
system:Korg-L
alsa_pcm:hw:1,0:out1
system:capture_1
system:Korg-R
alsa_pcm:hw:1,0:out2
system:capture_2
system:IN-ADAT1.3
alsa_pcm:hw:1,0:out3
system:capture_3
system:IN-ADAT1.4
alsa_pcm:hw:1,0:out4
system:capture_4
system:IN-ADAT1.5
alsa_pcm:hw:1,0:out5
system:capture_5
system:IN-ADAT1.6
alsa_pcm:hw:1,0:out6
system:capture_6
system:Guitar
alsa_pcm:hw:1,0:out7
system:capture_7
system:Sindy
alsa_pcm:hw:1,0:out8
system:capture_8
system:IN-ADAT2.1
alsa_pcm:hw:1,0:out9
system:capture_9
system:IN-ADAT2.2
alsa_pcm:hw:1,0:out10
system:capture_10
system:IN-ADAT2.3
alsa_pcm:hw:1,0:out11
system:capture_11
system:IN-ADAT2.4
alsa_pcm:hw:1,0:out12
system:capture_12
system:IN-ADAT2.5
alsa_pcm:hw:1,0:out13
system:capture_13
system:IN-ADAT2.6
alsa_pcm:hw:1,0:out14
system:capture_14
system:IN-ADAT2.7
alsa_pcm:hw:1,0:out15
system:capture_15
system:IN-ADAT2.8
alsa_pcm:hw:1,0:out16
system:capture_16
system:IN-ADAT3.1
alsa_pcm:hw:1,0:out17
system:capture_17
system:IN-ADAT3.2
alsa_pcm:hw:1,0:out18
system:capture_18
system:IN-ADAT3.3
alsa_pcm:hw:1,0:out19
system:capture_19
system:IN-ADAT3.4
alsa_pcm:hw:1,0:out20
system:capture_20
system:IN-ADAT3.5
alsa_pcm:hw:1,0:out21
system:capture_21
system:IN-ADAT3.6
alsa_pcm:hw:1,0:out22
system:capture_22
system:IN-ADAT3.7
alsa_pcm:hw:1,0:out23
system:capture_23
system:IN-ADAT3.8
alsa_pcm:hw:1,0:out24
system:capture_24
system:IN-ADAT4.1
alsa_pcm:hw:1,0:out25
system:capture_25
system:IN-ADAT4.2
alsa_pcm:hw:1,0:out26
system:capture_26
system:IN-ADAT4.3
alsa_pcm:hw:1,0:out27
system:capture_27
system:IN-ADAT4.4
alsa_pcm:hw:1,0:out28
system:capture_28
system:IN-ADAT4.5
alsa_pcm:hw:1,0:out29
system:capture_29
system:IN-ADAT4.6
alsa_pcm:hw:1,0:out30
system:capture_30
system:IN-ADAT4.7
alsa_pcm:hw:1,0:out31
system:capture_31
system:IN-ADAT4.8
alsa_pcm:hw:1,0:out32
system:capture_32
system:IN-AES.L
alsa_pcm:hw:1,0:out33
system:capture_33
system:IN-AES.R
alsa_pcm:hw:1,0:out34
system:capture_34
system:IN-SPDIF.L
alsa_pcm:hw:1,0:out35
system:capture_35
system:IN-SPDIF.R
alsa_pcm:hw:1,0:out36
system:capture_36
system:ADAT1.1
alsa_pcm:hw:1,0:in1
system:playback_1
system:ADAT1.2
alsa_pcm:hw:1,0:in2
system:playback_2
system:ADAT1.3
alsa_pcm:hw:1,0:in3
system:playback_3
system:ADAT1.4
alsa_pcm:hw:1,0:in4
system:playback_4
system:ADAT1.5
alsa_pcm:hw:1,0:in5
system:playback_5
system:ADAT1.6
alsa_pcm:hw:1,0:in6
system:playback_6
system:ADAT1.7
alsa_pcm:hw:1,0:in7
system:playback_7
system:ADAT1.8
alsa_pcm:hw:1,0:in8
system:playback_8
system:ADAT2.1
alsa_pcm:hw:1,0:in9
system:playback_9
system:ADAT2.2
alsa_pcm:hw:1,0:in10
system:playback_10
system:ADAT2.3
alsa_pcm:hw:1,0:in11
system:playback_11
system:ADAT2.4
alsa_pcm:hw:1,0:in12
system:playback_12
system:ADAT2.5
alsa_pcm:hw:1,0:in13
system:playback_13
system:ADAT2.6
alsa_pcm:hw:1,0:in14
system:playback_14
system:ADAT2.7
alsa_pcm:hw:1,0:in15
system:playback_15
system:ADAT2.8
alsa_pcm:hw:1,0:in16
system:playback_16
system:ADAT3.1
alsa_pcm:hw:1,0:in17
system:playback_17
system:ADAT3.2
alsa_pcm:hw:1,0:in18
system:playback_18
system:ADAT3.3
alsa_pcm:hw:1,0:in19
system:playback_19
system:ADAT3.4
alsa_pcm:hw:1,0:in20
system:playback_20
system:ADAT3.5
alsa_pcm:hw:1,0:in21
system:playback_21
system:ADAT3.6
alsa_pcm:hw:1,0:in22
system:playback_22
system:ADAT3.7
alsa_pcm:hw:1,0:in23
system:playback_23
system:ADAT3.8
alsa_pcm:hw:1,0:in24
system:playback_24
system:ADAT4.1
alsa_pcm:hw:1,0:in25
system:playback_25
system:ADAT4.2
alsa_pcm:hw:1,0:in26
system:playback_26
system:ADAT4.3
alsa_pcm:hw:1,0:in27
system:playback_27
system:ADAT4.4
alsa_pcm:hw:1,0:in28
system:playback_28
system:ADAT4.5
alsa_pcm:hw:1,0:in29
system:playback_29
system:ADAT4.6
alsa_pcm:hw:1,0:in30
system:playback_30
system:ADAT4.7
alsa_pcm:hw:1,0:in31
system:playback_31
system:ADAT4.8
alsa_pcm:hw:1,0:in32
system:playback_32
system:AES.L
alsa_pcm:hw:1,0:in33
system:playback_33
system:AES.R
alsa_pcm:hw:1,0:in34
system:playback_34
system:SPDIF.L
alsa_pcm:hw:1,0:in35
system:playback_35
system:SPDIF.R
alsa_pcm:hw:1,0:in36
system:playback_36
1320317666.13694_0.ltw:2,a <4EB272B5.9020108 at drcomp dot erfurt dot thur dot de>