my $timeoffset_postdone = 2;
my $timeoffset_preintermission = 2;
my $timeoffset_postintermission = 2;
+my $time_forgetfulness = 1.5;
my ($config, @midilist) = @ARGV;
{
$timeoffset_postintermission = $1;
}
+ elsif(/^time_forgetfulness (.*)/)
+ {
+ $time_forgetfulness = $1;
+ }
else
{
print "unknown command: $_\n";
$mintime = $_->[1]
if not defined $mintime or $_->[1] < $mintime;
$maxtime = $_->[1] + SYS_TICRATE
- if not defined $maxtime or $_->[1] > $maxtime;
+ if not defined $maxtime or $_->[1] + SYS_TICRATE > $maxtime;
}
elsif($_->[0] eq 'busy')
{
{
$commands .= sprintf "sv_cmd bot_cmd %d %s\n", $bot->{id}, join " ", @{$_}[1..@$_-1];
}
+ elsif($_->[0] eq 'aim_random')
+ {
+ $commands .= sprintf "sv_cmd bot_cmd %d aim %f 0 %f\n", $bot->{id}, $_->[1] + rand($_->[2] - $_->[1]), $_->[3];
+ }
elsif($_->[0] eq 'barrier')
{
$commands .= sprintf "sv_cmd bot_cmd %d barrier\n", $bot->{id};
$bot->{timer} = $bot->{busytimer} = 0;
+ undef $bot->{lastuse};
}
elsif($_->[0] eq 'raw')
{
my ($bot, $time, $channel, $program, $note, $init, $force) = @_;
return -1 # I won't play on this channel
if defined $bot->{channels} and not $bot->{channels}->{$channel};
-# return -1 # I won't play this program
-# if defined $bot->{programs} and not $bot->{programs}->{$program};
+ return -1 # I won't play this program
+ if defined $bot->{programs} and not $bot->{programs}->{$program};
my ($cmds, $cmds_off, $k0, $k1) = busybot_get_cmds_bot($bot, $channel, $note);
$bot->{busy} = [$channel, $note, $cmds_off];
}
++$bot->{seen}{$k0}{$k1};
+
+ if(($bot->{lastuse} // -666) >= $time - $time_forgetfulness && $channel == $bot->{lastchannel})
+ {
+ $bot->{lastchannelsequence} += 1;
+ }
+ else
+ {
+ $bot->{lastchannelsequence} = 1;
+ }
+ $bot->{lastuse} = $time;
+ $bot->{lastchannel} = $channel;
+
+# print STDERR "$time $bot->{id} $channel:$note\n"
+# if $channel == 11;
+
return 1;
}
return 0;
}
+sub botsort($$$$@)
+{
+ my ($time, $channel, $program, $note, @bots) = @_;
+ return
+ map
+ {
+ $_->[0]
+ }
+ sort
+ {
+ $b->[1] <=> $a->[1]
+ or
+ ($a->[0]->{lastuse} // -666) <=> ($b->[0]->{lastuse} // -666)
+ or
+ $a->[2] <=> $b->[2]
+ }
+ map
+ {
+ my $q = 0;
+ if($channel != 10) # percussion just should do round robin
+ {
+ if(($_->{lastuse} // -666) >= $time - $time_forgetfulness)
+ {
+ if($channel == $_->{lastchannel})
+ {
+ $q += $_->{lastchannelsequence};
+ }
+ else
+ {
+ # better leave this one alone
+ $q -= $_->{lastchannelsequence};
+ }
+ }
+ }
+ [$_, $q, rand]
+ }
+ @bots;
+}
+
sub busybot_note_on($$$$)
{
my ($time, $channel, $program, $note) = @_;
my @epicfailbots = ();
- for(unsort @busybots_allocated)
+ for(botsort $time, $channel, $program, $note, @busybots_allocated)
{
my $canplay = busybot_note_on_bot $_, $time, $channel, $program, $note, 0, 0;
if($canplay > 0)
next
unless $cmds;
my ($mintime, $maxtime, $busytime) = busybot_cmd_bot_cmdinfo @$cmds;
+ my ($mintime_off, $maxtime_off, $busytime_off) = busybot_cmd_bot_cmdinfo @$busy_cmds_off;
- my $noteofftime = busybot_cmd_bot_matchtime $bot, $time + $notetime + $mintime, $time, @$busy_cmds_off;
+ my $noteofftime = busybot_cmd_bot_matchtime $bot, $time + $notetime + $mintime, $time + $notetime, @$busy_cmds_off;
next
if $noteofftime < $bot->{busytimer};
next
- if $noteofftime + $mintime < $bot->{timer};
+ if $noteofftime + $mintime_off < $bot->{timer};
my $score = 0;
# prefer turning off long notes
my %notes_seen = ();
my %programs = ();
my $t = 0;
+ my %sustain = ();
+
+ my $note_on = sub
+ {
+ my ($ev) = @_;
+ my $chan = $ev->[4] + 1;
+ ++$notes_seen{$chan}{($programs{$chan} || 1)}{$ev->[5]};
+ if($midinotes{$chan}{$ev->[5]})
+ {
+ --$notes_stuck;
+ busybot_note_off($t - SYS_TICRATE - 0.001, $chan, $ev->[5]);
+ }
+ busybot_note_on($t, $chan, $programs{$chan} || 1, $ev->[5]);
+ ++$notes_stuck;
+ $midinotes{$chan}{$ev->[5]} = 1;
+ };
+
+ my $note_off = sub
+ {
+ my ($ev) = @_;
+ my $chan = $ev->[4] + 1;
+ if(exists $sustain{$chan})
+ {
+ push @{$sustain{$chan}}, $ev;
+ return;
+ }
+ if($midinotes{$chan}{$ev->[5]})
+ {
+ --$notes_stuck;
+ busybot_note_off($t - SYS_TICRATE - 0.001, $chan, $ev->[5]);
+ }
+ $midinotes{$chan}{$ev->[5]} = 0;
+ };
+
+ my $patch_change = sub
+ {
+ my ($ev) = @_;
+ my $chan = $ev->[4] + 1;
+ my $program = $ev->[5] + 1;
+ $programs{$chan} = $program;
+ };
+
+ my $sustain_change = sub
+ {
+ my ($ev) = @_;
+ my $chan = $ev->[4] + 1;
+ if($ev->[6] == 0)
+ {
+ # release all currently not pressed notes
+ my $s = $sustain{$chan};
+ delete $sustain{$chan};
+ for(@{($s || [])})
+ {
+ $note_off->($_);
+ }
+ }
+ else
+ {
+ # no more note-off
+ $sustain{$chan} = [];
+ }
+ };
+
for(@allmidievents)
{
$t = $tick2sec->($_->[1]);
- my $track = $_->[3];
+ # my $track = $_->[3];
if($_->[0] eq 'note_on')
{
- my $chan = $_->[4] + 1;
- ++$notes_seen{$chan}{$_->[5]}
- if $chan != 10 and $chan > 0;
- if($midinotes{$chan}{$_->[5]})
- {
- --$notes_stuck;
- busybot_note_off($t - SYS_TICRATE, $chan, $_->[5]);
- }
- busybot_note_on($t, $chan, $programs{$chan} || 1, $_->[5]);
- ++$notes_stuck;
- $midinotes{$chan}{$_->[5]} = 1;
+ $note_on->($_);
}
elsif($_->[0] eq 'note_off')
{
- my $chan = $_->[4] + 1;
- if($midinotes{$chan}{$_->[5]})
- {
- --$notes_stuck;
- busybot_note_off($t - SYS_TICRATE, $chan, $_->[5]);
- }
- $midinotes{$chan}{$_->[5]} = 0;
+ $note_off->($_);
}
elsif($_->[0] eq 'patch_change')
{
- my $chan = $_->[4] + 1;
- my $program = $_->[5] + 1;
- $programs{$chan} = $program;
+ $patch_change->($_);
}
+ elsif($_->[0] eq 'control_change' && $_->[5] == 64) # sustain pedal
+ {
+ $sustain_change->($_);
+ }
+ }
+
+ # fake events for releasing pedal
+ for(keys %sustain)
+ {
+ $sustain_change->(['control_change', $t, undef, undef, $_ - 1, 64, 0]);
}
print STDERR "For file $filename:\n";
my $good = 0;
for my $channel(sort keys %notes_seen)
{
- for my $note(sort keys %{$notes_seen{$channel}})
+ next if $channel == 10 or $channel < 0;
+ for my $program(sort keys %{$notes_seen{$channel}})
{
- my $cnt = $notes_seen{$channel}{$note};
- my $votehigh = 0;
- my $votelow = 0;
- my $votegood = 0;
- for(@busybots_allocated)
+ for my $note(sort keys %{$notes_seen{$channel}{$program}})
{
- next # I won't play on this channel
- if defined $_->{channels} and not $_->{channels}->{$channel};
-# next # I won't play this program
-# if defined $bot->{programs} and not $bot->{programs}->{$program};
- my $transposed = $note - ($_->{transpose} || 0) - $testtranspose;
- if(exists $_->{notes_on}{$transposed})
+ my $cnt = $notes_seen{$channel}{$program}{$note};
+ my $votehigh = 0;
+ my $votelow = 0;
+ my $votegood = 0;
+ for(@busybots_allocated, grep { $_->{count} > 0 } values %$busybots)
{
- ++$votegood;
+ next # I won't play on this channel
+ if defined $_->{channels} and not $_->{channels}->{$channel};
+ next # I won't play this program
+ if defined $_->{programs} and not $_->{programs}->{$program};
+ my $transposed = $note - ($_->{transpose} || 0) - $testtranspose;
+ if(exists $_->{notes_on}{$transposed})
+ {
+ ++$votegood;
+ }
+ else
+ {
+ ++$votehigh if $transposed >= 0;
+ ++$votelow if $transposed < 0;
+ }
+ }
+ if($votegood)
+ {
+ $good += $cnt;
+ }
+ elsif($votelow >= $votehigh)
+ {
+ $toolow += $cnt;
}
else
{
- ++$votehigh if $transposed >= 0;
- ++$votelow if $transposed < 0;
+ $toohigh += $cnt;
}
}
- if($votegood)
- {
- $good += $cnt;
- }
- elsif($votelow >= $votehigh)
- {
- $toolow += $cnt;
- }
- else
- {
- $toohigh += $cnt;
- }
}
}
next if !$toohigh != !$toolow;
print STDERR " Transpose $testtranspose: $toohigh too high, $toolow too low, $good good\n";
}
+ for my $program(sort keys %{$notes_seen{10}})
+ {
+ for my $note(sort keys %{$notes_seen{10}{$program}})
+ {
+ my $cnt = $notes_seen{10}{$program}{$note};
+ my $votegood = 0;
+ for(@busybots_allocated)
+ {
+ next # I won't play on this channel
+ if defined $_->{channels} and not $_->{channels}->{10};
+ next # I won't play this program
+ if defined $_->{programs} and not $_->{programs}->{$program};
+ if(exists $_->{percussion}{$note})
+ {
+ ++$votegood;
+ }
+ }
+ if(!$votegood)
+ {
+ print STDERR "Failed percussion $note ($cnt times)\n";
+ }
+ }
+ }
+
while(my ($k1, $v1) = each %midinotes)
{
while(my ($k2, $v2) = each %$v1)
my @preallocate_new = map { $_->{classname} } @busybots_allocated;
if(@preallocate_new == @preallocate)
{
+ print "sv_cmd bot_cmd reset\n";
+ print "sv_cmd bot_cmd setbots @{[scalar @preallocate_new]}\n";
print "$precommands$commands";
exit 0;
}