line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package BokkaKumiai; |
2
|
1
|
|
|
1
|
|
1983
|
use Mouse; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
use Mouse::Util::TypeConstraints; |
4
|
|
|
|
|
|
|
our $VERSION = '0.02'; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
#- type |
7
|
|
|
|
|
|
|
subtype 'BokkaKumiai::Keys' |
8
|
|
|
|
|
|
|
=> as 'Str', |
9
|
|
|
|
|
|
|
=> where { $_ =~ /^(C|C#|Db|D|D#|Eb|E|F|F#|Gb|G|G#|Ab|A|A#|Bb|B)$/ } |
10
|
|
|
|
|
|
|
=> message { "This key ($_) is not musical keys!" } |
11
|
|
|
|
|
|
|
; |
12
|
|
|
|
|
|
|
subtype 'BokkaKumiai::Time' |
13
|
|
|
|
|
|
|
=> as 'Str', |
14
|
|
|
|
|
|
|
=> where { $_ =~ /^\d+\/\d+$/ } |
15
|
|
|
|
|
|
|
=> message { "This time ($_) is not musical time!" } |
16
|
|
|
|
|
|
|
; |
17
|
|
|
|
|
|
|
subtype 'BokkaKumiai::Beat' |
18
|
|
|
|
|
|
|
=> as 'Int', |
19
|
|
|
|
|
|
|
=> where { $_ =~ /^(2|4|8|16)$/ }, |
20
|
|
|
|
|
|
|
=> message { "This beat ($_) is not musical beat!" } |
21
|
|
|
|
|
|
|
; |
22
|
|
|
|
|
|
|
subtype 'BokkaKumiai::Tension' |
23
|
|
|
|
|
|
|
=> as 'Int', |
24
|
|
|
|
|
|
|
=> where { $_ =~ /^(undef|0|1|2|3|4)$/ } |
25
|
|
|
|
|
|
|
=> message { "This tention level ($_) is not supperted by BokkaKumiai.enter 1-4" } |
26
|
|
|
|
|
|
|
; |
27
|
|
|
|
|
|
|
subtype 'BokkaKumiai::OneRow' |
28
|
|
|
|
|
|
|
=> as 'Int', |
29
|
|
|
|
|
|
|
=> where { $_ =~ /^(2|4)$/ } |
30
|
|
|
|
|
|
|
=> message { "This bars_by_one_row ($_) is not supperted by BokkaKumiai: enter 2 or 4" } |
31
|
|
|
|
|
|
|
; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
#- input |
34
|
|
|
|
|
|
|
has 'key' => ( |
35
|
|
|
|
|
|
|
is => 'rw', |
36
|
|
|
|
|
|
|
isa => 'BokkaKumiai::Keys', |
37
|
|
|
|
|
|
|
required => 1, |
38
|
|
|
|
|
|
|
); |
39
|
|
|
|
|
|
|
has 'time' => ( |
40
|
|
|
|
|
|
|
is => 'rw', |
41
|
|
|
|
|
|
|
isa => 'BokkaKumiai::Time', |
42
|
|
|
|
|
|
|
required => 1, |
43
|
|
|
|
|
|
|
default => '4/4', |
44
|
|
|
|
|
|
|
); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
has 'beat' => ( |
47
|
|
|
|
|
|
|
is => 'rw', |
48
|
|
|
|
|
|
|
isa => 'BokkaKumiai::Beat', |
49
|
|
|
|
|
|
|
default => 4, |
50
|
|
|
|
|
|
|
required => 1, |
51
|
|
|
|
|
|
|
); |
52
|
|
|
|
|
|
|
has 'pattern' => ( |
53
|
|
|
|
|
|
|
is => 'rw', |
54
|
|
|
|
|
|
|
isa => 'Str', |
55
|
|
|
|
|
|
|
required => 1, |
56
|
|
|
|
|
|
|
); |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
has 'chord_progress' => ( #- コード進行 |
59
|
|
|
|
|
|
|
is => 'rw', |
60
|
|
|
|
|
|
|
isa => 'ArrayRef', |
61
|
|
|
|
|
|
|
); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
has 'tension' => ( |
64
|
|
|
|
|
|
|
is => 'rw', |
65
|
|
|
|
|
|
|
isa => 'BokkaKumiai::Tension', |
66
|
|
|
|
|
|
|
); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
has 'bars_by_one_row' => ( #- 一行の小節数(タブ) |
69
|
|
|
|
|
|
|
is => 'rw', |
70
|
|
|
|
|
|
|
isa => 'BokkaKumiai::OneRow', |
71
|
|
|
|
|
|
|
default => 2, |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
74
|
|
|
|
|
|
|
no Mouse; |
75
|
|
|
|
|
|
|
no Mouse::Util::TypeConstraints; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
use Data::Dumper; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
#- customize your favorite chords |
80
|
|
|
|
|
|
|
#- if undefined, substituted by auto calculated chords. |
81
|
|
|
|
|
|
|
my $guitar_chords = +{ |
82
|
|
|
|
|
|
|
'standard' => +{ |
83
|
|
|
|
|
|
|
'C' => [qw(0 1 0 2 3 X)], |
84
|
|
|
|
|
|
|
'Cm' =>[qw(3 4 5 5 3 3)], |
85
|
|
|
|
|
|
|
'C6'=> [qw(0 1 2 2 3 X)], |
86
|
|
|
|
|
|
|
'C69'=>[qw(0 3 2 2 3 X)], |
87
|
|
|
|
|
|
|
'CM7'=>[qw(0 0 0 2 3 X)], |
88
|
|
|
|
|
|
|
'C7' =>[qw(0 1 3 2 3 X)], |
89
|
|
|
|
|
|
|
'C#' =>[qw(4 6 6 6 4 4)], |
90
|
|
|
|
|
|
|
'C#M7'=>[qw(4 6 5 6 4 4)], |
91
|
|
|
|
|
|
|
'D' => [qw(2 3 2 0 0 X)], |
92
|
|
|
|
|
|
|
'D7'=> [qw(2 1 2 0 0 X)], |
93
|
|
|
|
|
|
|
'Dm'=> [qw(1 3 2 0 0 X)], |
94
|
|
|
|
|
|
|
'Dm7'=>[qw(1 1 2 0 0 X)], |
95
|
|
|
|
|
|
|
'Eb'=> [qw(6 8 8 8 6 6)], |
96
|
|
|
|
|
|
|
'Eb7'=>[qw(6 8 6 8 6 6)], |
97
|
|
|
|
|
|
|
'E'=> [qw(0 0 1 2 2 0)], |
98
|
|
|
|
|
|
|
'E7'=> [qw(0 0 1 0 2 0)], |
99
|
|
|
|
|
|
|
'Em'=> [qw(0 0 0 2 2 0)], |
100
|
|
|
|
|
|
|
'Em7'=>[qw(0 0 0 0 2 0)], |
101
|
|
|
|
|
|
|
'F' => [qw(1 1 2 3 3 1)], |
102
|
|
|
|
|
|
|
'Fm'=> [qw(1 1 1 3 3 1)], |
103
|
|
|
|
|
|
|
'FM7'=>[qw(0 1 2 3 3 X)], |
104
|
|
|
|
|
|
|
'FM79'=>[qw(0 1 0 3 3 X)], |
105
|
|
|
|
|
|
|
'G' => [qw(3 0 0 0 2 3)], |
106
|
|
|
|
|
|
|
'Gm'=> [qw(3 3 3 5 5 3)], |
107
|
|
|
|
|
|
|
'G7'=> [qw(1 0 0 0 2 3)], |
108
|
|
|
|
|
|
|
'Ab'=> [qw(4 4 5 6 6 4)], |
109
|
|
|
|
|
|
|
'Ab6'=>[qw(X 6 5 6 6 X)], |
110
|
|
|
|
|
|
|
'Ab7'=>[qw(4 4 5 4 6 4)], |
111
|
|
|
|
|
|
|
'Am'=> [qw(0 1 2 2 0 0)], |
112
|
|
|
|
|
|
|
'Am7'=>[qw(0 1 0 2 0 0)], |
113
|
|
|
|
|
|
|
'Bb'=> [qw(1 3 3 3 1 1)], |
114
|
|
|
|
|
|
|
'Bbm'=>[qw(1 2 3 3 1 1)], |
115
|
|
|
|
|
|
|
'Bb7'=>[qw(1 3 1 3 1 1)], |
116
|
|
|
|
|
|
|
'Bbm7'=>[qw(1 2 1 3 1 1)], |
117
|
|
|
|
|
|
|
'B'=> [qw(2 4 4 4 2 2)], |
118
|
|
|
|
|
|
|
'Bm'=> [qw(2 3 4 4 2 2)], |
119
|
|
|
|
|
|
|
}, |
120
|
|
|
|
|
|
|
'funky' => +{ |
121
|
|
|
|
|
|
|
#- now developing.. |
122
|
|
|
|
|
|
|
}, |
123
|
|
|
|
|
|
|
}; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
#- サブルーチン群 |
126
|
|
|
|
|
|
|
#- コード進行出力 |
127
|
|
|
|
|
|
|
sub print_chord_progress { |
128
|
|
|
|
|
|
|
my ( $self ) = shift; |
129
|
|
|
|
|
|
|
my ( $output ) = "Time: $self->{time}\n"; |
130
|
|
|
|
|
|
|
$output .= "Beat: $self->{beat}\n"; |
131
|
|
|
|
|
|
|
$output .= "Key : $self->{key}\n"; |
132
|
|
|
|
|
|
|
my ( $cntr ) = 0; |
133
|
|
|
|
|
|
|
foreach my $bar ( @{$self->{chord_progress}} ){ |
134
|
|
|
|
|
|
|
$output .= sprintf("| %-8s", $bar); |
135
|
|
|
|
|
|
|
$cntr++; |
136
|
|
|
|
|
|
|
if ( $cntr % 4 eq 0 ) { |
137
|
|
|
|
|
|
|
$output .= "|\n"; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
print $output . "\n"; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
#- 拍とビートのチェック(制約) |
143
|
|
|
|
|
|
|
sub check_time_and_beat { |
144
|
|
|
|
|
|
|
my ( $self, $beat, $time ) = @_; |
145
|
|
|
|
|
|
|
if ( ( $beat >= 8 ) && ( $time ne '8/8' ) ) { |
146
|
|
|
|
|
|
|
print "Error: 8 or 16 beat must be used in 8/8 time music.\n"; |
147
|
|
|
|
|
|
|
exit; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
#- コード進行をパターンから生成 |
152
|
|
|
|
|
|
|
sub mk_chord_progress { |
153
|
|
|
|
|
|
|
my $self = shift; |
154
|
|
|
|
|
|
|
$self->check_time_and_beat($self->{beat}, $self->{time});; |
155
|
|
|
|
|
|
|
my $cp; #- array ref |
156
|
|
|
|
|
|
|
if ( $self->{pattern} eq 'pachelbel' ) { |
157
|
|
|
|
|
|
|
$self->{chord_progress} = ['I V/VII', 'VIm IIIm/V', 'IV I/III', 'IV/II V7']; |
158
|
|
|
|
|
|
|
} elsif ( $self->{pattern} eq 'blues' ) { |
159
|
|
|
|
|
|
|
$self->{chord_progress} = ['I', 'I', 'I', 'I', 'IV', 'IV', 'I', 'I', 'V', 'IV', 'I', 'V7']; |
160
|
|
|
|
|
|
|
} elsif ( $self->{pattern} eq 'vamp' ) { |
161
|
|
|
|
|
|
|
$self->{chord_progress} = ['I', 'I', 'IV', 'IV', 'I', 'I', 'IV', 'IV']; |
162
|
|
|
|
|
|
|
} elsif ( $self->{pattern} eq 'icecream' ) { |
163
|
|
|
|
|
|
|
$self->{chord_progress} = ['I', 'VIm', 'IIm', 'V7', 'I', 'VIm', 'IIm', 'V7']; |
164
|
|
|
|
|
|
|
} elsif ( $self->{pattern} eq 'major3' ) { |
165
|
|
|
|
|
|
|
$self->{chord_progress} = ['bVI', 'bVII', 'I', 'I']; |
166
|
|
|
|
|
|
|
} elsif ( $self->{pattern} eq 'iwantyouback' ) { |
167
|
|
|
|
|
|
|
$self->{chord_progress} = ['I','IV','VIm I/III IVM7 I','IIm7 V7 I I']; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
if ( $self->{tension} ) { |
170
|
|
|
|
|
|
|
$self->add_tension; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
$self->adjust_keys; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
#- キーに合わせる |
176
|
|
|
|
|
|
|
sub adjust_keys { |
177
|
|
|
|
|
|
|
my ( $self ) = shift; |
178
|
|
|
|
|
|
|
my ( $wholetone ) = ['C','C#', 'D', 'Eb', 'E', 'F', 'F#','G', 'Ab', 'A', 'Bb', 'B']; |
179
|
|
|
|
|
|
|
my ( $relative_tone ) = { |
180
|
|
|
|
|
|
|
'I' => 0, |
181
|
|
|
|
|
|
|
'#I' => 1, |
182
|
|
|
|
|
|
|
'II' => 2, |
183
|
|
|
|
|
|
|
'bIII' => 3, |
184
|
|
|
|
|
|
|
'III' => 4, |
185
|
|
|
|
|
|
|
'IV' => 5, |
186
|
|
|
|
|
|
|
'#IV'=>6, |
187
|
|
|
|
|
|
|
'V' => 7, |
188
|
|
|
|
|
|
|
'bVI'=>8, |
189
|
|
|
|
|
|
|
'VI'=>9, |
190
|
|
|
|
|
|
|
'bVII' => 10, |
191
|
|
|
|
|
|
|
'VII' => 11 |
192
|
|
|
|
|
|
|
}; |
193
|
|
|
|
|
|
|
$wholetone = $self->arrange_order( $wholetone ); |
194
|
|
|
|
|
|
|
my ( $many_chords ) = 0; |
195
|
|
|
|
|
|
|
my ( $pedal_chords ) = 0; |
196
|
|
|
|
|
|
|
foreach my $bar ( @{$self->{chord_progress}} ) { |
197
|
|
|
|
|
|
|
my @chords; |
198
|
|
|
|
|
|
|
if ( $bar =~ /\s+/ ) { |
199
|
|
|
|
|
|
|
@chords = split (/\s+/, $bar); |
200
|
|
|
|
|
|
|
$many_chords = 1; |
201
|
|
|
|
|
|
|
} else { |
202
|
|
|
|
|
|
|
push @chords, $bar; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
foreach my $chord ( @chords ) { |
205
|
|
|
|
|
|
|
my ( @notes ); |
206
|
|
|
|
|
|
|
if ( $chord =~ /\// ) { |
207
|
|
|
|
|
|
|
@notes = split (/\//, $chord ); |
208
|
|
|
|
|
|
|
$pedal_chords = 1; |
209
|
|
|
|
|
|
|
} else { |
210
|
|
|
|
|
|
|
push @notes, $chord; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
foreach my $note ( @notes ) { #- 1コードレベル |
213
|
|
|
|
|
|
|
my ( $minor_Major ); |
214
|
|
|
|
|
|
|
if ( $note =~ /([mM\d]+)$/ ) { |
215
|
|
|
|
|
|
|
$minor_Major = $1; |
216
|
|
|
|
|
|
|
$note =~ s/$minor_Major//; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
my ( $pntr ) = $relative_tone->{$note}; |
219
|
|
|
|
|
|
|
if ( $minor_Major ) { |
220
|
|
|
|
|
|
|
$note = $wholetone->[$pntr] . $minor_Major; |
221
|
|
|
|
|
|
|
} else { |
222
|
|
|
|
|
|
|
$note = $wholetone->[$pntr]; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
if ( $pedal_chords ) { |
226
|
|
|
|
|
|
|
$chord = join ('/', @notes); |
227
|
|
|
|
|
|
|
} else { |
228
|
|
|
|
|
|
|
$chord = $notes[0]; |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
if ( $many_chords ) { |
232
|
|
|
|
|
|
|
$bar = join (' ', @chords); |
233
|
|
|
|
|
|
|
} else { |
234
|
|
|
|
|
|
|
$bar = $chords[0]; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
#- ホールトーンスケールの順序を変える |
241
|
|
|
|
|
|
|
sub arrange_order { |
242
|
|
|
|
|
|
|
my ( $self, $wholetone ) = @_; |
243
|
|
|
|
|
|
|
my ( $neworder ) = []; |
244
|
|
|
|
|
|
|
my ( @tmparray_before, @tmparray ); |
245
|
|
|
|
|
|
|
my ( $done ) = 0; |
246
|
|
|
|
|
|
|
for ( my $i = 0; $i <= $#$wholetone; $i++ ) { |
247
|
|
|
|
|
|
|
if ( $self->{key} eq $wholetone->[$i] ) { |
248
|
|
|
|
|
|
|
$done = 1; |
249
|
|
|
|
|
|
|
push @tmparray, $wholetone->[$i]; |
250
|
|
|
|
|
|
|
} elsif ( $done < 1 ) { |
251
|
|
|
|
|
|
|
push @tmparray_before, $wholetone->[$i]; |
252
|
|
|
|
|
|
|
} else { |
253
|
|
|
|
|
|
|
push @tmparray, $wholetone->[$i]; |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
push @tmparray, @tmparray_before; |
257
|
|
|
|
|
|
|
$neworder = \@tmparray; |
258
|
|
|
|
|
|
|
return $neworder; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
#- テンションをつける |
262
|
|
|
|
|
|
|
sub add_tension { |
263
|
|
|
|
|
|
|
my ( $self ) = shift; |
264
|
|
|
|
|
|
|
my ( $tension_notes ) = { |
265
|
|
|
|
|
|
|
#- 適当 |
266
|
|
|
|
|
|
|
'I' => ['6', '69', 'M7', 'M79'], |
267
|
|
|
|
|
|
|
'#I' => [], |
268
|
|
|
|
|
|
|
'II' => ['7'], |
269
|
|
|
|
|
|
|
'bIII' => ['7'], |
270
|
|
|
|
|
|
|
'III' => ['7'], |
271
|
|
|
|
|
|
|
'IV' => ['M7', 'M79', 'M713'], |
272
|
|
|
|
|
|
|
'#IV'=> [], |
273
|
|
|
|
|
|
|
'V' => ['7', '79', '713'], |
274
|
|
|
|
|
|
|
'bVI'=>['7'], |
275
|
|
|
|
|
|
|
'VI'=>['7'], |
276
|
|
|
|
|
|
|
'bVII' => ['7'], |
277
|
|
|
|
|
|
|
'VII' => [], |
278
|
|
|
|
|
|
|
}; |
279
|
|
|
|
|
|
|
my ( $many_chords ) = 0; |
280
|
|
|
|
|
|
|
my ( $pedal_chords ) = 0; |
281
|
|
|
|
|
|
|
foreach my $bar ( @{$self->{chord_progress}} ) { |
282
|
|
|
|
|
|
|
my @chords; |
283
|
|
|
|
|
|
|
if ( $bar =~ /\s+/ ) { |
284
|
|
|
|
|
|
|
@chords = split (/\s+/, $bar); |
285
|
|
|
|
|
|
|
$many_chords = 1; |
286
|
|
|
|
|
|
|
} else { |
287
|
|
|
|
|
|
|
push @chords, $bar; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
foreach my $chord ( @chords ) { |
290
|
|
|
|
|
|
|
my $pedal_chord; |
291
|
|
|
|
|
|
|
if ( $chord =~ '/' ) { |
292
|
|
|
|
|
|
|
( $chord, $pedal_chord) = split ('/', $chord); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
$chord =~ s/\d+$//g; |
295
|
|
|
|
|
|
|
my ( $minor_Major ); |
296
|
|
|
|
|
|
|
if ( $chord =~ /([mM])$/ ) { |
297
|
|
|
|
|
|
|
$minor_Major = $1; |
298
|
|
|
|
|
|
|
$chord =~ s/$minor_Major//; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
#- def tension |
301
|
|
|
|
|
|
|
my $tension = ''; |
302
|
|
|
|
|
|
|
for ( my $i = ($self->{tension} - 1); $i >= 0; $i-- ) { |
303
|
|
|
|
|
|
|
if ( $tension_notes->{$chord}->[$i] ) { |
304
|
|
|
|
|
|
|
$tension = $tension_notes->{$chord}->[$i]; |
305
|
|
|
|
|
|
|
last; |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
if ( $minor_Major ) { |
309
|
|
|
|
|
|
|
$chord .= $minor_Major . $tension; |
310
|
|
|
|
|
|
|
} else { |
311
|
|
|
|
|
|
|
$chord .= $tension; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
# bug patch :-) |
314
|
|
|
|
|
|
|
$chord =~ s/MM/M/; |
315
|
|
|
|
|
|
|
$chord =~ s/mm/m/; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
if ( $pedal_chord ) { |
318
|
|
|
|
|
|
|
$chord .= '/' . $pedal_chord; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
if ( $many_chords ) { |
322
|
|
|
|
|
|
|
$bar = join (' ', @chords); |
323
|
|
|
|
|
|
|
} else { |
324
|
|
|
|
|
|
|
$bar = $chords[0]; |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
#- ギタータブ譜を書く |
330
|
|
|
|
|
|
|
sub guitar_tab { |
331
|
|
|
|
|
|
|
my $self = shift; |
332
|
|
|
|
|
|
|
my $one_bar_str = 1; |
333
|
|
|
|
|
|
|
my $guitar_str = [qw(e B G D A E)]; |
334
|
|
|
|
|
|
|
my $tab = +{}; |
335
|
|
|
|
|
|
|
my $print_out_block = +{}; #-書き出し用単位 |
336
|
|
|
|
|
|
|
my $beat_tick = +{}; |
337
|
|
|
|
|
|
|
my $tab_blocks = 0; |
338
|
|
|
|
|
|
|
#- 拍子で長さを決める。フォーマトbuild_tab_format; |
339
|
|
|
|
|
|
|
my ( $child, $mother, $one_bar_length, $one_beat_length, $one_row, $one_bar_tick ) = $self->build_tab_format; |
340
|
|
|
|
|
|
|
my $bar_cnt = 0; |
341
|
|
|
|
|
|
|
my $bars_by_one_row = $self->{bars_by_one_row}; |
342
|
|
|
|
|
|
|
#- コード進行に応じた一小節ごとのループ |
343
|
|
|
|
|
|
|
for my $bar ( @{$self->{chord_progress}} ) { |
344
|
|
|
|
|
|
|
#- 一行目のコード進行表示部分 |
345
|
|
|
|
|
|
|
if ( $bar_cnt % $bars_by_one_row == 0 ) { |
346
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= ' '; |
347
|
|
|
|
|
|
|
} else { |
348
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= ' '; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
my ( @chords ); |
351
|
|
|
|
|
|
|
if ( $bar =~ / / ) { |
352
|
|
|
|
|
|
|
@chords = split (/ /, $bar); |
353
|
|
|
|
|
|
|
} else { |
354
|
|
|
|
|
|
|
push @chords, $bar; |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
my ( $chords_in_one_bar ) = $#chords + 1; #-一小節内のコード数 |
357
|
|
|
|
|
|
|
my ( $bytes_for_one_chord ) = int( $one_bar_length / $chords_in_one_bar ); #- 3つあるときは?? #-ひとつのコードごとに持つ拍数 |
358
|
|
|
|
|
|
|
my ( $chord_num ) = 0; |
359
|
|
|
|
|
|
|
for my $chord ( @chords ) { |
360
|
|
|
|
|
|
|
my $format = '%-' . $bytes_for_one_chord . 's'; |
361
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= sprintf($format, $chord); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
if ( $bar_cnt % $bars_by_one_row == ($bars_by_one_row -1) ) { |
364
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= "\n"; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
#- 以上ヘッダづくり |
367
|
|
|
|
|
|
|
my $string_num = 0; |
368
|
|
|
|
|
|
|
#- ギターの弦ごとのループ |
369
|
|
|
|
|
|
|
for my $string ( @{$guitar_str} ) { |
370
|
|
|
|
|
|
|
my $one_tab_row = $one_row; |
371
|
|
|
|
|
|
|
#- コードの内容に応じて、指をおく。 |
372
|
|
|
|
|
|
|
my ( $chord_num ) = 0; |
373
|
|
|
|
|
|
|
for my $chord ( @chords ) { |
374
|
|
|
|
|
|
|
my ( $chords_in_one_bar ) = $#chords + 1; #-一小節内のコード数 |
375
|
|
|
|
|
|
|
my ( $bytes_for_one_chord ) = int( $one_bar_length / $chords_in_one_bar ); #-ひとつのコードごとに持つ拍数 |
376
|
|
|
|
|
|
|
if ( $chord =~ /(\/[A-Z#b]+)/ ) { |
377
|
|
|
|
|
|
|
$chord =~ s/$1//g; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
if ( ( defined $guitar_chords->{standard}->{$chord}->[$string_num] ) && ( $guitar_chords->{standard}->{$chord}->[$string_num] ne '' )) { |
380
|
|
|
|
|
|
|
#- コードが明示されていない場合、相対的に決めるルーチンも欲しい。 |
381
|
|
|
|
|
|
|
my $string_len = length ( $guitar_chords->{standard}->{$chord}->[$string_num] ); |
382
|
|
|
|
|
|
|
#- 置き換え位置をここで決めている。 |
383
|
|
|
|
|
|
|
#- 強拍は一応押さえる。 |
384
|
|
|
|
|
|
|
for ( my $j = 0; $j < $bytes_for_one_chord; $j++ ) { |
385
|
|
|
|
|
|
|
if ( ( $self->{beat} == 2 ) or ( $self->{beat} == 4) ) { |
386
|
|
|
|
|
|
|
#- 拍の頭なら |
387
|
|
|
|
|
|
|
if ( $j % $one_beat_length == 0 ) { |
388
|
|
|
|
|
|
|
my $offset = $self->return_offset($self->{beat}, $bytes_for_one_chord, $chord_num, $j); |
389
|
|
|
|
|
|
|
#- 弦を押さえる。 |
390
|
|
|
|
|
|
|
substr($one_tab_row, $offset, $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} elsif ( $self->{beat} == 8 ) { |
393
|
|
|
|
|
|
|
#- 強拍 |
394
|
|
|
|
|
|
|
if ( ( $bytes_for_one_chord >= ( $one_beat_length * 4 ) ) && ( $j % ( $one_beat_length * 4 ) == 0 )) { |
395
|
|
|
|
|
|
|
#- 1コードが2分音符以上続く場合 |
396
|
|
|
|
|
|
|
my $offset = $self->return_offset($self->{beat}, $bytes_for_one_chord, $chord_num, $j); |
397
|
|
|
|
|
|
|
#- 強拍 |
398
|
|
|
|
|
|
|
substr($one_tab_row, $offset, $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
399
|
|
|
|
|
|
|
substr($one_tab_row, ($offset + 16) , $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
400
|
|
|
|
|
|
|
#- 弱拍の考慮 mute beat |
401
|
|
|
|
|
|
|
substr($one_tab_row, ($offset + 12), $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
402
|
|
|
|
|
|
|
substr($one_tab_row, ($offset + 28), $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
403
|
|
|
|
|
|
|
} elsif ( ( $bytes_for_one_chord = $one_beat_length ) && ( $j % $one_beat_length == 0 )) { |
404
|
|
|
|
|
|
|
#- 1コード一つの四分音符の場合 |
405
|
|
|
|
|
|
|
my $offset = $self->return_offset($self->{beat}, $bytes_for_one_chord, $chord_num, $j); |
406
|
|
|
|
|
|
|
substr($one_tab_row, $offset, $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
407
|
|
|
|
|
|
|
#- 弱拍 |
408
|
|
|
|
|
|
|
substr($one_tab_row, ($offset + 4), $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
} elsif ( $self->{beat} == 16) { |
412
|
|
|
|
|
|
|
if ( $string_num >= 3 ) { |
413
|
|
|
|
|
|
|
#- 16ビートの場合、第四弦以下は弾かない。 |
414
|
|
|
|
|
|
|
next; |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
#- 強拍 |
417
|
|
|
|
|
|
|
if ( $j % ( $one_beat_length * 4 ) == 0 ) { |
418
|
|
|
|
|
|
|
#- あくまでもサンプルカッティング(センスよくしたいw |
419
|
|
|
|
|
|
|
my $offset = $self->return_offset($self->{beat}, $bytes_for_one_chord, $chord_num, $j); |
420
|
|
|
|
|
|
|
substr($one_tab_row, $offset, $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
421
|
|
|
|
|
|
|
substr($one_tab_row, ( $offset + 2) , $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
422
|
|
|
|
|
|
|
substr($one_tab_row, ( $offset + 4) , $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
423
|
|
|
|
|
|
|
if ( $bytes_for_one_chord >= ( $one_beat_length * 4 ) ) { |
424
|
|
|
|
|
|
|
substr($one_tab_row, ( $offset + 8 ) , $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
425
|
|
|
|
|
|
|
substr($one_tab_row, ( $offset + 10 ) , $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
426
|
|
|
|
|
|
|
substr($one_tab_row, ( $offset + 14 ) , $string_len, $guitar_chords->{standard}->{$chord}->[$string_num]); |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
$chord_num++; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
if ( $bar_cnt % $bars_by_one_row == 0 ) { |
437
|
|
|
|
|
|
|
$tab->{$bar_cnt}->{$string} = "$string:$one_tab_row|"; #- 譜面を書く |
438
|
|
|
|
|
|
|
} else { |
439
|
|
|
|
|
|
|
$tab->{$bar_cnt}->{$string} = "$one_tab_row|"; #- 譜面を書く |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
#- 最後に来て、かつ2ブロック目ならまとめて書きだしハッシュを作る |
442
|
|
|
|
|
|
|
if (( $bar_cnt % $bars_by_one_row == ( $bars_by_one_row - 1)) && ( $#$guitar_str == $string_num )) { |
443
|
|
|
|
|
|
|
#- 一拍ごとの区切りをつける |
444
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= ' '; |
445
|
|
|
|
|
|
|
for ( my $i = 0; $i < $bars_by_one_row; $i++ ) { |
446
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= ' '. $one_bar_tick; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= "\n"; |
449
|
|
|
|
|
|
|
#- 各弦ごとのタブを連結 |
450
|
|
|
|
|
|
|
for my $Str ( @{$guitar_str} ) { |
451
|
|
|
|
|
|
|
for my $i ( sort {$a<=>$b} keys %$tab ) { |
452
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= $tab->{$i}->{$Str}; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
$print_out_block->{$tab_blocks} .= "\n"; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
$tab_blocks++; |
457
|
|
|
|
|
|
|
$tab = undef; |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
$string_num++; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
$bar_cnt++; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
#- 出力する |
464
|
|
|
|
|
|
|
for my $cnt ( sort {$a<=>$b} keys %$print_out_block ) { |
465
|
|
|
|
|
|
|
print $print_out_block->{$cnt}; |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
#-弦上のオフセット戻し |
470
|
|
|
|
|
|
|
sub return_offset { |
471
|
|
|
|
|
|
|
my ( $self, $beat, $bytes_for_one_chord, $chord_num, $j) = @_; |
472
|
|
|
|
|
|
|
if ( $beat !~ /^\d+$/ ) { |
473
|
|
|
|
|
|
|
die "beat must be number: $beat"; |
474
|
|
|
|
|
|
|
} |
475
|
|
|
|
|
|
|
if ( ( $beat == 2 ) || ( $beat == 4) || ( $beat == 16 )) { |
476
|
|
|
|
|
|
|
return ( 1 + ($bytes_for_one_chord * $chord_num ) + $j ); |
477
|
|
|
|
|
|
|
} elsif ( $beat == 8) { |
478
|
|
|
|
|
|
|
return ( 1 + ($bytes_for_one_chord * $chord_num * 2 ) + $j ); |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
#- 一小節のフォーマットづくり |
485
|
|
|
|
|
|
|
sub build_tab_format { |
486
|
|
|
|
|
|
|
my $self = shift; |
487
|
|
|
|
|
|
|
my ( $one_bar_length, $one_beat_length, $one_row, $one_bar_tick); |
488
|
|
|
|
|
|
|
my ( $child, $mother ) = split ('/', $self->{time} ); |
489
|
|
|
|
|
|
|
if ( ( $mother == 4 ) || ( $mother == 2) ) { |
490
|
|
|
|
|
|
|
$one_bar_length = $mother * $child ; |
491
|
|
|
|
|
|
|
} elsif ( ( $mother == 8 ) || ( $mother == 16 ) ) { |
492
|
|
|
|
|
|
|
$one_bar_length = ( $mother * $child ) / 2 ; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
$one_beat_length = $one_bar_length / $child; |
495
|
|
|
|
|
|
|
for ( my $i = 0; $i < $one_bar_length; $i++ ) { |
496
|
|
|
|
|
|
|
$one_row .= '-'; |
497
|
|
|
|
|
|
|
if ( ( $self->{beat} =~ /^(2|4)$/ ) && ( $i % $one_beat_length == 0 ) ) { |
498
|
|
|
|
|
|
|
$one_bar_tick .= '+'; |
499
|
|
|
|
|
|
|
} elsif ( ( $self->{beat} =~ /^(8|16)$/) && ( $i % 8 == 0 ) ) { |
500
|
|
|
|
|
|
|
$one_bar_tick .= '+'; |
501
|
|
|
|
|
|
|
} elsif ( ( $self->{beat} =~ /^(8|16)$/ ) && ( $i % 4 == 0 ) ) { |
502
|
|
|
|
|
|
|
$one_bar_tick .= '-'; |
503
|
|
|
|
|
|
|
} else { |
504
|
|
|
|
|
|
|
$one_bar_tick .= ' '; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
$one_row .= '-'; #-見やすくするため一つ足す |
508
|
|
|
|
|
|
|
$one_bar_tick = ' ' . $one_bar_tick; |
509
|
|
|
|
|
|
|
return ( $child, $mother, $one_bar_length, $one_beat_length, $one_row, $one_bar_tick); |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
1; |
513
|
|
|
|
|
|
|
__END__ |