Skip to content

Commit feafce4

Browse files
committed
updating README, better exclusivity handling
1 parent 6bff597 commit feafce4

File tree

9 files changed

+77
-22
lines changed

9 files changed

+77
-22
lines changed

README.md

+41-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,46 @@
11
# midibus
22
My implementation of a MIDI bus to connect multiple synths
33

4+
## Legacy implementation
5+
46
Uses `udev` rules to make ALSA sequencer connections when plugged in, updates the UI when changes are made.
57

6-
<img src="Matrix.png" />
8+
## Latest incarnation
9+
10+
Still uses `udev` rules, but uses the ALSA RawMidi Interface to listen for
11+
incoming messages and relay them to output devices in a threaded server which
12+
handles SIGHUP when a new device is connected.
13+
14+
### But why?
15+
16+
- I want to get as close to parsing actual MIDI messages as possible
17+
without all the extra metadata attached to each event by ALSA.
18+
- This way I can implement my own filtering and custom macros.
19+
- Relay realtime messages from the Deluge, send all realtime messages from the
20+
Electribe to `/dev/null`.
21+
- Gracefully handle forwarding system exclusive messages so I can program my
22+
JUNO from the computer.
23+
24+
## What does my setup look like?
25+
26+
The concept of a "master keyboard" has stuck with me, and for this purpose I
27+
currently use the Roland JUNO-DS 88. Connected to my Raspberry Pi running this
28+
software, I have a Synthstrom Audible Deluge for
29+
sequencing and sound design, a Moog Little Phatty Stage II for warm analog synth
30+
sounds, a Novation Circuit for synth layering, a Korg Electribe Sampler for
31+
effects and drum layering, and finally an iConnectivity mio that acts as a
32+
bridge between my synth rig and my DAW computer.
33+
34+
## Screenshot
35+
36+
<img src="images/fbcat.png" />
37+
38+
## Client/server
39+
40+
Currently the client is extremely dumb, simply reading the server's config file.
41+
This will change in the future making it more interactive. But hey, at least it
42+
looks good, and you can see the current configuration presented visually.
43+
44+
The server has a thread for each input device, and maintains state for each
45+
output device in shared memory.
46+

Matrix.png images/Matrix.png

File renamed without changes.

images/fbcat.png

67 KB
Loading

server/main.c

+21-12
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,31 @@ void configure_connection(const char *in_name, const char *out_name, char *func_
3232

3333
void manage_inputs()
3434
{
35-
int i = 0;
36-
for(; i < n_read_threads; ++i )
35+
int tries = 0;
36+
int had_errors;
37+
do
3738
{
38-
if( read_data[i].midi != NULL )
39-
continue;
40-
if( strlen(read_data[i].port_name) <= 0 )
41-
continue;
42-
int result, tries = 0;
43-
while( (result = setup_midi_device( &read_data[i] ) ) != 0 && tries < 5 )
39+
had_errors = 0;
40+
int i = 0;
41+
for(; i < n_read_threads; ++i )
4442
{
45-
tries++;
46-
usleep(200*1000);
43+
if( read_data[i].midi != NULL )
44+
continue;
45+
if( strlen(read_data[i].port_name) <= 0 )
46+
continue;
47+
int result;
48+
result = setup_midi_device( &read_data[i] );
49+
if( result == 0 && read_data[i].midi != NULL )
50+
pthread_create( &threads[i], NULL, read_thread, (void *) &read_data[i] );
51+
else
52+
had_errors++;
4753
}
48-
if( result == 0 && read_data[i].midi != NULL )
49-
pthread_create( &threads[i], NULL, read_thread, (void *) &read_data[i] );
54+
55+
if( had_errors )
56+
usleep(400*1000);
57+
tries++;
5058
}
59+
while( had_errors && tries < 5 );
5160
}
5261

5362
void manage_outputs()

server/midi-server

-18.3 KB
Binary file not shown.

server/notes.txt

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
JUNO-DS switching to Performance mode, USER 01
2+
3+
BF 00 55 BF 20 00 CF 00
4+
5+
Ch16 CC 0 BANK MSB = 85
6+
Ch16 CC 32 BANK LSB = 0
7+
Ch16 PC = 00

server/thru.c

+6-7
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,13 @@ void *read_thread(void *arg)
127127
struct write_data *out = &data->outs[o];
128128
if( out->output_device == NULL || out->output_device->midi == NULL || out->func == NULL )
129129
continue;
130-
if( out->output_device->midi_in_exclusive == data )
131-
{
132-
write_thru( out, buf, err, out->args );
133-
}
134-
else if( out->func( out, buf, err, out->args ) )
135-
{
130+
snd_rawmidi_t *excl = out->output_device->midi_in_exclusive;
131+
132+
if( excl != NULL && excl != data->midi )
133+
continue;
134+
135+
if( out->func( out, buf, err, out->args ) )
136136
printf("%s\n", out->port_name);
137-
}
138137
}
139138
fflush(stdout);
140139
}

server/thru.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct output_device_t {
1919
struct write_data
2020
{
2121
struct output_device_t *output_device;
22-
struct snd_rawmidi_t *midi_in;
22+
snd_rawmidi_t *midi_in;
2323
const char *port_name;
2424
int (*func)();
2525
void *args;

server/write.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ void setup_write_func( struct write_data *data, char *name, char *args )
106106
else if( strcmp(pt, "rt") == 0 )
107107
channel_mask |= MASK_RT;
108108
else if( strcmp(pt, "all") == 0 )
109-
channel_mask |= MASK_ALL_CHANNELS;
109+
channel_mask |= MASK_ALL;
110110
}
111111
else
112112
channel_mask |= 1 << a;

0 commit comments

Comments
 (0)