-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathcapture-midi
122 lines (101 loc) · 3.75 KB
/
capture-midi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#!/usr/bin/env perl
# Capture MIDI events and send text or keystrokes to the focused window.
# Write-up: https://ology.github.io/2023/05/08/control-linux-with-midi/
# Please see the config file: capture-midi.yaml in this same directory.
# Also, this program is Linux-only. You'll need aseqdump and xdotool.
use strict;
use warnings;
use YAML::XS qw(LoadFile);
my $config_file = shift || "$0.yaml";
my $config = LoadFile($config_file);
my $DEBUG = $config->{debug};
my $device = $config->{device};
my @dump_tool = qw(aseqdump -p);
my @type_tool = qw(xdotool type);
my @key_tool = qw(xdotool key);
my $last = '';
my $direction = '';
open(my $midi, '-|', @dump_tool, $device)
or die "Can't fork: $!";
warn "PID: $$\n" if $DEBUG;
while (my $line = readline($midi)) {
# print $line; next;
chomp $line;
$line =~ s/^\s*//;
my @parts = split /(?:\s{2,}|,\s+)/, $line;
my $event = $parts[1];
next unless $event;
warn "Ev: $event\n" if $DEBUG;
my ($channel, $data, $value);
my @cmd;
# capture the MIDI data
if ($event =~ /^Note[ -]on$/i
|| $event =~ /^\w+[ -]change$/
|| $event =~ /Pitch[ -]bend/i
|| $event =~ /Channel[ -](?:aftertouch|pressure)/i
) {
$channel = $parts[2];
$data = $parts[3];
$data .= ", $parts[4]" if defined $parts[4];
warn "Ch: $channel | Data: $data\n" if $DEBUG;
}
elsif ($event =~ /System[ -]exclusive/i) {
$data = $parts[-1];
warn "Data: $data\n" if $DEBUG;
}
# find a matching trigger
for my $entry ($config->{triggers}->@*) {
# the events are equal, the data matches and either there is no channel or the channels are equal
if ($event eq $entry->{event} && $data =~ /$entry->{data}/
&& (!defined $entry->{channel} || $channel == $entry->{channel})
) {
warn "Match: $entry->{event}: '$entry->{data}'\n" if $DEBUG;
$value = $1; # XXX What is $1 here??
my ($key, $text);
# handle entry directions
if (defined $value && exists $entry->{down} && exists $entry->{up}) {
$direction = 'down' if $last ne '' && $value < $last;
$direction = 'up' if $last ne '' && $value > $last;
warn "Value: $value, Last: $last, Direction: $direction\n" if $DEBUG;
$key = $entry->{$direction}{key} if exists $entry->{$direction}{key};
$text = $entry->{$direction}{text} if exists $entry->{$direction}{text};
$last = $value if $last eq '' || $last != $value;
}
else {
# set key (default) & text
$key = $entry->{key} if exists $entry->{key};
$text = $entry->{text} if exists $entry->{text};
}
# build the command
if (defined $key) {
@cmd = (@key_tool, $key);
}
elsif (defined $text) {
@cmd = (@type_tool, "$text\n");
}
last;
}
}
# execute the trigger
if (@cmd) {
system(@cmd) == 0
or die "system(@cmd) failed: $?";
}
}
close $midi
or die "Bad @dump_tool: $! $?";
__END__
# aseqdump:
Source Event Ch Data
20:0 Note on 0, note 60, velocity 58
20:0 Note off 0, note 60, velocity 0
20:0 Program change 15, program 31
20:0 System exclusive F0 7F 7F 06 01 F7
20:0 Song position pointer value 0
20:0 Start
20:0 Stop
20:0 Control change 15, controller 28, value 127
20:0 Control change 15, controller 28, value 0
20:0 Channel aftertouch 9, value 127
20:0 Channel aftertouch 9, value 113
20:0 Channel aftertouch 9, value 0