File Coverage

blib/lib/Music/Voss.pm
Criterion Covered Total %
statement 50 79 63.2
branch 12 46 26.0
condition 5 36 13.8
subroutine 11 14 78.5
pod 3 3 100.0
total 81 178 45.5


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # Functions for fractal noise generation functions.
4             #
5             # Run perldoc(1) on this file for additional documentation.
6              
7             package Music::Voss;
8              
9 3     3   160293 use 5.010000;
  3         8  
10 3     3   9 use strict;
  3         4  
  3         47  
11 3     3   8 use warnings;
  3         6  
  3         71  
12 3     3   7 use Carp qw(croak);
  3         3  
  3         122  
13 3     3   10 use Exporter 'import';
  3         3  
  3         63  
14 3     3   9 use List::Util ();
  3         3  
  3         56  
15 3     3   8 use Scalar::Util qw(looks_like_number);
  3         7  
  3         2012  
16              
17             our $VERSION = '0.05';
18              
19             our @EXPORT_OK = qw(bitchange powers powers_stateless);
20              
21             # This method derived from the "White and brown music, fractal curves
22             # and one-over-f fluctuations" article. Should also be possible with
23             # physical dice and a binary chart to show how the bits change between
24             # tries and thus which dice need be re-rolled for a particular try.
25             sub bitchange {
26 0     0 1 0 my (%params) = @_;
27 0 0 0     0 if ( !exists $params{rollers} ) {
    0          
28 0         0 $params{rollers} = 3;
29             } elsif ( !defined $params{rollers} or !looks_like_number $params{rollers} ) {
30 0         0 croak "rollers must be a number";
31             } else {
32 0         0 $params{rollers} = int $params{rollers};
33             }
34 0 0 0     0 if ( !exists $params{roll} ) {
    0          
35 0     0   0 $params{roll} = sub { int rand 6 };
  0         0  
36             } elsif ( !defined $params{roll} or ref $params{roll} ne 'CODE' ) {
37 0         0 croak "roll must be code reference";
38             }
39 0 0 0     0 if ( !exists $params{summer} ) {
    0          
40 0         0 $params{summer} = \&List::Util::sum0;
41             } elsif ( !defined $params{summer} or ref $params{summer} ne 'CODE' ) {
42 0         0 croak "summer must be code reference";
43             }
44 0         0 my @nums = map { $params{roll}->( undef, $_ ) } 0 .. $params{rollers} - 1;
  0         0  
45 0         0 my $prev;
46             return sub {
47 0     0   0 my ($n) = @_;
48 0 0 0     0 croak "input must be number" if !defined $n or !looks_like_number $n;
49 0 0       0 if ( defined $prev ) {
50 0         0 for my $rnum ( 0 .. $params{rollers} - 1 ) {
51 0 0       0 if ( ( $n >> $rnum & 1 ) != ( $prev >> $rnum & 1 ) ) {
52 0         0 $nums[$rnum] = $params{roll}->( $n, $rnum );
53             }
54             }
55             }
56 0         0 $prev = $n;
57 0         0 return $params{summer}->(@nums);
58 0         0 };
59             }
60              
61             # "Musimathics, Vol 1" p.358 based function generator based on powers of
62             # (by default) 2, the supplied number, and a list of subroutines to
63             # (perhaps) run. (Was called "voss", orginally.)
64             sub powers {
65 2     2 1 285 my (%params) = @_;
66             croak "must be given list of calls"
67             if !$params{calls}
68 2 50 33     14 or ref $params{calls} ne 'ARRAY';
69 2 50 33     11 if ( !exists $params{summer} ) {
    50          
70 0         0 $params{summer} = \&List::Util::sum0;
71             } elsif ( !defined $params{summer} or ref $params{summer} ne 'CODE' ) {
72 0         0 croak "summer must be code reference";
73             }
74 2 50 0     4 if ( !exists $params{e} ) {
    0          
75 2         3 $params{e} = 2;
76             } elsif ( !defined $params{e} or !looks_like_number $params{e} ) {
77 0         0 croak "e must be a number";
78             }
79 2         1 my @nums = (0) x @{ $params{calls} };
  2         4  
80             return sub {
81 3     3   318 my ($n) = @_;
82 3 50 33     45 croak "input must be number" if !defined $n or !looks_like_number $n;
83 3         3 for my $k ( 0 .. $#{ $params{calls} } ) {
  3         9  
84 3 50       8 if ( $n % $params{e}**$k == 0 ) {
85 3         7 $nums[$k] = $params{calls}->[$k]->( $n, $k );
86             }
87             }
88 3         10 return $params{summer}->(@nums);
89 2         10 };
90             }
91              
92             sub powers_stateless {
93 2     2 1 641 my (%params) = @_;
94             croak "must be given list of calls"
95             if !$params{calls}
96 2 50 33     14 or ref $params{calls} ne 'ARRAY';
97 2 50 0     5 if ( !exists $params{summer} ) {
    0          
98 2         3 $params{summer} = \&List::Util::sum0;
99             } elsif ( !defined $params{summer} or ref $params{summer} ne 'CODE' ) {
100 0         0 croak "summer must be code reference";
101             }
102 2 50 0     3 if ( !exists $params{e} ) {
    0          
103 2         3 $params{e} = 2;
104             } elsif ( !defined $params{e} or !looks_like_number $params{e} ) {
105 0         0 croak "e must be a number";
106             }
107             return sub {
108 18     18   31 my ($n) = @_;
109 18 50 33     45 croak "input must be number" if !defined $n or !looks_like_number $n;
110 18         10 my @nums;
111 18         11 for my $k ( 0 .. $#{ $params{calls} } ) {
  18         27  
112 28 100       51 if ( $n % $params{e}**$k == 0 ) {
113 23         25 push @nums, $params{calls}->[$k]->( $n, $k );
114             }
115             }
116 18         77 return $params{summer}->(@nums);
117 2         8 };
118             }
119              
120             1;
121             __END__