3 # converter from Type 1 MIDI files to CFG files that control bots with the Tuba and other weapons for percussion (requires g_weaponarena all)
11 # workaround for possible refire time problems
12 use constant SYS_TICRATE => 0.033333;
13 #use constant SYS_TICRATE => 0;
15 use constant MIDI_FIRST_NONCHANNEL => 17;
16 use constant MIDI_DRUMS_CHANNEL => 10;
17 use constant TEXT_EVENT_CHANNEL => -1;
19 die "Usage: $0 filename.conf midifile1 transpose1 midifile2 transpose2 ..."
20 unless @ARGV > 1 and @ARGV % 2;
22 my $timeoffset_preinit = 2;
23 my $timeoffset_postinit = 2;
24 my $timeoffset_predone = 2;
25 my $timeoffset_postdone = 2;
26 my $timeoffset_preintermission = 2;
27 my $timeoffset_postintermission = 2;
28 my $time_forgetfulness = 1.5;
32 my ($config, @midilist) = @ARGV;
36 return map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, rand] } @_;
42 my ($dest, $src) = @_;
43 if(ref $src eq 'HASH')
49 $dest->{$_} = override $dest->{$_}, $src->{$_};
52 elsif(ref $src eq 'ARRAY')
58 push @$dest, override undef, $_;
63 $dest = Storable::dclone $src;
75 my @busybots_allocated;
79 my $lowestnotestart = undef;
88 my $currentbot = undef;
89 my $appendref = undef;
98 my @cmd = split /\s+/, $_;
99 if($cmd[0] eq 'super')
101 push @$appendref, @$super
104 elsif($cmd[0] eq 'percussion') # simple import
106 push @$appendref, @{$currentbot->{percussion}->{$cmd[1]}};
110 push @$appendref, \@cmd;
117 my $base = $bots{$1};
118 $currentbot = override $currentbot, $base;
120 elsif(/^count (\d+)/)
122 $currentbot->{count} = $1;
124 elsif(/^transpose (\d+)/)
126 $currentbot->{transpose} ||= 0;
127 $currentbot->{transpose} += $1;
129 elsif(/^channels (.*)/)
131 $currentbot->{channels} = { map { $_ => 1 } split /\s+/, $1 };
133 elsif(/^programs (.*)/)
135 $currentbot->{programs} = { map { $_ => 1 } split /\s+/, $1 };
139 $super = $currentbot->{init};
140 $currentbot->{init} = $appendref = [];
142 elsif(/^intermission$/)
144 $super = $currentbot->{intermission};
145 $currentbot->{intermission} = $appendref = [];
149 $super = $currentbot->{done};
150 $currentbot->{done} = $appendref = [];
152 elsif(/^note on (-?\d+)/)
154 $super = $currentbot->{notes_on}->{$1};
155 $currentbot->{notes_on}->{$1} = $appendref = [];
157 elsif(/^note off (-?\d+)/)
159 $super = $currentbot->{notes_off}->{$1};
160 $currentbot->{notes_off}->{$1} = $appendref = [];
162 elsif(/^percussion (\d+)/)
164 $super = $currentbot->{percussion}->{$1};
165 $currentbot->{percussion}->{$1} = $appendref = [];
169 $super = $currentbot->{text}->{$1};
170 $currentbot->{text}->{$1} = $appendref = [];
174 print STDERR "unknown command: $_\n";
179 $currentbot = ($bots{$1} ||= {count => 0});
183 $precommands .= "$1\n";
185 elsif(/^timeoffset_preinit (.*)/)
187 $timeoffset_preinit = $1;
189 elsif(/^timeoffset_postinit (.*)/)
191 $timeoffset_postinit = $1;
193 elsif(/^timeoffset_predone (.*)/)
195 $timeoffset_predone = $1;
197 elsif(/^timeoffset_postdone (.*)/)
199 $timeoffset_postdone = $1;
201 elsif(/^timeoffset_preintermission (.*)/)
203 $timeoffset_preintermission = $1;
205 elsif(/^timeoffset_postintermission (.*)/)
207 $timeoffset_postintermission = $1;
209 elsif(/^time_forgetfulness (.*)/)
211 $time_forgetfulness = $1;
213 elsif(/^list (.*?) (.*)/)
215 $lists{$1} = [split / /, $2];
216 $listindexes{$1} = 0;
220 print STDERR "unknown command: $_\n";
226 for(values %{$_->{notes_on}}, values %{$_->{percussion}})
228 my $t = $_->[0]->[0] eq 'time' ? $_->[0]->[1] : 0;
229 $lowestnotestart = $t if not defined $lowestnotestart or $t < $lowestnotestart;
235 my $busybots_orig = botconfig_read $config;
238 # returns: ($mintime, $maxtime, $busytime)
239 sub busybot_cmd_bot_cmdinfo(@)
245 my $busytime = undef;
249 if($_->[0] eq 'time')
252 if not defined $mintime or $_->[1] < $mintime;
253 $maxtime = $_->[1] + SYS_TICRATE
254 if not defined $maxtime or $_->[1] + SYS_TICRATE > $maxtime;
256 elsif($_->[0] eq 'busy')
258 $busytime = $_->[1] + SYS_TICRATE;
262 return ($mintime, $maxtime, $busytime);
265 sub busybot_cmd_bot_matchtime($$$@)
267 my ($bot, $targettime, $targetbusytime, @commands) = @_;
269 # I want to execute @commands so that I am free on $targettime and $targetbusytime
270 # when do I execute it then?
272 my ($mintime, $maxtime, $busytime) = busybot_cmd_bot_cmdinfo @commands;
274 my $tstart_max = defined $maxtime ? $targettime - $maxtime : $targettime;
275 my $tstart_busy = defined $busytime ? $targetbusytime - $busytime : $targettime;
277 return $tstart_max < $tstart_busy ? $tstart_max : $tstart_busy;
280 # TODO function to find out whether, and when, to insert a command before another command to make it possible
281 # (note-off before note-on)
283 sub busybot_cmd_bot_test($$$@)
285 my ($bot, $time, $force, @commands) = @_;
287 my $bottime = defined $bot->{timer} ? $bot->{timer} : -1;
288 my $botbusytime = defined $bot->{busytimer} ? $bot->{busytimer} : -1;
290 my ($mintime, $maxtime, $busytime) = busybot_cmd_bot_cmdinfo @commands;
292 if($time < $botbusytime)
294 warn "FORCE: $time < $botbusytime"
299 if(defined $mintime and $time + $mintime < $bottime)
301 warn "FORCE: $time + $mintime < $bottime"
316 ? do { $lists{$1}[$listindexes{$1}++ % @{$lists{$1}}]; }
322 sub busybot_cmd_bot_execute($$@)
324 my ($bot, $time, @commands) = @_;
328 if($_->[0] eq 'time')
330 $commands .= sprintf "sv_cmd bot_cmd %d wait_until %f\n", $bot->{id}, $time + $_->[1];
331 if($bot->{timer} > $time + $_->[1] + SYS_TICRATE)
333 #use Carp; carp "Negative wait: $bot->{timer} <= @{[$time + $_->[1] + SYS_TICRATE]}";
335 $bot->{timer} = $time + $_->[1] + SYS_TICRATE;
337 elsif($_->[0] eq 'busy')
339 $bot->{busytimer} = $time + $_->[1] + SYS_TICRATE;
341 elsif($_->[0] eq 'buttons')
343 my %buttons_release = %{$bot->{buttons} ||= {}};
347 delete $buttons_release{$1};
349 for(keys %buttons_release)
351 $commands .= sprintf "sv_cmd bot_cmd %d releasekey %s\n", $bot->{id}, $_;
352 delete $bot->{buttons}->{$_};
358 $commands .= sprintf "sv_cmd bot_cmd %d presskey %s\n", $bot->{id}, $_;
359 $bot->{buttons}->{$_} = 1;
362 elsif($_->[0] eq 'cmd')
364 $commands .= sprintf "sv_cmd bot_cmd %d %s\n", $bot->{id}, buildstring @{$_}[1..@$_-1];
366 elsif($_->[0] eq 'aim_random')
368 $commands .= sprintf "sv_cmd bot_cmd %d aim \"%f 0 %f\"\n", $bot->{id}, $_->[1] + rand($_->[2] - $_->[1]), $_->[3];
370 elsif($_->[0] eq 'barrier')
372 $commands .= sprintf "sv_cmd bot_cmd %d barrier\n", $bot->{id};
373 $bot->{timer} = $bot->{busytimer} = 0;
374 undef $bot->{lastuse};
376 elsif($_->[0] eq 'raw')
378 $commands .= sprintf "%s\n", buildstring @{$_}[1..@$_-1];
382 warn "Invalid command: @$_";
389 my $intermissions = 0;
391 sub busybot_intermission_bot($)
394 busybot_cmd_bot_execute $bot, 0, ['cmd', 'wait', $timeoffset_preintermission];
395 busybot_cmd_bot_execute $bot, 0, ['barrier'];
396 if($bot->{intermission})
398 busybot_cmd_bot_execute $bot, 0, @{$bot->{intermission}};
400 busybot_cmd_bot_execute $bot, 0, ['barrier'];
401 $notetime = $timeoffset_postintermission - $lowestnotestart;
405 sub busybot_note_off_bot($$$$)
407 my ($bot, $time, $channel, $note) = @_;
408 #print STDERR "note off $bot:$time:$channel:$note\n";
411 my ($busychannel, $busynote, $cmds) = @{$bot->{busy}};
413 if not defined $cmds; # note off cannot fail
414 die "Wrong note-off?!?"
415 if $busychannel != $channel || $busynote ne $note;
416 $bot->{busy} = undef;
418 my $t = $time + $notetime;
419 my ($mintime, $maxtime, $busytime) = busybot_cmd_bot_cmdinfo @$cmds;
421 # perform note-off "as soon as we can"
422 $t = $bot->{busytimer}
423 if $t < $bot->{busytimer};
424 $t = $bot->{timer} - $mintime
425 if $t < $bot->{timer} - $mintime;
427 busybot_cmd_bot_execute $bot, $t, @$cmds;
431 sub busybot_get_cmds_bot($$$)
433 my ($bot, $channel, $note) = @_;
434 my ($k0, $k1, $cmds, $cmds_off) = (undef, undef, undef, undef);
435 if($channel == TEXT_EVENT_CHANNEL)
438 $note =~ /^([^:]*):(.*)$/;
441 $cmds = $bot->{text}->{$name};
444 $cmds = [ map { [ map { $_ eq '%s' ? $data : $_ } @$_ ] } @$cmds ];
449 elsif($channel == 10)
452 $cmds = $bot->{percussion}->{$note};
459 $cmds = $bot->{notes_on}->{$note - ($bot->{transpose} || 0) - $transpose};
460 $cmds_off = $bot->{notes_off}->{$note - ($bot->{transpose} || 0) - $transpose};
462 $k1 = $note - ($bot->{transpose} || 0) - $transpose;
464 return ($cmds, $cmds_off, $k0, $k1);
467 sub busybot_note_on_bot($$$$$$$)
469 my ($bot, $time, $channel, $program, $note, $init, $force) = @_;
471 return -1 # I won't play on this channel
472 if defined $bot->{channels} and not $bot->{channels}->{$channel};
473 return -1 # I won't play this program
474 if defined $bot->{programs} and not $bot->{programs}->{$program};
476 my ($cmds, $cmds_off, $k0, $k1) = busybot_get_cmds_bot($bot, $channel, $note);
478 return -1 # I won't play this note
479 if not defined $cmds;
482 #print STDERR "note on $bot:$time:$channel:$note\n";
486 if not busybot_cmd_bot_test $bot, $time + $notetime, $force, @$cmds;
487 busybot_cmd_bot_execute $bot, 0, ['cmd', 'wait', $timeoffset_preinit];
488 busybot_cmd_bot_execute $bot, 0, ['barrier'];
489 busybot_cmd_bot_execute $bot, 0, @{$bot->{init}}
491 busybot_cmd_bot_execute $bot, 0, ['barrier'];
492 for(1..$intermissions)
494 busybot_intermission_bot $bot;
496 # we always did a barrier, so we know this works
497 busybot_cmd_bot_execute $bot, $time + $notetime, @$cmds;
502 if not busybot_cmd_bot_test $bot, $time + $notetime, $force, @$cmds;
503 busybot_cmd_bot_execute $bot, $time + $notetime, @$cmds;
505 if(defined $cmds_off)
507 $bot->{busy} = [$channel, $note, $cmds_off];
509 ++$bot->{seen}{$k0}{$k1};
511 if(($bot->{lastuse} // -666) >= $time - $time_forgetfulness && $channel == $bot->{lastchannel})
513 $bot->{lastchannelsequence} += 1;
517 $bot->{lastchannelsequence} = 1;
519 $bot->{lastuse} = $time;
520 $bot->{lastchannel} = $channel;
522 # print STDERR "$time $bot->{id} $channel:$note\n"
530 $busybots = Storable::dclone $busybots_orig;
531 @busybots_allocated = ();
532 %notechannelbots = ();
534 $notetime = $timeoffset_postinit - $lowestnotestart;
537 sub busybot_note_off($$$)
539 my ($time, $channel, $note) = @_;
541 # print STDERR "note off $time:$channel:$note\n";
543 if(my $bot = $notechannelbots{$channel}{$note})
545 busybot_note_off_bot $bot, $time, $channel, $note;
546 delete $notechannelbots{$channel}{$note};
555 my ($time, $channel, $program, $note, @bots) = @_;
565 ($a->[0]->{lastuse} // -666) <=> ($b->[0]->{lastuse} // -666)
572 if($channel != 10) # percussion just should do round robin
574 if(($_->{lastuse} // -666) >= $time - $time_forgetfulness)
576 if($channel == $_->{lastchannel})
578 $q += $_->{lastchannelsequence};
582 # better leave this one alone
583 $q -= $_->{lastchannelsequence};
592 sub busybot_note_on($$$$)
594 my ($time, $channel, $program, $note) = @_;
596 if($notechannelbots{$channel}{$note})
598 print STDERR "THIS SHOULD NEVER HAPPEN\n";
599 busybot_note_off $time, $channel, $note;
602 # print STDERR "note on $time:$channel:$note\n";
606 my @epicfailbots = ();
608 for(botsort $time, $channel, $program, $note, @busybots_allocated)
610 my $canplay = busybot_note_on_bot $_, $time, $channel, $program, $note, 0, 0;
613 $notechannelbots{$channel}{$note} = $_;
616 push @epicfailbots, $_
623 for(unsort keys %$busybots)
625 next if $busybots->{$_}->{count} <= 0;
626 my $bot = Storable::dclone $busybots->{$_};
627 $bot->{id} = @busybots_allocated + 1;
628 $bot->{classname} = $_;
629 my $canplay = busybot_note_on_bot $bot, $time, $channel, $program, $note, 1, 0;
638 --$busybots->{$_}->{count};
639 $notechannelbots{$channel}{$note} = $bot;
640 push @busybots_allocated, $bot;
644 die "Fresh bot cannot play stuff"
650 # we cannot add a new bot to play this
651 # we could try finding a bot that could play this, and force him to stop the note!
653 my @candidates = (); # contains: [$bot, $score, $offtime]
655 # put in all currently busy bots that COULD play this, if they did a note-off first
656 for my $bot(@epicfailbots)
659 if $busybots->{$bot->{classname}}->{count} != 0;
662 my ($busy_chan, $busy_note, $busy_cmds_off) = @{$bot->{busy}};
664 unless $busy_cmds_off;
665 my ($cmds, $cmds_off, $k0, $k1) = busybot_get_cmds_bot $bot, $channel, $note;
668 my ($mintime, $maxtime, $busytime) = busybot_cmd_bot_cmdinfo @$cmds;
669 my ($mintime_off, $maxtime_off, $busytime_off) = busybot_cmd_bot_cmdinfo @$busy_cmds_off;
671 my $noteofftime = busybot_cmd_bot_matchtime $bot, $time + $notetime + $mintime, $time + $notetime, @$busy_cmds_off;
673 if $noteofftime < $bot->{busytimer};
675 if $noteofftime + $mintime_off < $bot->{timer};
678 # prefer turning off long notes
679 $score += 100 * ($noteofftime - $bot->{timer});
680 # prefer turning off low notes
681 $score += 1 * (-$note);
682 # prefer turning off notes that already play on another channel
683 $score += 1000 * (grep { $_ != $busy_chan && $notechannelbots{$_}{$busy_note} && $notechannelbots{$_}{$busy_note}{busy} } keys %notechannelbots);
685 push @candidates, [$bot, $score, $noteofftime];
692 @candidates = sort { $a->[1] <=> $b->[1] } @candidates;
693 my ($bot, $score, $offtime) = @{(pop @candidates)};
694 my $oldchan = $bot->{busy}->[0];
695 my $oldnote = $bot->{busy}->[1];
696 busybot_note_off $offtime - $notetime, $oldchan, $oldnote;
697 my $canplay = busybot_note_on_bot $bot, $time, $channel, $program, $note, 0, 1;
698 die "Canplay but not?"
700 warn "Made $channel:$note play by stopping $oldchan:$oldnote";
701 $notechannelbots{$channel}{$note} = $bot;
711 warn "Not enough bots to play this (when playing $channel:$note)";
714 # my $b = $_->{busy};
715 # warn "$_->{classname} -> @{[$b ? qq{$b->[0]:$b->[1]} : 'none']} @{[$_->{timer} - $notetime]} ($time)\n";
720 warn "Note $channel:$note cannot be played by any bot";
728 my (@preallocate) = @_;
732 die "Cannot preallocate any more $_ bots"
733 if $busybots->{$_}->{count} <= 0;
734 my $bot = Storable::dclone $busybots->{$_};
735 $bot->{id} = @busybots_allocated + 1;
736 $bot->{classname} = $_;
737 busybot_cmd_bot_execute $bot, 0, ['cmd', 'wait', $timeoffset_preinit];
738 busybot_cmd_bot_execute $bot, 0, ['barrier'];
739 busybot_cmd_bot_execute $bot, 0, @{$bot->{init}}
741 busybot_cmd_bot_execute $bot, 0, ['barrier'];
742 --$busybots->{$_}->{count};
743 push @busybots_allocated, $bot;
749 my ($filename, $trans) = @_;
752 my $opus = MIDI::Opus->new({from_file => $filename});
753 my $ticksperquarter = $opus->ticks();
754 my $tracks = $opus->tracks_r();
755 my @tempi = (); # list of start tick, time per tick pairs (calculated as seconds per quarter / ticks per quarter)
759 for($tracks->[0]->events())
762 if($_->[0] eq 'set_tempo')
764 push @tempi, [$tick, $_->[2] * 0.000001 / $ticksperquarter];
771 my $curtempo = [0, 0.5 / $ticksperquarter];
776 # this event is in the past
777 # we add the full time since the last one then
778 $sec += ($_->[0] - $curtempo->[0]) * $curtempo->[1];
782 # if this event is in the future, we break
787 $sec += ($tick - $curtempo->[0]) * $curtempo->[1];
791 # merge all to a single track
792 my @allmidievents = ();
794 for my $track(0..@$tracks-1)
797 for($tracks->[$track]->events())
799 my ($command, $delta, @data) = @$_;
800 $command = 'note_off' if $command eq 'note_on' and $data[2] == 0;
803 if $command eq 'text_event' && $data[0] !~ /:/;
804 push @allmidievents, [$command, $tick, $sequence++, $track, @data];
808 if(open my $fh, "$filename.vocals")
815 my ($tick, $file) = split /\s+/, $_;
820 elsif($tick eq 'shift')
826 push @allmidievents, ['text_event', $tick * $scale + $shift, $sequence++, -1, "vocals:$file"];
831 # HACK for broken rosegarden export: put patch changes first by clearing their sequence number
834 if($_->[0] eq 'patch_change')
841 @allmidievents = sort { $a->[1] <=> $b->[1] or $a->[2] <=> $b->[2] } @allmidievents;
843 # find the first interesting event
844 my $shift = [grep { $_->[0] eq 'note_on' || $_->[0] eq 'text_event' } @allmidievents]->[0][1];
846 unless defined $shift;
848 # shift times by first event, no boring waiting
849 $_->[0] = ($_->[0] < $shift ? 0 : $_->[0] - $shift) for @tempi;
850 $_->[1] = ($_->[1] < $shift ? 0 : $_->[1] - $shift) for @allmidievents;
864 my $chan = $ev->[4] + 1;
865 ++$notes_seen{$chan}{($programs{$chan} || 1)}{$ev->[5]};
866 if($midinotes{$chan}{$ev->[5]})
869 busybot_note_off($t - SYS_TICRATE - 0.001, $chan, $ev->[5]);
871 busybot_note_on($t, $chan, $programs{$chan} || 1, $ev->[5]);
873 $midinotes{$chan}{$ev->[5]} = 1;
879 my $chan = $ev->[4] + 1;
880 if(exists $sustain{$chan})
882 push @{$sustain{$chan}}, $ev;
885 if($midinotes{$chan}{$ev->[5]})
888 busybot_note_off($t - SYS_TICRATE - 0.001, $chan, $ev->[5]);
890 $midinotes{$chan}{$ev->[5]} = 0;
897 my $chan = TEXT_EVENT_CHANNEL;
899 busybot_note_on($t, TEXT_EVENT_CHANNEL, -1, $ev->[4]);
900 busybot_note_off($t, TEXT_EVENT_CHANNEL, $ev->[4]);
903 my $patch_change = sub
906 my $chan = $ev->[4] + 1;
907 my $program = $ev->[5] + 1;
908 $programs{$chan} = $program;
911 my $sustain_change = sub
914 my $chan = $ev->[4] + 1;
917 # release all currently not pressed notes
918 my $s = $sustain{$chan};
919 delete $sustain{$chan};
928 $sustain{$chan} = [];
934 $t = $tick2sec->($_->[1]);
935 # my $track = $_->[3];
936 if($_->[0] eq 'note_on')
940 elsif($_->[0] eq 'note_off')
944 elsif($_->[0] eq 'text_event')
948 elsif($_->[0] eq 'patch_change')
952 elsif($_->[0] eq 'control_change' && $_->[5] == 64) # sustain pedal
954 $sustain_change->($_);
958 # fake events for releasing pedal
961 $sustain_change->(['control_change', $t, undef, undef, $_ - 1, 64, 0]);
964 print STDERR "For file $filename:\n";
965 print STDERR " Stuck notes: $notes_stuck\n";
967 for my $testtranspose(-127..127)
972 for my $channel(sort keys %notes_seen)
974 next if $channel == 10;
975 for my $program(sort keys %{$notes_seen{$channel}})
977 for my $note(sort keys %{$notes_seen{$channel}{$program}})
979 my $cnt = $notes_seen{$channel}{$program}{$note};
983 for(@busybots_allocated, grep { $_->{count} > 0 } values %$busybots)
985 next # I won't play on this channel
986 if defined $_->{channels} and not $_->{channels}->{$channel};
987 next # I won't play this program
988 if defined $_->{programs} and not $_->{programs}->{$program};
989 my $transposed = $note - ($_->{transpose} || 0) - $testtranspose;
990 if(exists $_->{notes_on}{$transposed})
996 ++$votehigh if $transposed >= 0;
997 ++$votelow if $transposed < 0;
1004 elsif($votelow >= $votehigh)
1015 next if !$toohigh != !$toolow;
1016 print STDERR " Transpose $testtranspose: $toohigh too high, $toolow too low, $good good\n";
1019 for my $program(sort keys %{$notes_seen{10}})
1021 for my $note(sort keys %{$notes_seen{10}{$program}})
1023 my $cnt = $notes_seen{10}{$program}{$note};
1025 for(@busybots_allocated)
1027 next # I won't play on this channel
1028 if defined $_->{channels} and not $_->{channels}->{10};
1029 next # I won't play this program
1030 if defined $_->{programs} and not $_->{programs}->{$program};
1031 if(exists $_->{percussion}{$note})
1038 print STDERR "Failed percussion $note ($cnt times)\n";
1043 while(my ($k1, $v1) = each %midinotes)
1045 while(my ($k2, $v2) = each %$v1)
1047 busybot_note_off($t, $k1, $k2);
1051 for(@busybots_allocated)
1053 busybot_intermission_bot $_;
1060 print STDERR "Bots allocated:\n";
1063 for(@busybots_allocated)
1065 print STDERR "$_->{id} is a $_->{classname}\n";
1066 ++$counthash{$_->{classname}};
1067 while(my ($type, $notehash) = each %{$_->{seen}})
1069 while(my ($k, $v) = each %$notehash)
1071 $notehash{$_->{classname}}{$type}{$k} += $v;
1075 for my $cn(sort keys %counthash)
1077 print STDERR "$counthash{$cn} bots of $cn have played:\n";
1078 for my $type(sort keys %{$notehash{$cn}})
1080 for my $note(sort keys %{$notehash{$cn}{$type}})
1082 my $cnt = $notehash{$cn}{$type}{$note};
1083 print STDERR " $type $note ($cnt times)\n";
1087 for(@busybots_allocated)
1089 busybot_cmd_bot_execute $_, 0, ['cmd', 'wait', $timeoffset_predone];
1090 busybot_cmd_bot_execute $_, 0, ['barrier'];
1093 busybot_cmd_bot_execute $_, 0, @{$_->{done}};
1095 busybot_cmd_bot_execute $_, 0, ['cmd', 'wait', $timeoffset_postdone];
1096 busybot_cmd_bot_execute $_, 0, ['barrier'];
1100 my @preallocate = ();
1108 Preallocate(@preallocate);
1112 my $filename = shift @l;
1113 my $transpose = shift @l;
1114 ConvertMIDI($filename, $transpose);
1117 my @preallocate_new = map { $_->{classname} } @busybots_allocated;
1118 if(@preallocate_new == @preallocate)
1120 print "sv_cmd bot_cmd setbots @{[scalar @preallocate_new]}\n";
1121 print "$precommands$commands";
1124 @preallocate = @preallocate_new;
1129 unless $@ eq "noalloc\n";