[nexuiz-commits] r6949 - trunk/misc/tools
DONOTREPLY at icculus.org
DONOTREPLY at icculus.org
Tue Jun 9 15:17:49 EDT 2009
Author: div0
Date: 2009-06-09 15:17:48 -0400 (Tue, 09 Jun 2009)
New Revision: 6949
Added:
trunk/misc/tools/midi2cfg-ng.conf
trunk/misc/tools/midi2cfg-ng.pl
Log:
midi2cfg-ng: a rewrite of midi2cfg using a config file, to later support percussion easily
Added: trunk/misc/tools/midi2cfg-ng.conf
===================================================================
--- trunk/misc/tools/midi2cfg-ng.conf (rev 0)
+++ trunk/misc/tools/midi2cfg-ng.conf 2009-06-09 19:17:48 UTC (rev 6949)
@@ -0,0 +1,349 @@
+bot tuba
+ count 0
+ init
+ time -2
+ cmd selectweapon 16
+ note on -18
+ time -0.05
+ buttons left backward crouch
+ time 0
+ buttons attack1 left backward crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -17
+ time -0.05
+ buttons backward crouch
+ time 0
+ buttons attack1 backward crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -16
+ time -0.05
+ buttons backward right crouch
+ time 0
+ buttons attack1 backward right crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -13
+ time -0.05
+ buttons forward right crouch
+ time 0
+ buttons attack1 forward right crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -12
+ time -0.05
+ buttons crouch
+ time 0
+ buttons attack1 crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -11
+ time -0.05
+ buttons left backward crouch
+ time 0
+ buttons attack2 left backward crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -10
+ time -0.05
+ buttons right crouch
+ time 0
+ buttons attack1 right crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -9
+ time -0.05
+ buttons forward left crouch
+ time 0
+ buttons attack1 forward left crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -8
+ time -0.05
+ buttons forward crouch
+ time 0
+ buttons attack1 forward crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -7
+ time -0.05
+ buttons left crouch
+ time 0
+ buttons attack1 left crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -6
+ time -0.05
+ buttons left backward
+ time 0
+ buttons attack1 left backward
+ time 0.05
+ buttons
+ busy 0.25
+ note on -5
+ time -0.05
+ buttons backward
+ time 0
+ buttons attack1 backward
+ time 0.05
+ buttons
+ busy 0.25
+ note on -4
+ time -0.05
+ buttons backward right
+ time 0
+ buttons attack1 backward right
+ time 0.05
+ buttons
+ busy 0.25
+ note on -3
+ time -0.05
+ buttons right crouch
+ time 0
+ buttons attack2 right crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -2
+ time -0.05
+ buttons forward left crouch
+ time 0
+ buttons attack2 forward left crouch
+ time 0.05
+ buttons
+ busy 0.25
+ note on -1
+ time -0.05
+ buttons forward left
+ time 0
+ buttons attack1 forward left
+ time 0.05
+ buttons
+ busy 0.25
+ note on 0
+ time -0.05
+ buttons
+ time 0
+ buttons attack1
+ time 0.05
+ buttons
+ busy 0.25
+ note on 1
+ time -0.05
+ buttons left backward
+ time 0
+ buttons attack2 left backward
+ time 0.05
+ buttons
+ busy 0.25
+ note on 2
+ time -0.05
+ buttons right
+ time 0
+ buttons attack1 right
+ time 0.05
+ buttons
+ busy 0.25
+ note on 3
+ time -0.05
+ buttons forward left
+ time 0
+ buttons attack1 forward left
+ time 0.05
+ buttons
+ busy 0.25
+ note on 4
+ time -0.05
+ buttons forward
+ time 0
+ buttons attack1 forward
+ time 0.05
+ buttons
+ busy 0.25
+ note on 5
+ time -0.05
+ buttons left
+ time 0
+ buttons attack1 left
+ time 0.05
+ buttons
+ busy 0.25
+ note on 6
+ time -0.05
+ buttons forward right
+ time 0
+ buttons attack2 forward right
+ time 0.05
+ buttons
+ busy 0.25
+ note on 7
+ time -0.05
+ buttons
+ time 0
+ buttons attack2
+ time 0.05
+ buttons
+ busy 0.25
+ note on 8
+ time -0.05
+ buttons backward right jump
+ time 0
+ buttons attack1 backward right jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 9
+ time -0.05
+ buttons right
+ time 0
+ buttons attack2 right
+ time 0.05
+ buttons
+ busy 0.25
+ note on 10
+ time -0.05
+ buttons forward left
+ time 0
+ buttons attack2 forward left
+ time 0.05
+ buttons
+ busy 0.25
+ note on 11
+ time -0.05
+ buttons forward
+ time 0
+ buttons attack2 forward
+ time 0.05
+ buttons
+ busy 0.25
+ note on 12
+ time -0.05
+ buttons left
+ time 0
+ buttons attack2 left
+ time 0.05
+ buttons
+ busy 0.25
+ note on 13
+ time -0.05
+ buttons left backward jump
+ time 0
+ buttons attack2 left backward jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 14
+ time -0.05
+ buttons right jump
+ time 0
+ buttons attack1 right jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 15
+ time -0.05
+ buttons forward left jump
+ time 0
+ buttons attack1 forward left jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 16
+ time -0.05
+ buttons forward jump
+ time 0
+ buttons attack1 forward jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 17
+ time -0.05
+ buttons left jump
+ time 0
+ buttons attack1 left jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 18
+ time -0.05
+ buttons forward right jump
+ time 0
+ buttons attack2 forward right jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 19
+ time -0.05
+ buttons jump
+ time 0
+ buttons attack2 jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 21
+ time -0.05
+ buttons right jump
+ time 0
+ buttons attack2 right jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 22
+ time -0.05
+ buttons forward left jump
+ time 0
+ buttons attack2 forward left jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 23
+ time -0.05
+ buttons forward jump
+ time 0
+ buttons attack2 forward jump
+ time 0.05
+ buttons
+ busy 0.25
+ note on 24
+ time -0.05
+ buttons left jump
+ time 0
+ buttons attack2 left jump
+ time 0.05
+ buttons
+ busy 0.25
+
+bot tuba_red
+ include tuba
+ transpose 0
+ init
+ time -4
+ cmd cc "color 68"
+ super
+
+bot tuba_blue
+ include tuba
+ transpose 3
+ init
+ time -4
+ cmd cc "color 221"
+ super
+
+bot red
+ include tuba_red
+ count 256
+
+bot blue
+ include tuba_blue
+ count 256
Added: trunk/misc/tools/midi2cfg-ng.pl
===================================================================
--- trunk/misc/tools/midi2cfg-ng.pl (rev 0)
+++ trunk/misc/tools/midi2cfg-ng.pl 2009-06-09 19:17:48 UTC (rev 6949)
@@ -0,0 +1,412 @@
+#!/usr/bin/perl
+
+# converter from Type 1 MIDI files to CFG files that control bots with the Tuba and other weapons for percussion (requires g_weaponarena all)
+# usage:
+# perl midi2cfg.pl filename.mid basenote walktime "x y z" "x y z" "x y z" ... "/" "x y z" "x y z" ... > filename.cfg
+
+use strict;
+use warnings;
+use MIDI;
+use MIDI::Opus;
+use Storable;
+
+use constant MIDI_FIRST_NONCHANNEL => 17;
+use constant MIDI_DRUMS_CHANNEL => 10;
+
+my ($filename, $transpose, $timeoffset) = @ARGV;
+
+my $opus = MIDI::Opus->new({from_file => $filename});
+#$opus->write_to_file("/tmp/y.mid");
+my $ticksperquarter = $opus->ticks();
+my $tracks = $opus->tracks_r();
+my @tempi = (); # list of start tick, time per tick pairs (calculated as seconds per quarter / ticks per quarter)
+my $tick;
+
+$tick = 0;
+for($tracks->[0]->events())
+{
+ $tick += $_->[1];
+ if($_->[0] eq 'set_tempo')
+ {
+ push @tempi, [$tick, $_->[2] * 0.000001 / $ticksperquarter];
+ }
+}
+sub tick2sec($)
+{
+ my ($tick) = @_;
+ my $sec = 0;
+ my $curtempo = [0, 0.5 / $ticksperquarter];
+ for(@tempi)
+ {
+ if($_->[0] < $tick)
+ {
+ # this event is in the past
+ # we add the full time since the last one then
+ $sec += ($_->[0] - $curtempo->[0]) * $curtempo->[1];
+ }
+ else
+ {
+ # if this event is in the future, we break
+ last;
+ }
+ $curtempo = $_;
+ }
+ $sec += ($tick - $curtempo->[0]) * $curtempo->[1];
+ return $sec;
+}
+
+# merge all to a single track
+my @allmidievents = ();
+my $sequence = 0;
+for my $track(0..@$tracks-1)
+{
+ $tick = 0;
+ for($tracks->[$track]->events())
+ {
+ my ($command, $delta, @data) = @$_;
+ $tick += $delta;
+ push @allmidievents, [$command, $tick, $sequence++, $track, @data];
+ }
+}
+ at allmidievents = sort { $a->[1] <=> $b->[1] or $a->[2] <=> $b->[2] } @allmidievents;
+
+
+
+
+
+
+sub botconfig_read($)
+{
+ my ($fn) = @_;
+ my %bots = ();
+ open my $fh, "<", $fn
+ or die "<$fn: $!";
+
+ my $currentbot = undef;
+ my $appendref = undef;
+ my $super = undef;
+ while(<$fh>)
+ {
+ chomp;
+ s/\s*#.*//;
+ next if /^$/;
+ if(s/^\t\t//)
+ {
+ my @cmd = split /\s+/, $_;
+ if($cmd[0] eq 'super')
+ {
+ push @$appendref, @$super;
+ }
+ else
+ {
+ push @$appendref, \@cmd;
+ }
+ }
+ elsif(s/^\t//)
+ {
+ if(/^include (.*)/)
+ {
+ my $base = $bots{$1};
+ for(keys %$base)
+ {
+ if(ref $base->{$_})
+ {
+ $currentbot->{$_} = Storable::dclone $base->{$_}; # copy array items as new array
+ }
+ else
+ {
+ $currentbot->{$_} = $base->{$_};
+ }
+ }
+ # better: do some merging TODO
+ }
+ elsif(/^count (\d+)/)
+ {
+ $currentbot->{count} = $1;
+ }
+ elsif(/^transpose (\d+)/)
+ {
+ $currentbot->{transpose} += $1;
+ }
+ elsif(/^channels (.*)/)
+ {
+ $currentbot->{channels} = { map { $_ => 1 } split /\s+/, $1 };
+ }
+ elsif(/^init$/)
+ {
+ $super = $currentbot->{init};
+ $currentbot->{init} = $appendref = [];
+ }
+ elsif(/^note on (-?\d+)/)
+ {
+ $super = $currentbot->{notes_on}->{$1};
+ $currentbot->{notes_on}->{$1} = $appendref = [];
+ }
+ elsif(/^note off (-?\d+)/)
+ {
+ $super = $currentbot->{notes_off}->{$1};
+ $currentbot->{notes_off}->{$1} = $appendref = [];
+ }
+ elsif(/^percussion (\d+)/)
+ {
+ $super = $currentbot->{percussion}->{$1};
+ $currentbot->{percussion}->{$1} = $appendref = [];
+ }
+ else
+ {
+ print "unknown command: $_\n";
+ }
+ }
+ elsif(/^bot (.*)/)
+ {
+ $currentbot = ($bots{$1} ||= {});
+ }
+ else
+ {
+ print "unknown command: $_\n";
+ }
+ }
+
+ my $lowesttimeoffset = 0;
+ for(values %bots)
+ {
+ my $l = $_->{init};
+ next unless defined $l;
+ next unless $l->[0]->[0] eq 'time';
+ my $t = $l->[0]->[1];
+ $lowesttimeoffset = $t
+ if $t < $lowesttimeoffset;
+ }
+ print STDERR "Using a time adjustment of $lowesttimeoffset\n";
+ $timeoffset -= $lowesttimeoffset;
+
+ return \%bots;
+}
+
+sub busybot_cmd_bot_test($$@)
+{
+ my ($bot, $time, @commands) = @_;
+
+ my $bottime = defined $bot->{timer} ? $bot->{timer} : -$timeoffset-1;
+ my $botbusytime = defined $bot->{busytimer} ? $bot->{busytimer} : -$timeoffset-1;
+
+ return 0
+ if $time < $botbusytime;
+
+ my $mintime = (@commands && ($commands[0]->[0] eq 'time')) ? $commands[0]->[1] : 0;
+
+ return 0
+ if $time + $mintime < $bottime;
+
+ return 1;
+}
+
+sub busybot_cmd_bot_execute($$@)
+{
+ my ($bot, $time, @commands) = @_;
+
+ for(@commands)
+ {
+ if($_->[0] eq 'time')
+ {
+ printf "w %d %f\n", $bot->{id}, $time + $_->[1] + $timeoffset;
+ $bot->{timer} = $time + $_->[1];
+ }
+ elsif($_->[0] eq 'busy')
+ {
+ $bot->{busytimer} = $time + $_->[1];
+ }
+ elsif($_->[0] eq 'buttons')
+ {
+ my %buttons_release = %{$bot->{buttons} ||= {}};
+ for(@{$_}[1..@$_-1])
+ {
+ /(.*)\??/ or next;
+ delete $buttons_release{$1};
+ }
+ for(keys %buttons_release)
+ {
+ printf "r %d %s\n", $bot->{id}, $_;
+ delete $bot->{buttons}->{$_};
+ }
+ for(@{$_}[1..@$_-1])
+ {
+ /(.*)(\?)?/ or next;
+ defined $2 and next;
+ printf "p %d %s\n", $bot->{id}, $_;
+ $bot->{buttons}->{$_} = 1;
+ }
+ }
+ elsif($_->[0] eq 'cmd')
+ {
+ printf "sv_cmd bot_cmd %d %s\n", $bot->{id}, join " ", @{$_}[1..@$_-1];
+ }
+ }
+
+ return 1;
+}
+
+sub busybot_note_off_bot($$$$)
+{
+ my ($bot, $time, $channel, $note) = @_;
+ my $cmds = $bot->{notes_off}->{$note - $bot->{transpose} - $transpose};
+ return 1
+ if not defined $cmds; # note off cannot fail
+ busybot_cmd_bot_execute $bot, $time, @$cmds;
+ return 1;
+}
+
+sub busybot_note_on_bot($$$$$)
+{
+ my ($bot, $time, $channel, $note, $init) = @_;
+ return -1 # I won't play on this channel
+ if defined $bot->{channels} and not grep { $_ == $channel } $bot->{channels};
+ my $cmds = $bot->{notes_on}->{$note - $bot->{transpose} - $transpose};
+ if(not defined $cmds)
+ {
+ $cmds = $bot->{percussion}->{$note};
+ return -1 # I won't play this note
+ if not defined $cmds;
+ }
+ if($init && $bot->{init})
+ {
+ return 0
+ if not busybot_cmd_bot_test $bot, 0, @{$bot->{init}};
+ return 0
+ if not busybot_cmd_bot_test $bot, $time, @$cmds;
+ busybot_cmd_bot_execute $bot, 0, @{$bot->{init}};
+ busybot_cmd_bot_execute $bot, $time, @$cmds;
+ }
+ else
+ {
+ return 0
+ if not busybot_cmd_bot_test $bot, $time, @$cmds;
+ busybot_cmd_bot_execute $bot, $time, @$cmds;
+ }
+ return 1;
+}
+
+my $busybots = botconfig_read "midi2cfg-ng.conf";
+my @busybots_allocated;
+my %notechannelbots;
+
+sub busybot_note_off($$$)
+{
+ my ($time, $channel, $note) = @_;
+
+ if(my $bot = $notechannelbots{$channel}{$note})
+ {
+ busybot_note_off_bot $bot, $time, $channel, $note;
+ delete $notechannelbots{$channel}{$note};
+ return 1;
+ }
+
+ return 0;
+}
+
+sub busybot_note_on($$$)
+{
+ my ($time, $channel, $note) = @_;
+
+ if($notechannelbots{$channel}{$note})
+ {
+ busybot_note_off $time, $channel, $note;
+ }
+
+ my $overflow = 0;
+
+ for(@busybots_allocated)
+ {
+ my $canplay = busybot_note_on_bot $_, $time, $channel, $note, 0;
+ if($canplay > 0)
+ {
+ $notechannelbots{$channel}{$note} = $_;
+ return 1;
+ }
+ $overflow = 1
+ if $canplay == 0;
+ # wrong
+ }
+
+ for(map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, rand] } keys %$busybots)
+ {
+ next if $busybots->{$_}->{count} <= 0;
+ my $bot = Storable::dclone $busybots->{$_};
+ $bot->{id} = @busybots_allocated + 1;
+ $bot->{classname} = $_;
+ my $canplay = busybot_note_on_bot $bot, $time, $channel, $note, 1;
+ if($canplay > 0)
+ {
+ --$busybots->{$_}->{count};
+ $notechannelbots{$channel}{$note} = $bot;
+ push @busybots_allocated, $bot;
+ return 1;
+ }
+ $overflow = 1
+ if $canplay == 0;
+ }
+
+ if($overflow)
+ {
+ warn "Not enough bots to play this";
+ use Data::Dumper;
+ print STDERR Dumper \@busybots_allocated;
+ }
+ else
+ {
+ warn "Note $channel:$note cannot be played by any bot"
+ }
+
+ return 0;
+}
+
+print 'alias p "sv_cmd bot_cmd $1 presskey $2"' . "\n";
+print 'alias r "sv_cmd bot_cmd $1 releasekey $2"' . "\n";
+print 'alias w "sv_cmd bot_cmd $1 wait_until $2"' . "\n";
+print 'alias m "sv_cmd bot_cmd $1 moveto \"$2 $3 $4\""' . "\n";
+
+my %midinotes = ();
+my $note_min = undef;
+my $note_max = undef;
+my $notes_stuck = 0;
+for(@allmidievents)
+{
+ my $t = tick2sec $_->[1];
+ my $track = $_->[3];
+ if($_->[0] eq 'note_on')
+ {
+ my $chan = $_->[4] + 1;
+ $note_min = $_->[5]
+ if not defined $note_min or $_->[5] < $note_min;
+ $note_max = $_->[5]
+ if not defined $note_max or $_->[5] > $note_max;
+ if($midinotes{$chan}{$_->[5]})
+ {
+ --$notes_stuck;
+ busybot_note_off($t, $chan, $_->[5]);
+ }
+ busybot_note_on($t, $chan, $_->[5]);
+ ++$notes_stuck;
+ $midinotes{$chan}{$_->[5]} = 1;
+ }
+ elsif($_->[0] eq 'note_off')
+ {
+ my $chan = $_->[4] + 1;
+ if($midinotes{$chan}{$_->[5]})
+ {
+ --$notes_stuck;
+ busybot_note_off($t, $chan, $_->[5]);
+ }
+ $midinotes{$chan}{$_->[5]} = 0;
+ }
+}
+
+print STDERR "Range of notes: $note_min .. $note_max\n";
+print STDERR "Safe transpose range: @{[$note_max - 19]} .. @{[$note_min + 13]}\n";
+print STDERR "Unsafe transpose range: @{[$note_max - 27]} .. @{[$note_min + 18]}\n";
+print STDERR "Stuck notes: $notes_stuck\n";
+print STDERR "Bots allocated:\n";
+for(@busybots_allocated)
+{
+ print STDERR "$_->{id} is a $_->{classname}\n";
+}
Property changes on: trunk/misc/tools/midi2cfg-ng.pl
___________________________________________________________________
Name: svn:executable
+ *
More information about the nexuiz-commits
mailing list