synapse-old/scripts/midi_to_matrix.pl

137 lines
3.2 KiB
Perl
Executable File

#!/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++;
}
}
}