File Coverage

blib/lib/Music/MelodicDevice/Arpeggiation.pm
Criterion Covered Total %
statement 42 42 100.0
branch 9 12 75.0
condition 7 12 58.3
subroutine 9 9 100.0
pod 2 2 100.0
total 69 77 89.6


line stmt bran cond sub pod time code
1             package Music::MelodicDevice::Arpeggiation;
2             our $AUTHORITY = 'cpan:GENE';
3              
4             # ABSTRACT: Apply arpeggiation patterns to groups of notes
5              
6             our $VERSION = '0.0302';
7              
8 2     2   595536 use Moo;
  2         17548  
  2         13  
9 2     2   4232 use strictures 2;
  2         3578  
  2         78  
10 2     2   1848 use Array::Circular ();
  2         2621  
  2         60  
11 2     2   1162 use Data::Dumper::Compact qw(ddc);
  2         35399  
  2         12  
12 2     2   1493 use namespace::clean;
  2         42585  
  2         17  
13              
14 2     2   776 use constant TICKS => 96;
  2         5  
  2         2380  
15              
16             my $DISPATCH = {
17             up => sub { my ($notes) = @_; return [ 0 .. $#$notes ] },
18             down => sub { my ($notes) = @_; return [ reverse(0 .. $#$notes) ] },
19             updown => sub { my ($notes) = @_; return [ 0 .. $#$notes, reverse(1 .. $#$notes - 1) ] },
20             random => sub { my ($notes) = @_; return [ map { rand @$notes } @$notes ] },
21             };
22              
23              
24             has type => (
25             is => 'rw',
26             isa => sub { die "$_[0] is not a known named type" unless exists $DISPATCH->{$_[0]} },
27             default => sub { 'up' },
28             );
29              
30              
31             has duration => (
32             is => 'rw',
33             isa => sub { die "$_[0] is not a valid duration" unless $_[0] =~ /^\d+\.?(\d+)?$/ },
34             default => sub { 1 },
35             );
36              
37              
38             has repeats => (
39             is => 'rw',
40             isa => sub { die "$_[0] is not a positive integer" unless $_[0] =~ /^\d+$/ },
41             default => sub { 1 },
42             );
43              
44              
45             has verbose => (
46             is => 'rw',
47             isa => sub { die "$_[0] is not a valid boolean" unless $_[0] =~ /^[01]$/ },
48             default => sub { 0 },
49             );
50              
51              
52              
53             sub arp {
54 10     10 1 9558 my ($self, $notes, $duration, $type, $repeats) = @_;
55              
56 10   33     40 $duration ||= $self->duration;
57 10   33     24 $type ||= $self->type;
58 10   66     317 $repeats ||= $self->repeats;
59              
60 10 50       115 my $pattern = ref $type eq 'ARRAY' ? $type : $self->_build_pattern($type, $notes);
61              
62 10         71 my $pat = Array::Circular->new(@$pattern);
63              
64             # compute the arp durations
65 10         235 my $x = $duration * TICKS;
66 10         98 my $z = sprintf '%0.f', $x / @$pattern;
67 10 50       295 print "Durations: $x, $z\n" if $self->verbose;
68 10         114 $z = 'd' . $z;
69              
70 10         20 my @arp;
71 10         31 for my $i (1 .. $repeats) {
72 12         102 for my $j (1 .. @$pattern) {
73 33 100       654 push @arp, [ $z, $notes->[ $pat->current ] ]
74             if $pat->current < @$notes;
75 33         813 $pat->next;
76             }
77             }
78 10 50       708 print 'Arp: ', ddc(\@arp) if $self->verbose;
79              
80 10         106 return \@arp;
81             }
82              
83             sub _build_pattern {
84 11     11   1770 my ($self, $type, $notes) = @_;
85 11         32 return $self->arp_type($type)->($notes);
86             }
87              
88              
89             sub arp_type {
90 15     15 1 2762 my ($self, $type, $coderef) = @_;
91 15 100 100     126 if ($type && $coderef) {
    100          
92 1         5 $DISPATCH->{$type} = $coderef;
93             }
94             elsif ($type) {
95 13         55 return $DISPATCH->{$type};
96             }
97             else {
98 1         3 return $DISPATCH;
99             }
100             }
101              
102             1;
103              
104             __END__