1+ """
2+ Connect all midi devices to each other!
3+ """
4+
5+ import sys
6+ import logging
7+ import subprocess
8+ import re
9+
10+ __author__ = 'Stephen Brown (Little Fish Solutions LTD)'
11+
12+
13+ if sys .version_info .major < 3 :
14+ sys .exit ('Sorry, this script only supports Python 3' )
15+
16+ log = logging .getLogger (__name__ )
17+
18+ VERSION = '0.0.0'
19+
20+ VERBOSE = False
21+
22+ if __name__ == '__main__' :
23+ if VERBOSE :
24+ logging .basicConfig (level = logging .DEBUG )
25+ else :
26+ logging .basicConfig (level = logging .INFO , format = '%(message)s' )
27+
28+ log .info ('Midi Connect v{}' .format (VERSION ))
29+
30+ # Run aconnect and get the output
31+ aconnect_process = subprocess .run (['aconnect' , '-i' , '-l' ], stdout = subprocess .PIPE ,
32+ stderr = subprocess .STDOUT )
33+
34+ # Keep a list of clients
35+ clients = {}
36+
37+ # Keep a list of existing mappings (from, to)
38+ existing_connections = []
39+
40+ def add_client_port (client_number , port_number ):
41+ if client_number not in clients :
42+ clients [client_number ] = []
43+
44+ clients [client_number ].append (port_number )
45+
46+ # Regular expressions used for parsing
47+ client_regex = re .compile (r'^client ([0-9]+): \'([^\']*)\'.*$' )
48+ port_regex = re .compile (r'^\s*([0-9]+) \'([^\']*)\'.*$' )
49+ connecting_to_regex = re .compile (r'^\s*Connecting To: (.*)$' )
50+ connecting_from_regex = re .compile (r'^\sConnected From:.*$' )
51+
52+ client_number = None
53+ client_name = None
54+ skip_client = False
55+
56+ # break up into lines for processing
57+ lines = aconnect_process .stdout .decode ().split ('\n ' )
58+ for line in lines :
59+ if not line .strip ():
60+ continue
61+
62+ client_match = client_regex .match (line )
63+ if client_match :
64+ client_number = client_match .group (1 )
65+ client_name = client_match .group (2 )
66+
67+ log .debug ('Found client {} "{}"' .format (client_number , client_name ))
68+
69+ skip_client = client_name in ['System' , 'Midi Through' ]
70+ if skip_client :
71+ log .debug (' - Skipping client "{}"' .format (client_name ))
72+
73+ continue
74+
75+ port_match = port_regex .match (line )
76+ if port_match :
77+ if client_number is None :
78+ raise Exception ('Found port without client: {}' .format (line ))
79+
80+ if skip_client :
81+ continue
82+
83+ port_number = port_match .group (1 )
84+ port_name = port_match .group (2 )
85+
86+ log .debug (' - Found port {} "{}"' .format (port_number , port_name ))
87+ add_client_port (client_number , port_number )
88+
89+ continue
90+
91+ connecting_to_match = connecting_to_regex .match (line )
92+ if connecting_to_match :
93+ connecting_from = '{}:{}' .format (client_number , port_number )
94+ connecting_to_str = connecting_to_match .group (1 )
95+ connecting_to_list = [s .strip () for s in connecting_to_str .split (',' )]
96+ for connecting_to in connecting_to_list :
97+ log .debug ('Existing connection {} -> {}' .format (connecting_from , connecting_to ))
98+ existing_connections .append ((connecting_from , connecting_to ))
99+
100+ continue
101+
102+ connecting_from_match = connecting_from_regex .match (line )
103+ if connecting_from_match :
104+ continue
105+
106+ raise Exception ('Unhandled line: {}' .format (line ))
107+
108+ # We now have our clients dict
109+ for (from_client_number , from_ports ) in clients .items ():
110+ for (to_client_number , to_ports ) in clients .items ():
111+ if from_client_number == to_client_number :
112+ # Don't map things onto themselves
113+ continue
114+
115+ for from_port in from_ports :
116+ for to_port in to_ports :
117+ from_str = '{}:{}' .format (from_client_number , from_port )
118+ to_str = '{}:{}' .format (to_client_number , to_port )
119+
120+ if (from_str , to_str ) in existing_connections :
121+ log .info ('{} is already connected to {} - skipping' .format (
122+ from_str , to_str
123+ ))
124+
125+ continue
126+
127+ log .info ('Mapping {} -> {}' .format (from_str , to_str ))
128+
129+ map_process = subprocess .run ([
130+ 'aconnect' , from_str , to_str
131+ ], stdout = subprocess .PIPE , stderr = subprocess .STDOUT )
132+
133+ output = map_process .stdout
134+ if output :
135+ log .warning (output .decode ())
136+
0 commit comments