💾 Archived View for thrig.me › blog › 2023 › 10 › 07 › drums.pl captured on 2024-08-18 at 20:42:19.

View Raw

More Information

⬅️ Previous capture (2023-11-04)

-=-=-=-=-=-=-

#!/usr/bin/env perl
# drums - hybrid but with different instrumentation. Asian drums or
# gamelan might not be terrible to use here?
use 5.26.0;
use MIDI;
sub CCPAN ()   { 0x0A }
sub CHAN ()    { 0 }
sub MAXTIME () { 10240 * 8 }
sub NOW ()     { 0 }
sub QUIET ()   { 0 }

my $file = shift // 'drums.midi';

my $track_count = 8;

my @velo = qw( 105 80 90 80 );

sub velonoise { int( rand 4 + rand 4 + rand 4 ) }

sub velorand { $velo[rand @velo] + velonoise }

sub velostep {
    state $i = 0;
    my $v = $velo[$i];
    $i = ( $i + 1 ) % @velo;
    $v + velonoise;
}

sub makeatrack {
    state $pan   = 40;    # NOTE depends on track_count
    state $pitch = 42;
    my $interval   = 16 * ( 1 + int rand 8 );
    my $periodic   = 1;
    my $sectionlen = 1024;
    my $rand       = 256;

    my $mul = 2;

    my $dur    = 16 * ( int rand 8 );
    my @events = (
        [ set_tempo    => NOW, 800_000 ],
        [ patch_change => NOW, CHAN, 47 ],
        [ marker       => $dur ],
    );
    my ( $epoch, $subepoch ) = ( $dur, $dur );

    while (1) {
        if ($periodic) {
            $subepoch += $interval * $mul;
            $epoch    += $subepoch;
            if ( $subepoch >= $sectionlen ) {
                my $delta = $subepoch - $sectionlen;
                if ( $delta > 0 ) {
                    $epoch -= $delta;
                    push @events, [ marker => $delta ];
                }
                $subepoch = 0;
                $periodic ^= 1;
                next;
            }
            push @events,
              [ control_change => NOW, CHAN, CCPAN, $pan ],
              [ note_on        => NOW, CHAN, $pitch, velostep ],
              [ note_off       => $interval, CHAN, $pitch, QUIET ];
        } else {
            my $delay = ( 1 + int rand $rand ) * $mul;
            $subepoch += $interval;
            $epoch    += $subepoch;
            if ( $subepoch >= ( $sectionlen / 2 ) ) {
                my $delta = $subepoch - $sectionlen;    # BUG !
                if ( $delta > 0 ) {
                    $epoch -= $delta;
                    push @events, [ marker => $delta ];
                }
                $subepoch = 0;
                $periodic ^= 1;
                next;
            }
            push @events,
              [ control_change => NOW, CHAN, CCPAN, $pan ],
              [ note_on        => NOW, CHAN, $pitch, velostep ],
              [ note_off       => $delay, CHAN, $pitch, QUIET ];
        }
    } continue {
        last if $epoch >= MAXTIME;
    }
    $pan   += 6;                       # NOTE depends on track_count
    my @leaps = ( 3, 5, 5, 5, 7 );
    $pitch += $leaps[ rand @leaps ];
    MIDI::Track->new( { events => \@events } );
}

sub gentracks { [ map makeatrack, 1 .. $track_count ] }

MIDI::Opus->new( { tracks => gentracks } )->write_to_file($file);