File Coverage

blib/lib/Music/RecRhythm.pm
Criterion Covered Total %
statement 70 70 100.0
branch 14 14 100.0
condition 9 12 75.0
subroutine 15 15 100.0
pod 5 6 83.3
total 113 117 96.5


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # rhythms within rhythms within rhythms
4             #
5             # Run perldoc(1) on this file for additional documentation.
6              
7             package Music::RecRhythm;
8              
9 3     3   113394 use 5.10.0;
  3         8  
10 3     3   9 use strict;
  3         3  
  3         44  
11 3     3   9 use warnings;
  3         6  
  3         50  
12              
13 3     3   2620 use Math::BigInt ();
  3         44263  
  3         91  
14 3     3   1433 use Moo;
  3         27454  
  3         10  
15 3     3   4121 use namespace::clean;
  3         23202  
  3         9  
16 3     3   400 use List::Util qw(sum0);
  3         4  
  3         219  
17 3     3   11 use Scalar::Util qw(looks_like_number);
  3         3  
  3         1792  
18              
19             our $VERSION = '0.02';
20              
21             with 'MooX::Rebuild';
22              
23             has next => ( is => 'rw', );
24              
25             has set => (
26             is => 'rw',
27             coerce => sub {
28             my ($set) = @_;
29             die "need a set of positive integers"
30             if !Music::RecRhythm->validate_set($set);
31             for my $n (@$set) {
32             $n = int $n;
33             }
34             return $set;
35             },
36             trigger => sub {
37             my ( $self, $set ) = @_;
38             $self->_set_count( scalar @$set );
39             $self->_set_sum( sum0(@$set) );
40             },
41             );
42             has count => ( is => 'rwp' );
43             has sum => ( is => 'rwp' );
44              
45             # flag to skip the callback (though the rhythm will still be present in
46             # the recursion)
47             has is_silent => (
48             is => 'rw',
49             default => sub { 0 },
50             coerce => sub { $_[0] ? 1 : 0 },
51             );
52              
53             sub BUILD {
54 16     16 0 83 my ( $self, $param ) = @_;
55 16 100       211 die "need a set of positive integers" if !exists $param->{set};
56             }
57              
58             ########################################################################
59             #
60             # METHODS
61              
62             sub audible_levels {
63 5     5 1 7 my ($self) = @_;
64 5         4 my $count = 0;
65 5         10 while ($self) {
66 13 100       225 $count++ unless $self->is_silent;
67 13         512 $self = $self->next;
68             }
69 5         23 return $count;
70             }
71              
72             sub beatfactor {
73 10     10 1 44 my ($self) = @_;
74 10         8 my %factors;
75 10         9 my $prev_sum = 1;
76 10         19 while ($self) {
77 26         33 my $sum = $self->sum * $prev_sum;
78 26         22 @factors{ @{ $self->set }, $sum } = ();
  26         370  
79 26         504 $self = $self->next;
80 26         41 $prev_sum = $sum;
81             }
82 10         44 return Math::BigInt->bone()->blcm( keys %factors )->numify;
83             }
84              
85             sub levels {
86 5     5 1 13530 my ($self) = @_;
87 5         6 my $count = 0;
88 5         10 while ($self) {
89 13         10 $count++;
90 13         24 $self = $self->next;
91             }
92 5         17 return $count;
93             }
94              
95             sub recurse {
96 5     5 1 6 my ( $self, $callback, $extra ) = @_;
97 5         8 my $bf = $self->beatfactor;
98 5         2823 _recurse( $self, $callback, $extra, $bf, 0, 0 );
99             }
100              
101             sub _recurse {
102 75     75   65 my ( $rset, $callback, $extra, $totaltime, $level, $audible_level ) = @_;
103 75         85 my %param = ( level => $level, audible_level => $audible_level );
104 75         63 for my $p (qw/next set/) {
105 150         1094 $param{$p} = $rset->$p;
106             }
107 75         1081 my $sil = $rset->is_silent;
108 75 100       258 $audible_level++ if !$sil;
109 75         89 my $unittime = $totaltime / $rset->sum;
110 75         52 for my $n ( 0 .. $#{ $param{set} } ) {
  75         123  
111 472         371 $param{beat} = $param{set}[$n];
112 472         287 $param{index} = $n;
113 472         346 $param{duration} = int( $unittime * $param{beat} );
114 472 100       486 if ( !$sil ) {
115 469         517 $callback->( $rset, \%param, $extra );
116             }
117             _recurse( $param{next}, $callback, $extra, $param{duration}, $level + 1,
118             $audible_level )
119 472 100       1593 if defined $param{next};
120             }
121             }
122              
123             sub validate_set {
124 21     21 1 31 my ( $class, $set ) = @_;
125 21 100 100     167 return 0 if !defined $set or ref $set ne 'ARRAY' or !@$set;
      100        
126 17         28 for my $x (@$set) {
127 56 100 33     259 return 0 if !defined $x or !looks_like_number $x or $x < 1;
      66        
128             }
129 16         28 return 1;
130             }
131              
132             1;
133             __END__