The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Music::Duration::Partition::Tutorial::Advanced

VERSION

version 0.0820

Usage Examples

Shuffling the Motif

By shuffling a single generated motif, you can get permutations of the durations.

  use List::Util qw(shuffle);
  use MIDI::Util qw(setup_score);
  use Music::Duration::Partition ();
  use Music::Scales qw(get_scale_MIDI);

  my $score = setup_score(bpm => 90);

  my @scale = get_scale_MIDI('A', 3, 'pminor');

  # instantiate a 4 beat phrase generator
  my $mdp = Music::Duration::Partition->new(
      pool => [qw(qn en sn)],
      #pool => [qw(twn thn tqn ten tsn)],
  );

  # get a single motif of note durations
  my $motif = $mdp->motif;

  # repeat 2 measures, 8 times
  for my $i (1 .. 8) {
      # get a fresh set of random scale pitches for the motif
      my @voices = map { $scale[ int rand @scale ] } 0 .. $#$motif;

      # every other measure, shuffle the motif durations
      my @phrase = $i % 2 ? @$motif : shuffle @$motif;

      $mdp->add_to_score($score, \@phrase, \@voices);

      $score->r('wn');
  }

  $score->write_score('shuffled.mid');

Computing Voices

Play the same motif but with different voices computed by the Voss method.

  use MIDI::Util qw(setup_score);
  use Music::Duration::Partition ();
  use Music::Scales qw(get_scale_MIDI);
  use Music::Voss qw(powers);

  my $score = setup_score(bpm => 90);

  my $mdp = Music::Duration::Partition->new(
      pool => [qw(qn en sn)],
  );

  my $motif = $mdp->motif;

  # get the scale and compute a generating function
  my ($scale, $genf) = voss('A', 5, 'minor');

  for my $i (1 .. 8) {
      my @voices = map { $scale->[ $genf->($_) % @$scale ] } 0 .. $#$motif;

      $mdp->add_to_score($score, $motif, \@voices);

      $score->r('wn');
  }

  $score->write_score('voss.mid');

  sub voss {
      my ($note, $octave, $named) = @_;

      my @scale = get_scale_MIDI($note, $octave, $named);

      my $seed = [ map { sub { int rand 2 } } @scale ];
      my $genf = powers(calls => $seed);

      return \@scale, $genf;
  }

Weights and Grouping

Triplet durations will be twice as likely to be chosen, and will be played in groups of 3.

  use MIDI::Util qw(setup_score);
  use Music::Duration::Partition ();
  use Music::Scales qw(get_scale_MIDI);

  my $score = setup_score(bpm => 120);

  my @scale = get_scale_MIDI('C', 4, 'major');

  my $mdp = Music::Duration::Partition->new(
      size    => 8,
      pool    => [qw(hn qn ten)],
      weights => [   1, 1, 2   ],
      groups  => [   1, 1, 3   ],
      verbose => 1,
  );

  my $motif = $mdp->motif;

  for my $i (1 .. 4) {
      for my $n (0 .. $#$motif) {
          $score->n($motif->[$n], $scale[ int rand @scale ]);
      }
  }

  $score->n('wn', $scale[0]);

  $score->write_score('weighted-grouped.mid');

Figured Bass

Play a an alternating, randomized bass-line of 2 motifs, for 3 beats and rest for the 4th. Also play a steady closed hi-hat simultaneously.

  use MIDI::Drummer::Tiny ();
  use MIDI::Util qw(set_chan_patch);
  use Music::Duration::Partition ();
  use Music::Scales qw(get_scale_MIDI);
  use Music::VoiceGen ();

  my $d = MIDI::Drummer::Tiny->new(file => 'figured.mid');

  $d->sync(
      \&beat, # must come first so the channel is 9
      \&bass,
  );

  $d->write;

  sub beat {
      for my $n (1 .. 8) {
          $d->note($d->quarter, $d->closed_hh) for 1 .. 4;
      }
  }

  sub bass {
      set_chan_patch($d->score, 0, 35); # fretless bass on channel 0

      my $mdp = Music::Duration::Partition->new(
          size => 3,
          pool => [qw(qn en sn)],
      );

      my @motifs = $mdp->motifs(2);

      my @pitches = get_scale_MIDI('A', 2, 'pminor');

      my $voice = Music::VoiceGen->new(
          pitches   => \@pitches,
          intervals => [qw(-4 -3 -2 2 3 4)],
      );

      my @notes1 = map { $voice->rand } $motifs[0]->@*;

      for my $i (1 .. 8) {
          if ($i % 2) {
              $mdp->add_to_score($d->score, $motifs[0], \@notes1);
          }
          else {
              my @notes2 = map { $voice->rand } $motifs[1]->@*;
              $mdp->add_to_score($d->score, $motifs[1], \@notes2);
          }

          $d->rest($d->quarter);
      }
  }

7/8 Time

In x/8 time, the Music::Duration::Partition size must be the number of beats (i.e. 7 here) divided by 2.

  use MIDI::Drummer::Tiny ();
  use MIDI::Util qw(set_chan_patch);
  use Music::Duration::Partition ();
  use Music::Scales qw(get_scale_MIDI);
  use Music::VoiceGen ();

  my $d = MIDI::Drummer::Tiny->new(
      file      => 'odd-meter.mid',
      bpm       => 90,
      signature => '7/8',
      bars      => 8,
  );

  $d->sync(
      \&drums,
      \&bass,
  );

  $d->write;

  sub drums {
      $d->metronome78($d->bars * 2);
  }

  sub bass {
      set_chan_patch($d->score, 0, 35);

      my $mdp = Music::Duration::Partition->new(
          size   => 3.5, # == 7/2
          pool   => [qw(qn en)],
          groups => [   1, 2  ],
      );

      my ($motif1, $motif2) = $mdp->motifs(2);

      my @pitches = get_scale_MIDI('C', 2, 'pminor');
      my @intervals = qw(-3 -2 -1 1 2 3);
      my $voice = Music::VoiceGen->new(
          pitches   => \@pitches,
          intervals => \@intervals,
      );
      my @voices1 = map { $voice->rand } @$motif1;

      # add 2 bars to the score
      for my $n (1 .. $d->bars) {
          $mdp->add_to_score($score, $motif1, \@voices1);

          # get a fresh set of voices
          my @voices2 = map { $voice->rand } @$motif2;
          $mdp->add_to_score($score, $motif2, \@voices2);
      }

      $d->note($d->whole, $pitches[0]);
  }

Quick-start

Go back to the Music::Duration::Partition::Tutorial::Quickstart tutorial.

AUTHOR

Gene Boggs <gene@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2019-2024 by Gene Boggs.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.