#! /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 my $Version = '1.3'; # MIDI-Perl module not required if -m my $VersionDate = '9mar2009'; 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; my $OutputFormat = 'midi'; # 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 'm') { $OutputFormat = 'muscript'; shift; } 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 $a = shift; if ($a !~ /^\d+$/) { die "bad -v arg: $a\n"; } $DefaultVolume = 0+$a; if ($DefaultVolume < 2) { $DefaultVolume = 1; } elsif ($DefaultVolume > 127) { $DefaultVolume = 127; } } else { my $n = $0; $n =~ s{^.*/([^/]+)$}{$1}; print "$n version $Version $VersionDate\n"; 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; } } if ($OutputFormat eq 'midi') { eval 'require MIDI'; if ($@) { die "you'll need to install the MIDI::Perl module from www.cpan.org\n"; } import MIDI; @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]; } elsif ($OutputFormat eq 'muscript') { print <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( '>-' ); } elsif ($OutputFormat eq 'muscript') { print " ", pitch2ascii($pitch), "\n"; } # ------------------------- 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; } my $last_c = 9; sub pitch2ascii { my $p = shift; my $c = $p % 12; my $a = qw(c c# d eb e f f# g g# a bb b)[$c]; if ($p < 36) { $a = ucfirst($a); } elsif ($p > 47) { $a =~ s/^(\w)/$1~/; } if (($c==0 && $last_c==1) || ($c==4 && $last_c==3) || ($c==5 && $last_c==6) || ($c==7 && $last_c==8) || ($c==11 && $last_c==10)) { $a .= 'n'; } $last_c = $c; return $a; } __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 -m # generates output in muscript format bassline -n 500 # 500 beats (default = 5 minutes) bassline -p 33 # use Patch 33, Electric Bass. (default=32) bassline -t 120 # Tempo 120 beats/minute (default=144) bassline -v 80 # MIDI-Volume 80 (default=100, max=127) bassline | aplaymidi - # do your jazz piano practice :-) perldoc bassline # read the manual :-) =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<-m> The output will be generated in muscript format. This could be useful if you want to add by hand in some other instruments. Like a tenor saxpohone is General-MIDI Patch 66, for example. =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 90> This example sets the MIDI-volume to 90. The minimum is 1, the default is 100, and the maximum is 127. =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/muscript/gm.html http://www.pjb.com.au/midi =cut