|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # -*- Perl -*-  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Routines for musical canon construction. See also C of the  | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # L module for a command line tool interface to this  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # code, and the eg/ directory of this module's distribution for other  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # example scripts.  | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Run perldoc(1) on this file for additional documentation.  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Music::Canon;  | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
12
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
268650
 | 
 use 5.010000;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
    | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
21
 | 
 use List::Util qw/sum/;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
391
 | 
    | 
| 
15
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
2574
 | 
 use Moo;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54640
 | 
    | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
306
 | 
    | 
| 
16
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
9542
 | 
 use Music::AtonalUtil ();    # Forte Number to interval sets  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52532
 | 
    | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
192
 | 
    | 
| 
17
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
2404
 | 
 use Music::Scales qw/get_scale_nums is_scale/;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19282
 | 
    | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
373
 | 
    | 
| 
18
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
2500
 | 
 use namespace::clean;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
49577
 | 
    | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
20
 | 
    | 
| 
19
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
 
 | 
1008
 | 
 use Scalar::Util qw/blessed looks_like_number/;  | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
    | 
| 
 
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12730
 | 
    | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '2.04';  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Array indices for ascending versus descending scales (as some minor  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # scales are different, depending)  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $ASC = 0;  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $DSC = 1;  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $FORTE_NUMBER_RE;  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ##############################################################################  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # ATTRIBUTES  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has atonal => (  | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is      => 'rw',  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub { Music::AtonalUtil->new },  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has contrary => (  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is => 'rw',  | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   cocerce =>  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     sub { die "contrary needs boolean\n" if !defined $_[0]; $_[0] ? 1 : 0 },  | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub { 1 },  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   reader  => 'get_contrary',  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   writer  => 'set_contrary',  | 
| 
46
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has DEG_IN_SCALE => (  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is     => 'rw',  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   coerce => sub {  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     die "scale degrees must be integer greater than 1"  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       if !defined $_[0]  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       or !looks_like_number $_[0]  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       or $_[0] < 2;  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     int $_[0];  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub {  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     12;  | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has modal_chrome => (  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is     => 'rw',  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   coerce => sub {  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     die "modal_chrome needs troolean (-1,0,1)\n" if !defined $_[0];  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $_[0] <=> 0;  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub {  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0;  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   reader => 'get_modal_chrome',  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   writer => 'set_modal_chrome',  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has modal_hook => (  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is      => 'rw',  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub {  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     sub { undef }  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   isa => sub {  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     ref $_[0] eq 'CODE';  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # input tonic pitch for modal_map  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has modal_in => (  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is        => 'rw',  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   clearer   => 1,  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   predicate => 1,  | 
| 
90
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # output tonic pitch for modal_map  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has modal_out => (  | 
| 
94
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is        => 'rw',  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   clearer   => 1,  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   predicate => 1,  | 
| 
97
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # These have custom setters as support Forte Numbers and other such  | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # cases difficult to put into a simple coerce sub, so the user-facing  | 
| 
101
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # setter are really the set_modal_scale_* subs.  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has modal_scale_in => (  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is        => 'rw',  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   clearer   => 1,  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   predicate => 1,  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has modal_scale_out => (  | 
| 
108
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is        => 'rw',  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   clearer   => 1,  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   predicate => 1,  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has non_octave_scales => (  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is      => 'rw',  | 
| 
115
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   cocerce => sub {  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     die "non_octave_scales needs boolean\n" if !defined $_[0];  | 
| 
117
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     $_[0] ? 1 : 0;  | 
| 
118
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub {  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     0;  | 
| 
121
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   },  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has retrograde => (  | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is => 'rw',  | 
| 
126
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   cocerce =>  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     sub { die "retrograde needs boolean\n" if !defined $_[0]; $_[0] ? 1 : 0 },  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub { 1 },  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   reader  => 'get_retrograde',  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   writer  => 'set_retrograde',  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
133
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 has transpose => (  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   is      => 'rw',  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   default => sub { 0 },  | 
| 
136
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   reader  => 'get_transpose',  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   writer  => 'set_transpose',  | 
| 
138
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 );  | 
| 
139
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 ##############################################################################  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # METHODS  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub BUILD {  | 
| 
145
 | 
20
 | 
 
 | 
 
 | 
  
20
  
 | 
  
0
  
 | 
91
 | 
   my ( $self, $param ) = @_;  | 
| 
146
 | 
20
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
78
 | 
   with( exists $param->{pitchstyle} ? $param->{pitchstyle} : 'Music::PitchNum' );  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # as not expected to change much, if at all  | 
| 
149
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75046
 | 
   $FORTE_NUMBER_RE = $self->atonal->forte_number_re;  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
151
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # Major scale by default  | 
| 
152
 | 
20
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
172
 | 
   $self->modal_scale_in( [ [qw(2 2 1 2 2 2 1)], [qw(2 2 1 2 2 2 1)] ] )  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if !$self->has_modal_scale_in;  | 
| 
154
 | 
20
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
487
 | 
   $self->modal_scale_out( [ [qw(2 2 1 2 2 2 1)], [qw(2 2 1 2 2 2 1)] ] )  | 
| 
155
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if !$self->has_modal_scale_out;  | 
| 
156
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
157
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
158
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # One-to-one interval mapping, though with the contrary, retrograde, and  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # transpose parameters as possible influences on the results.  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub exact_map {  | 
| 
161
 | 
6
 | 
 
 | 
 
 | 
  
6
  
 | 
  
1
  
 | 
1399
 | 
   my $self = shift;  | 
| 
162
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
163
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
   my ( @new_phrase, $prev_in, $prev_out );  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
165
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
16
 | 
   for my $e ( ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_ ) {  | 
| 
 
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
5
 | 
    | 
| 
166
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
52
 | 
     my $pitch;  | 
| 
167
 | 
85
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
262
 | 
     if ( !defined $e ) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
168
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # presumably rests/silent bits  | 
| 
169
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push @new_phrase, undef;  | 
| 
170
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       next;  | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( blessed $e and $e->can('pitch') ) {  | 
| 
172
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $pitch = $e->pitch;  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( looks_like_number $e) {  | 
| 
174
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
62
 | 
       $pitch = $e;  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # pass through unknowns  | 
| 
177
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push @new_phrase, $e;  | 
| 
178
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       next;  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
181
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
     my $new_pitch;  | 
| 
182
 | 
85
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
79
 | 
     if ( !defined $prev_out ) {  | 
| 
183
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
       my $trans = $self->get_transpose;  | 
| 
184
 | 
6
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
       if ( !looks_like_number($trans) ) {  | 
| 
185
 | 
  
0
  
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
         my $transpose_to = $self->pitchnum($trans)  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           // die "pitchnum failed to parse '$trans'\n";  | 
| 
187
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         $trans = $transpose_to - $pitch;  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
189
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
       $new_pitch = $pitch + $trans;  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
191
 | 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
60
 | 
       my $delta = $pitch - $prev_in;  | 
| 
192
 | 
79
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
152
 | 
       $delta *= -1 if $self->get_contrary;  | 
| 
193
 | 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
       $new_pitch = $prev_out + $delta;  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
195
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
75
 | 
     push @new_phrase, $new_pitch;  | 
| 
196
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
     $prev_in  = $pitch;  | 
| 
197
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
     $prev_out = $new_pitch;  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
199
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
11
 | 
   @new_phrase = reverse @new_phrase if $self->get_retrograde;  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
201
 | 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
38
 | 
   return @new_phrase;  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # mostly for compatibility with older versions of this module  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub get_modal_pitches {  | 
| 
206
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
1
  
 | 
7
 | 
   my ($self) = @_;  | 
| 
207
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
   return $self->modal_in, $self->modal_out;  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub get_modal_scale_in {  | 
| 
211
 | 
8
 | 
 
 | 
 
 | 
  
8
  
 | 
  
1
  
 | 
1952
 | 
   return @{ $_[0]->modal_scale_in };  | 
| 
 
 | 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
53
 | 
    | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub get_modal_scale_out {  | 
| 
215
 | 
3
 | 
 
 | 
 
 | 
  
3
  
 | 
  
1
  
 | 
8
 | 
   return @{ $_[0]->modal_scale_out };  | 
| 
 
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
    | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Modal interval mapping - determines the number of diatonic steps and  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # chromatic offset (if any) from the direction and magnitude of the  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # delta from the previous input pitch via the input scale intervals,  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # then replays that number of diatonic steps and (if possible) chromatic  | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # offset via the output scale intervals. Ascending vs. descending motion  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # may be handled by different scale intervals, if a melodic minor or  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # similar asymmetric interval set is involved. If this sounds tricky and  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # complicated, it is because it is.  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub modal_map {  | 
| 
227
 | 
41
 | 
 
 | 
 
 | 
  
41
  
 | 
  
1
  
 | 
4779
 | 
   my $self = shift;  | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
229
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
33
 | 
   my ( $input_tonic, $output_tonic );  | 
| 
230
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
131
 | 
   if ( $self->has_modal_in ) {  | 
| 
231
 | 
18
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
39
 | 
     $input_tonic = $self->pitchnum( $self->modal_in )  | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       // die "pitchnum could not convert modal_in '", $self->modal_in,  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       "' to a pitch number\n";  | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
235
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
135
 | 
   if ( $self->has_modal_out ) {  | 
| 
236
 | 
18
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
34
 | 
     $output_tonic = $self->pitchnum( $self->modal_out )  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       // die "pitchnum could not convert modal_out '", $self->modal_out,  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       "' to a pitch number\n";  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
241
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
94
 | 
   my $input_mode = $self->modal_scale_in;  | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # local copy of the output scale in the event transposition forces a  | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # rotation of the intervals  | 
| 
244
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
   my $output_mode = $self->modal_scale_out;  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # but have to wait until have the first pitch as might be transposing  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # to a note instead of by some number  | 
| 
248
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26
 | 
   my $trans;  | 
| 
249
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
34
 | 
   my $rotate_by     = 0;  | 
| 
250
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
   my $rotate_chrome = 0;  | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
252
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
   my ( @new_phrase, $prev_in, $prev_out );  | 
| 
253
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
28
 | 
   my $phrase_index = 0;  | 
| 
254
 | 
41
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
88
 | 
   for my $obj ( ref $_[0] eq 'ARRAY' ? @{ $_[0] } : @_ ) {  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
255
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
154
 | 
     my $pitch;  | 
| 
256
 | 
271
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
846
 | 
     if ( !defined $obj ) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # presumably rests/silent bits  | 
| 
258
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push @new_phrase, undef;  | 
| 
259
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       next;  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( blessed $obj and $obj->can('pitch') ) {  | 
| 
261
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $pitch = $obj->pitch;  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( looks_like_number $obj) {  | 
| 
263
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
195
 | 
       $pitch = $obj;  | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # pass through unknowns  | 
| 
266
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       push @new_phrase, $obj;  | 
| 
267
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       next;  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
270
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
152
 | 
     my $new_pitch;  | 
| 
271
 | 
271
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
615
 | 
     if ( defined $prev_in and $pitch == $prev_in ) {  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # oblique motion optimization (a repeated note): just copy previous  | 
| 
273
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
       $new_pitch = $prev_out;  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Interval sets are useless without being tied to some pitch,  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # assume this is the first note of the phrase if not already set.  | 
| 
278
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
296
 | 
       $input_tonic = $pitch unless defined $input_tonic;  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # NOTE output tonic is not longer set based on transposed pitch  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # (as of v1.00); use set_modal_pitches() to specify as necessary.  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # This change motivated by transpose not really working  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # everywhere. Instead, output tonic by default is the same as the  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # input tonic (so the input and output modes share the same root  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # pitch by default).  | 
| 
286
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
282
 | 
       $output_tonic = $input_tonic unless defined $output_tonic;  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
288
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
272
 | 
       if ( !defined $trans ) {  | 
| 
289
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
54
 | 
         $trans = $self->get_transpose;  | 
| 
290
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
61
 | 
         if ( !looks_like_number($trans) ) {  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # Letter note: "transpose to 'A'" instead of "transpose by N"  | 
| 
292
 | 
1
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
4
 | 
           my $transpose_to = $self->pitchnum($trans)  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             // die 'pitchnum failed to parse ' . $self->transpose . "\n";  | 
| 
294
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
43
 | 
           $trans = $transpose_to - $pitch;  | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
297
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
62
 | 
         if ( $trans != 0 ) {  | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # Steps must be from input tonic to first note of phrase plus  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # transposition, as if in Bflat-Major if one has a phrase that  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # begins on "D" being moved to "Eflat" that transposition is  | 
| 
301
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # modal, and not chromatic.  | 
| 
302
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
           ( $rotate_by, $rotate_chrome ) =  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             ( $self->steps( $input_tonic, $input_tonic + $trans, $input_mode->[$ASC] ) )  | 
| 
304
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             [ 0, 1 ];  | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # inverted due to how M::AU->rotate works  | 
| 
306
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
           $rotate_by *= -1;  | 
| 
307
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
308
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
5
 | 
           if ( $rotate_chrome != 0 ) {  | 
| 
309
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
             die "transpose to chromatic pitch unsupported by modal_map()";  | 
| 
310
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           }  | 
| 
311
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
312
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # Transpositions require rotation of the output mode to match  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # where the starting pitch of the phrase lies in the output  | 
| 
314
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # mode, as otherwise for c-minor to c-minor, transposing from  | 
| 
315
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # C to E-flat, would for an input phrase of C->Bb->Ab get the  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # C->Bb->Ab intervals instead of those for Eb->D->C. That is,  | 
| 
317
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # the output would become E-flat minor by virtue of the  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # transposition without the rotation done here.  | 
| 
319
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
6
 | 
           if ( $rotate_by != 0 ) {  | 
| 
320
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
             $output_mode->[$ASC] =  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
               $self->atonal->rotate( $rotate_by, $output_mode->[$ASC] );  | 
| 
322
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
66
 | 
             $output_mode->[$DSC] =  | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
               $self->atonal->rotate( $rotate_by, $output_mode->[$DSC] );  | 
| 
324
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           }  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Determine whether input must be figured on the ascending or  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # descending scale intervals; descending intervals only if there  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # is a previous pitch and if the delta from that previous pitch  | 
| 
331
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # shows descending motion, otherwise ascending. The scales are  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # [[asc],[dsc]] AoA.  | 
| 
333
 | 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
191
 | 
       my $input_motion = $ASC;  | 
| 
334
 | 
267
 | 
  
100
  
 | 
  
100
  
 | 
 
 | 
 
 | 
632
 | 
       $input_motion = $DSC if defined $prev_in and $pitch - $prev_in < 0;  | 
| 
335
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
342
 | 
       my $output_motion = $self->get_contrary ? !$input_motion : $input_motion;  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
337
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Magnitude of interval from tonic, and whether above or below the  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # tonic (as if below, must walk scale intervals backwards).  | 
| 
339
 | 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
304
 | 
       my ( $steps, $chromatic_offset, $is_dsc, $last_input_interval ) =  | 
| 
340
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         $self->steps( $input_tonic, $pitch, $input_mode->[$input_motion] );  | 
| 
341
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
342
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Contrary motion means not only the opposite scale intervals,  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # but the opposite direction through those intervals (in  | 
| 
344
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # melodic minor, ascending motion in ascending intervals (C to  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Eflat) corresponds to descending motion in descending  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # intervals (C to Aflat).  | 
| 
347
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
416
 | 
       $is_dsc = !$is_dsc if $self->get_contrary;  | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
349
 | 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
165
 | 
       my $output_interval = 0;  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Replay the same number of diatonic steps using the appropriate  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # output intervals and direction of interval iteration, plus  | 
| 
353
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # chromatic adjustments, if any.  | 
| 
354
 | 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
152
 | 
       my $idx;  | 
| 
355
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
294
 | 
       if ($steps) {  | 
| 
356
 | 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
142
 | 
         $steps--;  | 
| 
357
 | 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
237
 | 
         for my $s ( 0 .. $steps ) {  | 
| 
358
 | 
1095
 | 
 
 | 
 
 | 
 
 | 
 
 | 
584
 | 
           $idx = $s % @{ $output_mode->[$output_motion] };  | 
| 
 
 | 
1095
 | 
 
 | 
 
 | 
 
 | 
 
 | 
779
 | 
    | 
| 
359
 | 
1095
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1107
 | 
           $idx = $#{ $output_mode->[$output_motion] } - $idx if $is_dsc;  | 
| 
 
 | 
707
 | 
 
 | 
 
 | 
 
 | 
 
 | 
528
 | 
    | 
| 
360
 | 
1095
 | 
 
 | 
 
 | 
 
 | 
 
 | 
873
 | 
           $output_interval += $output_mode->[$output_motion][$idx];  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
362
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
363
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
364
 | 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
151
 | 
       my $hooked = 0;  | 
| 
365
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
289
 | 
       if ( $chromatic_offset != 0 ) {  | 
| 
366
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
68
 | 
         my $step_interval = $output_mode->[$output_motion][$idx];  | 
| 
367
 | 
85
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
89
 | 
         my $step_dir = $step_interval < 0 ? -1 : 1;  | 
| 
368
 | 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
55
 | 
         $step_interval = abs $step_interval;  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
370
 | 
85
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
87
 | 
         if ( $chromatic_offset >= $step_interval ) {  | 
| 
371
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # Whoops, chromatic does not fit into output scale. Punt to hook  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           # function to handle everything for this pitch.  | 
| 
373
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
314
 | 
           $new_pitch = $self->modal_hook->(  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             $output_interval,  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             chromatic_offset => $chromatic_offset,  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             phrase_index     => $phrase_index,  | 
| 
377
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             scale            => $output_mode->[$output_motion],  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             scale_index      => $idx,  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             step_dir         => $step_dir,  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             step_interval    => $step_interval,  | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           );  | 
| 
382
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
22
 | 
           $hooked = 1;  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         } else {  | 
| 
384
 | 
68
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
67
 | 
           if ( $step_interval == 2 ) {  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # only one possible chromatic fits  | 
| 
386
 | 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
             $output_interval -= $step_dir * $chromatic_offset;  | 
| 
387
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           } else {  | 
| 
388
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # modal_chrome is a troolean - either a literal chromatic  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # going up or down if positive or negative, otherwise if 0  | 
| 
390
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # try to figure out something proportional to where the  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             # chromatic was between the diatonics of the input scale.  | 
| 
392
 | 
5
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
15
 | 
             if ( $self->get_modal_chrome > 0 ) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
393
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
               $output_interval -= $step_dir * $chromatic_offset;  | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } elsif ( $self->get_modal_chrome < 0 ) {  | 
| 
395
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
               $output_interval += $step_dir * ( $chromatic_offset - $step_interval );  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             } else {  | 
| 
397
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
               my $fraction = sprintf "%.0f",  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
                 $step_interval * $chromatic_offset / $last_input_interval;  | 
| 
399
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
6
 | 
               $output_interval += $step_dir * ( $fraction - $step_interval );  | 
| 
400
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
             }  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           }  | 
| 
402
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
403
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
404
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
405
 | 
267
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
299
 | 
       if ( !$hooked ) {  | 
| 
406
 | 
250
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
285
 | 
         $output_interval = int( $output_interval * -1 ) if $is_dsc;  | 
| 
407
 | 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
214
 | 
         $new_pitch = $output_tonic + $trans + $output_interval;  | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
409
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
410
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
411
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
216
 | 
     push @new_phrase, $new_pitch;  | 
| 
412
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
179
 | 
     $prev_in  = $pitch;  | 
| 
413
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
135
 | 
     $prev_out = $new_pitch;  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
415
 | 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
237
 | 
     $phrase_index++;  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
417
 | 
41
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
84
 | 
   @new_phrase = reverse @new_phrase if $self->get_retrograde;  | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
419
 | 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
223
 | 
   return @new_phrase;  | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
422
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub reset_modal_pitches {  | 
| 
423
 | 
2
 | 
 
 | 
 
 | 
  
2
  
 | 
  
1
  
 | 
28
 | 
   $_[0]->clear_modal_in;  | 
| 
424
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
302
 | 
   $_[0]->clear_modal_out;  | 
| 
425
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
426
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Mostly for compatibility with how older versions of this module  | 
| 
428
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # worked, and handy to do these in a single call.  | 
| 
429
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub set_modal_pitches {  | 
| 
430
 | 
17
 | 
 
 | 
 
 | 
  
17
  
 | 
  
1
  
 | 
4078
 | 
   my ( $self, $input_pitch, $output_pitch ) = @_;  | 
| 
431
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
432
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12
 | 
   my $pitch;  | 
| 
433
 | 
17
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
35
 | 
   if ( defined $input_pitch ) {  | 
| 
434
 | 
17
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
36
 | 
     $pitch = $self->pitchnum($input_pitch)  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       // die "pitchnum failed to parse $input_pitch\n";  | 
| 
436
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
92
 | 
     $self->modal_in($pitch);  | 
| 
437
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Auto-reset output if something prior there so not carrying along  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # something from a previous conversion, as the default is to use the  | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # same pitch for the output tonic as from the input.  | 
| 
440
 | 
17
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
33
 | 
     if ( !defined $output_pitch and $self->has_modal_out ) {  | 
| 
441
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $self->clear_modal_out;  | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
444
 | 
17
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
24
 | 
   if ( defined $output_pitch ) {  | 
| 
445
 | 
17
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
28
 | 
     $pitch = $self->pitchnum($output_pitch)  | 
| 
446
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       // die "pitchnum failed to parse $output_pitch\n";  | 
| 
447
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
74
 | 
     $self->modal_out($pitch);  | 
| 
448
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
449
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
450
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
451
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub set_modal_scale_in {  | 
| 
452
 | 
5
 | 
 
 | 
 
 | 
  
5
  
 | 
  
1
  
 | 
57
 | 
   my $self = shift;  | 
| 
453
 | 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
15
 | 
   $self->modal_scale_in( $self->scales2intervals(@_) );  | 
| 
454
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
455
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
456
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub set_modal_scale_out {  | 
| 
457
 | 
17
 | 
 
 | 
 
 | 
  
17
  
 | 
  
1
  
 | 
48
 | 
   my $self = shift;  | 
| 
458
 | 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
27
 | 
   $self->modal_scale_out( $self->scales2intervals(@_) );  | 
| 
459
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
460
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
461
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub scales2intervals {  | 
| 
462
 | 
22
 | 
 
 | 
 
 | 
  
22
  
 | 
  
1
  
 | 
24
 | 
   my ( $self, $asc, $dsc ) = @_;  | 
| 
463
 | 
22
 | 
  
  0
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
45
 | 
   if ( !defined $asc and !defined $dsc ) {  | 
| 
464
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     die "must define one of asc or dsc or both";  | 
| 
465
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
466
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
467
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
19
 | 
   my @intervals;  | 
| 
468
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
16
 | 
   my $is_scale = 0;  | 
| 
469
 | 
22
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
35
 | 
   if ( defined $asc ) {  | 
| 
470
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
116
 | 
     if ( ref $asc eq 'ARRAY' ) {  | 
| 
 
 | 
 
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
471
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # Assume arbitrary list of intervals as integers if array ref  | 
| 
472
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
18
 | 
       for my $n (@$asc) {  | 
| 
473
 | 
65
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
276
 | 
         die "ascending intervals must be positive integers"  | 
| 
474
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           unless looks_like_number $n and $n =~ m/^[+]?[0-9]+$/;  | 
| 
475
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
476
 | 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
35
 | 
       $intervals[$ASC] = [@$asc];  | 
| 
477
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
478
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( $asc =~ m/($FORTE_NUMBER_RE)/ ) {  | 
| 
479
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # derive scale intervals from pitches of the named Forte Number  | 
| 
480
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
       my $pset = $self->atonal->forte2pcs($1);  | 
| 
481
 | 
3
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
21
 | 
       die "no Forte Number parsed from ascending '$asc'" unless defined $pset;  | 
| 
482
 | 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
21
 | 
       $intervals[$ASC] = $self->atonal->pcs2intervals($pset);  | 
| 
483
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
484
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
485
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
27
 | 
       die "ascending scale '$asc' unknown to Music::Scales"  | 
| 
486
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         unless is_scale($asc);  | 
| 
487
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
       my @asc_nums = get_scale_nums($asc);  | 
| 
488
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
70
 | 
       my @dsc_nums;  | 
| 
489
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
14
 | 
       @dsc_nums = get_scale_nums( $asc, 1 ) unless defined $dsc;  | 
| 
490
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
491
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
47
 | 
       $intervals[$ASC] = [];  | 
| 
492
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11
 | 
       for my $i ( 1 .. $#asc_nums ) {  | 
| 
493
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
17
 | 
         push @{ $intervals[$ASC] }, $asc_nums[$i] - $asc_nums[ $i - 1 ];  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
37
 | 
    | 
| 
494
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
495
 | 
4
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
10
 | 
       if (@dsc_nums) {  | 
| 
496
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
7
 | 
         $intervals[$DSC] = [];  | 
| 
497
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
9
 | 
         for my $i ( 1 .. $#dsc_nums ) {  | 
| 
498
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
14
 | 
           unshift @{ $intervals[$DSC] }, $dsc_nums[ $i - 1 ] - $dsc_nums[$i];  | 
| 
 
 | 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
40
 | 
    | 
| 
499
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         }  | 
| 
500
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
501
 | 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8
 | 
       $is_scale = 1;  | 
| 
502
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
503
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
504
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
505
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
74
 | 
   if ( !defined $dsc ) {  | 
| 
506
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Assume descending equals ascending (true in most cases, except  | 
| 
507
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # melodic minor and similar), unless a scale was involved, as the  | 
| 
508
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Music::Scales code should already have setup the descending bit.  | 
| 
509
 | 
20
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
41
 | 
     $intervals[$DSC] = $intervals[$ASC] unless $is_scale;  | 
| 
510
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   } else {  | 
| 
511
 | 
2
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
33
 | 
     if ( ref $dsc eq 'ARRAY' ) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
512
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2
 | 
       for my $n (@$dsc) {  | 
| 
513
 | 
6
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
26
 | 
         die "descending intervals must be positive integers"  | 
| 
514
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
           unless looks_like_number $n and $n =~ m/^[+]?[0-9]+$/;  | 
| 
515
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
516
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
3
 | 
       $intervals[$DSC] = [@$dsc];  | 
| 
517
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
518
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( $dsc =~ m/($FORTE_NUMBER_RE)/ ) {  | 
| 
519
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       # derive scale intervals from pitches of the named Forte Number  | 
| 
520
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
       my $pset = $self->atonal->forte2pcs($1);  | 
| 
521
 | 
1
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
7
 | 
       die "no Forte Number parsed from descending '$dsc'" unless defined $pset;  | 
| 
522
 | 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
4
 | 
       $intervals[$DSC] = $self->atonal->pcs2intervals($pset);  | 
| 
523
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
524
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
525
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       die "descending scale '$dsc' unknown to Music::Scales"  | 
| 
526
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         unless is_scale($dsc);  | 
| 
527
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       my @dsc_nums = get_scale_nums( $dsc, 1 );  | 
| 
528
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
529
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       $intervals[$DSC] = [];  | 
| 
530
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
       for my $i ( 1 .. $#dsc_nums ) {  | 
| 
531
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         unshift @{ $intervals[$DSC] }, $dsc_nums[ $i - 1 ] - $dsc_nums[$i];  | 
| 
 
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
532
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
533
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
534
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
535
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
536
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # Complete scales to sum to 12 by default (Music::Scales omits the VII  | 
| 
537
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   # to I interval, and who knows what a custom list would contain).  | 
| 
538
 | 
22
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
54
 | 
   if ( !$self->non_octave_scales ) {  | 
| 
539
 | 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
24
 | 
     for my $ref (@intervals) {  | 
| 
540
 | 
40
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
210
 | 
       my $sum = sum(@$ref) // 0;  | 
| 
541
 | 
40
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
53
 | 
       die "empty interval set\n" if $sum == 0;  | 
| 
542
 | 
40
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
546
 | 
       if ( $sum < $self->DEG_IN_SCALE ) {  | 
| 
 
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
543
 | 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1711
 | 
         push @$ref, $self->DEG_IN_SCALE - $sum;  | 
| 
544
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       } elsif ( $sum > $self->DEG_IN_SCALE ) {  | 
| 
545
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
         die "non-octave scales require non_octave_scales param";  | 
| 
546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       }  | 
| 
547
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
548
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
549
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
550
 | 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
323
 | 
   return \@intervals;  | 
| 
551
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
552
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
553
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub steps {  | 
| 
554
 | 
272
 | 
 
 | 
 
 | 
  
272
  
 | 
  
1
  
 | 
245
 | 
   my ( $self, $from, $to, $scale ) = @_;  | 
| 
555
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
556
 | 
272
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
411
 | 
   die "from pitch must be a number\n" if !looks_like_number $from;  | 
| 
557
 | 
272
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
371
 | 
   die "to pitch must be a number\n"   if !looks_like_number $to;  | 
| 
558
 | 
272
 | 
  
 50
  
 | 
  
 33
  
 | 
 
 | 
 
 | 
723
 | 
   die "scales must be reference to two array ref of intervals\n"  | 
| 
559
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if !defined $scale  | 
| 
560
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     or ref $scale ne 'ARRAY';  | 
| 
561
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
562
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
193
 | 
   my $delta = $to - $from;  | 
| 
563
 | 
272
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
260
 | 
   my $dir = $delta < 0 ? $DSC : $ASC;  | 
| 
564
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
161
 | 
   $delta = abs $delta;  | 
| 
565
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
566
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
186
 | 
   my $running_total = 0;  | 
| 
567
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
162
 | 
   my $steps         = 0;  | 
| 
568
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
149
 | 
   my $index         = 0;  | 
| 
569
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
321
 | 
   while ( $running_total < $delta ) {  | 
| 
570
 | 
1105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
717
 | 
     $index = $steps++ % @$scale;  | 
| 
571
 | 
1105
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1117
 | 
     $index = $#{$scale} - $index if $dir == $DSC;  | 
| 
 
 | 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
281
 | 
    | 
| 
572
 | 
1105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1266
 | 
     $running_total += $scale->[$index];  | 
| 
573
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
574
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
575
 | 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
374
 | 
   return $steps, $running_total - $delta, $dir, $scale->[$index];  | 
| 
576
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
577
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
578
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
579
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |