summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xscripts/midi_to_matrix.pl136
1 files changed, 136 insertions, 0 deletions
diff --git a/scripts/midi_to_matrix.pl b/scripts/midi_to_matrix.pl
new file mode 100755
index 0000000000..e67f344c67
--- /dev/null
+++ b/scripts/midi_to_matrix.pl
@@ -0,0 +1,136 @@
+#!/opt/local/bin/perl
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use IO::Async::Loop;
+use Net::Async::Matrix;
+use Net::Pcap;
+use Data::HexDump;
+use JSON;
+use Music::Chord::Namer qw/chordname/;
+  
+$| = 1;
+
+our $notes = {};
+our $notenames = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
+
+our $room;
+
+my $loop = IO::Async::Loop->new;
+my $matrix = Net::Async::Matrix->new(
+    server => "echo-matrix:8008",
+    on_log => sub { warn "log: @_\n" },
+    on_room_new => sub {
+      my ($matrix, $new_room) = @_;
+      warn "[Matrix] have a room ID: " . $new_room->room_id . "\n";
+      $room = $new_room if ($new_room->room_id eq '!GaUcuyvZyXfoqmQTNR:echo-matrix');
+    },
+    on_error => sub {
+      print STDERR "Matrix failure: @_\n";
+    },
+);
+
+$loop->add( $matrix );
+$matrix->login(
+    # XXX: password is broke
+    user_id      => 'matthew',
+    access_token => 'QG1hdHRoZXc6ZWNoby1tYXRyaXg..ZVZbCQuOmnhwakNnOt',
+)->get;
+
+$matrix->join_room( '#midi:echo-matrix' )->get;
+# ->on_done(sub {
+#     print Dumper([@_]);
+#     ($room) = @_;
+#     warn "joined $room";
+# } )->get;
+
+$matrix->start();
+
+my $err = '';
+my $dev = "en1";
+
+my $pcap = pcap_open_live($dev, 1024, 0, 100, \$err);
+die $err if $err;
+
+my ($net, $mask);
+pcap_lookupnet($dev, \$net, \$mask, \$err);
+die $err if $err;
+
+my $filter_str = "src host 10.12.76.65 and udp and port 5005";
+my $filter;
+if (pcap_compile($pcap, \$filter, $filter_str, 1, $net) == -1) {
+    die "Unable to compile filter string '$filter_str'\n";
+}
+
+pcap_setfilter($pcap, $filter);
+
+while (1) {
+    pcap_dispatch($pcap, -1, \&process_packet, "");
+    #print ".\n";
+}
+
+pcap_close($pcap);
+
+sub handle_event {
+    my ($event) = @_;
+    print to_json($event, { pretty => 1 });
+    
+    if ($event->{state} eq 'on') {
+        $notes->{$event->{note}} = 1;
+    }
+    else {
+        delete $notes->{$event->{note}};
+    }
+    
+    if (scalar keys %$notes >= 3) {
+        my $chord = (chordname(map { $notenames->[$_ % 12] } sort keys %$notes))[0];
+        print "$chord\n";
+        $room->send_message( $chord )->get;
+    }
+    
+    # HACK HACK HACK HACK
+    $room->_do_POST_json( "/send/org.matrix.midi", $event )->get;
+}
+
+sub process_packet {
+    my ($user_data, $header, $packet) = @_;
+    my ($ether, $ip, $udp, $rtp_byte, $payload, $seqnum, $ts, $ssrc, @midi)
+        = unpack("a14a20a8CCSNNC*", $packet);
+    
+    return if ($rtp_byte == 0xff);
+    #print HexDump $packet;
+    
+    my $midilen;
+    if ($midi[0] & 0x80) { # long header
+        $midilen = (($midi[0] & 0x0f) << 8) | $midi[1];
+        shift @midi;
+        shift @midi;
+    }
+    else { # short header
+        $midilen = ($midi[0] & 0x0f);
+        shift @midi;
+    }
+
+    my $midiparsed = 0;
+    my $state = ($midi[0] >> 4 == 0x9 ? "on" : "off");
+    my $channel = ($midi[0] & 0x0f) + 1;
+    shift (@midi); $midiparsed++;
+    
+    while ($midiparsed < $midilen) {
+        my ($event) = {
+            midi_ts => $ts,
+            note => $midi[0],
+            channel => $channel,
+            state => ($midi[1] == 0 ? "off" : $state),
+            velocity => $midi[1],
+        };
+        handle_event($event);
+        shift (@midi); $midiparsed++;
+        shift (@midi); $midiparsed++;
+        if (scalar @midi) {
+            $ts += shift @midi; $midiparsed++;
+        }
+    }
+}