lost stuff from the MIDI hackathon
parent
7779ecc10d
commit
125e130f61
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue