#! /usr/bin/perl ######################################################################### # This Perl script is Copyright (c) 2006, Peter J Billam # # c/o P J B Computing, GPO Box 669, Hobart TAS 7001, Australia # # # # This script is free software; you can redistribute it and/or # # modify it under the same terms as Perl itself. # ######################################################################### # Generates a random jazz bassline. # # We work in MIDI::Event, appending new notes to the array eval 'require MIDI'; if ($@) { die "you'll need to install the MIDI::Perl module from www.cpan.org\n"; } import MIDI; my $Version = '1.0'; my $VersionDate = '3nov2006'; my $Channel = 1; # MIDI channel, see -c my $Patch = 32; # default Acoustic Bass, see -p my $Tempo = 144; # Beats per Minute, see -t my $Notes = 5 * $Tempo; # Number of Notes, see -n my $Turn_Probability = 0.15; # probability of line turning direction my $Quaver_Probability = 0.09; # probability of a pair of swung quavers my $Swing = 0.60; # First-quaver length in a pair of quavers my $TPC = 96; # MIDI Ticks Per Crochet my $Swing1 = int(0.5 + $Swing*$TPC); my $Swing2 = $TPC - $Swing1; my $DefaultLegato = 0.85; # MIDI default length of a crochet my $DefaultVolume = 100; # MIDI default volume (0..127) my $TopNote = 57; # high A my $BottomNote = 28; # low E my $Direction = -1; # start downwards my %IntervalProbability = ( 1, 0.55, 2, 0.85, 3, 0.90, 4, 0.92, 5, 0.94, 6, 0.96, 7, 0.98, 12, 1.00, ); my @Intervals = sort {$a<=>$b} keys %IntervalProbability; # check format of options args... while ($ARGV[$[] =~ /^-(\w)/) { if ($1 eq 'c') { shift; my $a = shift; if ($a !~ /^\d+$/) { die "bad -c arg: $a\n"; } $Channel = 0+$a; } elsif ($1 eq 'n') { shift; my $a = shift; if ($a !~ /^\d+$/) { die "bad -n arg: $a\n"; } $Notes = 0+$a; } elsif ($1 eq 'p') { shift; my $a = shift; if ($a !~ /^\d+$/) { die "bad -p arg: $a\n"; } $Patch = 0+$a; } elsif ($1 eq 't') { shift; my $a = shift; if ($a !~ /^\d+$/) { die "bad -t arg: $a\n"; } $Tempo = 0+$a; } elsif ($1 eq 'v') { shift; my $n = $0; $n =~ s{^.*/([^/]+)$}{$1}; print "$n version $Version $VersionDate\n"; exit 0; } else { print "usage:\n"; my $synopsis = 0; while () { if (/^=head1 SYNOPSIS/) { $synopsis = 1; next; } if ($synopsis && /^=head1/) { last; } if ($synopsis && /\S/) { s/^\s*/ /; print $_; next; } } exit 0; } } @newevents = (); my $nn = 4; my $dd = 4; # 4/4 my $cc = $TPC; # beat = cro push @newevents, ['patch_change', 0, $Channel, $Patch]; push @newevents, ['time_signature', 5, $nn,$dd,$cc,8]; my $miditempo = int (0.5 + 60000000*$TPC / ($TPC*$Tempo)); push @newevents, ['set_tempo', 0, $miditempo]; my $pitch = int (0.5 + 0.5*($TopNote+$BottomNote)); for my $n (1..$Notes) { if (rand() < $Quaver_Probability) { $pitch = &new_pitch($pitch); push @newevents,['note_on', 0, $Channel, $pitch, $DefaultVolume]; push @newevents, ['note_off', $Swing1,$Channel,$pitch,$DefaultVolume]; $pitch = &new_pitch($pitch); push @newevents,['note_on', 0, $Channel, $pitch, $DefaultVolume]; push @newevents, ['note_off',$Swing2,$Channel,$pitch,$DefaultVolume]; } else { $pitch = &new_pitch($pitch); push @newevents,['note_on', 0, $Channel, $pitch, $DefaultVolume]; push @newevents,['note_off', $TPC, $Channel, $pitch, $DefaultVolume]; } } # one last note... $pitch = &new_pitch($pitch); push @newevents,['note_on', 0, $Channel, $pitch, $DefaultVolume]; push @newevents,['note_off', 3*$TPC, $Channel, $pitch, $DefaultVolume]; # this bit copied from muscript: my $newtrack = MIDI::Track->new( {'events'=>\@newevents} ); if (!$newtrack) { die "MIDI::Track->new failed\n"; } $newopus = MIDI::Opus->new( {'format'=>0,'ticks'=>$TPC,'tracks'=>[$newtrack]} ); if (!$newopus) { die "MIDI::Opus->new failed\n"; } $newopus->write_to_file( '>-' ); # ------------------------- infrastructure ----------------------------- sub new_pitch { my $old_pitch = $_[$[]; if (rand() < $Turn_Probability) { $Direction = 0 - $Direction; } my $rand = rand(); my $interval; foreach (@Intervals) { if ($rand < $IntervalProbability{$_}) { $interval = $_; last; } } my $new_pitch; if ($Direction > 0) { $new_pitch = $old_pitch + $interval; if ($new_pitch > $TopNote) { $new_pitch = $old_pitch - $interval; $Direction = -1; } } else { $new_pitch = $old_pitch - $interval; if ($new_pitch < $BottomNote) { $new_pitch = $old_pitch + $interval; $Direction = 1; } } return $new_pitch; } __END__ =pod =head1 NAME bassline - Generates a random jazz-bassline, for improvisation practice. =head1 SYNOPSIS bassline -c 3 # output on MIDI Channel 3 (default=1) bassline -n 500 # 500 beats (default = 5 minutes) bassline -p 33 # use Patch 33, Electric Bass. (default=32) bassline -t 120 # 120 beats per minute (default=144) bassline -v # prints version number bassline | aplaymidi - # do your jazz piano practice :-) =head1 DESCRIPTION This program generates randomly wandering jazz-like basslines. It mostly plays one note per beat, but sometimes throws in a pair of swung quavers. It tends to keep moving either up or down, but sometimes it turns round and starts going the other way. It prefers intervals of one and two semitones, but from time to time it throws in larger intervals. =head1 OPTIONS =over 3 =item I<-c 3> This example will cause output to be generated on midi Bhannel 3. The default is channel 1. =item I<-n 300> This example will cause 300 beats of output to be generated. The default is to generate five minute's worth. =item I<-p 33> This example will cause output to be generated with midi B

atch 33. The default is patch 32, which is the General-MIDI pizzicato acoustic bass. =item I<-t 160> This example sets the tempo to 160 beats per minute. The default is 144 beats per minute. =item I<-v> Prints the version number. =back =head1 AUTHOR Peter J Billam http://www.pjb.com.au/comp/contact.html =head1 CREDITS Based on Sean Burke's MIDI::Perl CPAN module. =head1 SEE ALSO http://search.cpan.org/~sburke http://www.pjb.com.au/muscript http://www.pjb.com.au/midi =cut