Game-EnergyLoop
view release on metacpan or search on metacpan
eg/rhythm.pl view on Meta::CPAN
use Game::EnergyLoop;
use List::UtilsBy qw(rev_nsort_by);
use MIDI;
use Object::Pad;
my @events;
my $out_file = shift // 'out.midi';
class Timer {
field $name :param :reader = "none";
field $alive :reader = 1;
field $callback :param :writer;
field $energy :param = 96;
field $cur_energy = 0;
field $start :param = 0;
field $priority :param :reader = 0;
field $ttl :param = 1;
field $channel :param = 0;
field $pitch :param;
field $velocity :param = 90;
eg/rhythm.pl view on Meta::CPAN
return $cur_energy;
}
method enlo_update( $value, $min, $stash ) {
my $dtime = 0;
unless ( $stash->{advanced} ) {
$dtime = $value;
$stash->{advanced} = 1;
}
my $spawn = $callback->( $dtime, $channel, $pitch, $velocity );
$alive = 0 if --$ttl <= 0;
$start = 0;
# when we're next scheduled to run (if still alive)
return defined $spawn ? ( $energy, $spawn ) : $energy;
}
}
# Create a note_on event, and schedule a note_off for later.
sub note {
my ( $dtime, $channel, $pitch, $velocity ) = @_;
push @events, [ note_on => $dtime, $channel, $pitch, $velocity ];
return [
Timer->new(
eg/rhythm.pl view on Meta::CPAN
sub reorder ($objs) {
# The priority is so that note_off turn off previous note_on before
# a new note_on for the same pitch.
@$objs = rev_nsort_by { $_->priority } @$objs;
}
my $cost;
do {
$cost = Game::EnergyLoop::update( \@objects, \&reorder, $stash );
@objects = grep { $_->alive } @objects;
$stash->{advanced} = 0;
} while ( $cost < ~0 );
sub make_tracks () { [ MIDI::Track->new( { events => \@events } ) ] }
MIDI::Opus->new(
{ format => 0, ticks => 96, tracks => make_tracks() } )
->write_to_file($out_file);
( run in 0.834 second using v1.01-cache-2.11-cpan-39bf76dae61 )