wtf_chord 0.2.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +10 -0
- data/README.md +12 -12
- data/lib/wtf_chord/chord.rb +15 -42
- data/lib/wtf_chord/cli.rb +6 -5
- data/lib/wtf_chord/complexity_counter.rb +16 -6
- data/lib/wtf_chord/fingering.rb +22 -2
- data/lib/wtf_chord/fingerings_generator.rb +120 -0
- data/lib/wtf_chord/formatter.rb +7 -5
- data/lib/wtf_chord/formatters/base.rb +4 -0
- data/lib/wtf_chord/formatters/default.rb +28 -27
- data/lib/wtf_chord/formatters/piano.rb +25 -0
- data/lib/wtf_chord/fretboard.rb +8 -0
- data/lib/wtf_chord/guitar_string.rb +7 -0
- data/lib/wtf_chord/helpers/roman_numbers_helper.rb +11 -0
- data/lib/wtf_chord/keyboard.rb +48 -0
- data/lib/wtf_chord/note.rb +3 -2
- data/lib/wtf_chord/pitch.rb +4 -0
- data/lib/wtf_chord/rules.rb +3 -1
- data/lib/wtf_chord/scale.rb +7 -1
- data/lib/wtf_chord/version.rb +1 -1
- data/wtf_chord.gemspec +2 -2
- metadata +12 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6b17613170273223d591f45e88733cf89449d8b872088496c7c569a7e053f4e0
|
4
|
+
data.tar.gz: 10460d448a476b45fd1ea0c96b6b418458f7c123b83f9bc8f8d45593e6fd1b36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37dc9a3b6f74eeb4615f8ff897982c0a4dff8577316415ce50cd9278968298d2c9a18d24ca885fde1f599c919b6547884983ab38e68f5ae49e33f6671436e8a9
|
7
|
+
data.tar.gz: 8d326d7047187a13e778af13928a3e5ac847e56c57109817feab09b17d00a29c2d56a33c9d98d23946ccde52fa5cc65e158f14ee71e59470bdb57ec319514ccb
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -31,13 +31,13 @@ Or install it yourself as:
|
|
31
31
|
|
32
32
|
Finds fingerings of the requested chord.
|
33
33
|
|
34
|
-
v0.
|
34
|
+
v0.3.1
|
35
35
|
|
36
36
|
Options:
|
37
37
|
-h, --help Show command line help
|
38
38
|
--version Show help/version info
|
39
39
|
-n, --amount N Amount of fingering variants to output.
|
40
|
-
(default:
|
40
|
+
(default: 5)
|
41
41
|
-o, --output FORMAT Output format.
|
42
42
|
(default: default)
|
43
43
|
-R, --[no-]rates Output fingering complexity rates
|
@@ -57,25 +57,25 @@ For example, to print two fingering variants of the `Dm` chord, just run:
|
|
57
57
|
And you'll get the visual presentation of chords' fingerings:
|
58
58
|
|
59
59
|
[ × × 0 2 3 1 ]
|
60
|
-
|
61
|
-
| | | | |
|
62
|
-
| | |
|
63
|
-
| | | |
|
60
|
+
——————————————————
|
61
|
+
| | | | | ◉
|
62
|
+
| | | ◉ | |
|
63
|
+
| | | | ◉ |
|
64
64
|
| | | | | |
|
65
65
|
| | | | | |
|
66
66
|
| | | | | |
|
67
|
-
|
67
|
+
——————————————————
|
68
68
|
D A D F
|
69
69
|
|
70
70
|
[ × 5 7 7 6 5 ]
|
71
|
-
|
72
|
-
|
|
73
|
-
| | | |
|
74
|
-
| |
|
71
|
+
——————————————————
|
72
|
+
| ◉ | | | ◉ ← V
|
73
|
+
| | | | ◉ |
|
74
|
+
| | ◉ ◉ | |
|
75
75
|
| | | | | |
|
76
76
|
| | | | | |
|
77
77
|
| | | | | |
|
78
|
-
|
78
|
+
——————————————————
|
79
79
|
D A D F A
|
80
80
|
|
81
81
|
You can get simpler output, using the `--output` option:
|
data/lib/wtf_chord/chord.rb
CHANGED
@@ -1,68 +1,41 @@
|
|
1
1
|
require 'wtf_chord/fingering'
|
2
|
+
require 'wtf_chord/fingerings_generator'
|
2
3
|
|
3
4
|
module WTFChord
|
4
5
|
class Chord
|
5
|
-
|
6
|
-
MAX_FRET = 7
|
6
|
+
BASS_MATCH = /(?<=[\\\/])[A-H][b#]?(?=$)/
|
7
7
|
|
8
|
-
attr_reader :pitch, :steps, :notes, :name
|
8
|
+
attr_reader :pitch, :steps, :notes, :name, :bass
|
9
9
|
|
10
10
|
def initialize(note, name)
|
11
11
|
@pitch = note.is_a?(Pitch) ? note : WTFChord.note(note)
|
12
12
|
@name = "#{@pitch.key}#{name}".freeze
|
13
13
|
@steps = Array(WTFChord.rules[name])
|
14
14
|
@notes = @steps.map { |dist| (@pitch + dist).note }.tap(&:uniq!)
|
15
|
-
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
def fingerings(limit = 3)
|
22
|
-
list = []
|
23
|
-
|
24
|
-
(0..MAX_FRET).each do |from_fret|
|
25
|
-
f = get_fingering(from_fret)
|
26
|
-
next if list.include?(f)
|
27
|
-
list << f if all_notes?(f.used_strings)
|
16
|
+
BASS_MATCH.match(@name) do |m|
|
17
|
+
@bass = WTFChord.note(m[0]).note
|
28
18
|
end
|
29
|
-
|
30
|
-
sort_fingerings(list, limit)
|
31
19
|
end
|
32
20
|
|
33
|
-
def
|
34
|
-
|
35
|
-
Fingering.new(GUITAR) do |f|
|
36
|
-
f.strings.each do |s|
|
37
|
-
fret = (from_fret..to_fret).detect { |dist| @notes.include?((s.original + dist).note) }
|
38
|
-
fret ? s.hold_on(fret) : s.dead
|
39
|
-
end
|
40
|
-
|
41
|
-
adjust_fingering(f.used_strings[0], to_fret, 0) while should_adjust?(f.used_strings[0], 0)
|
42
|
-
end
|
21
|
+
def fingerings(limit = nil)
|
22
|
+
FingeringsGenerator.new(self).call[0, limit || 5]
|
43
23
|
end
|
44
24
|
|
45
|
-
def
|
46
|
-
|
47
|
-
@notes.all? { |n| snotes.include?(n) }
|
25
|
+
def third_tone
|
26
|
+
@third_tone ||= @notes[@steps.size > 3 ? 2 : -1]
|
48
27
|
end
|
49
28
|
|
50
|
-
def
|
51
|
-
|
52
|
-
string.hold_on(string.fret + 1)
|
53
|
-
break if @notes.include?(string.note)
|
54
|
-
end
|
55
|
-
|
56
|
-
string.dead if !string.dead? && string.note != @notes[i]
|
29
|
+
def bass?
|
30
|
+
!!@bass
|
57
31
|
end
|
58
32
|
|
59
|
-
def
|
60
|
-
|
33
|
+
def original_bass
|
34
|
+
@notes[0]
|
61
35
|
end
|
62
36
|
|
63
|
-
def
|
64
|
-
|
65
|
-
list[0...limit].sort_by!(&:min_fret)
|
37
|
+
def inspect
|
38
|
+
"#{name} (#{@notes.map(&:key) * ' - '})"
|
66
39
|
end
|
67
40
|
end
|
68
41
|
end
|
data/lib/wtf_chord/cli.rb
CHANGED
@@ -7,14 +7,15 @@ module WTFChord
|
|
7
7
|
include Methadone::CLILogging
|
8
8
|
|
9
9
|
main do |name|
|
10
|
-
chord
|
11
|
-
|
12
|
-
formatter = WTFChord::Formatter.new(options['output'], options['rates'])
|
10
|
+
chord = WTFChord.chord(name)
|
11
|
+
formatter = WTFChord::Formatter.new(options['output'], options['rates'])
|
13
12
|
|
14
13
|
debug { "Output using formatter: #{formatter.formatter.to_s}\n" }
|
15
14
|
|
15
|
+
options['amount'] = 1 if options['output'] == 'piano'
|
16
|
+
|
16
17
|
puts chord.inspect, nil
|
17
|
-
formatter.(*
|
18
|
+
puts formatter[*chord.fingerings(options['amount'])] * formatter.separator
|
18
19
|
end
|
19
20
|
|
20
21
|
version VERSION
|
@@ -31,7 +32,7 @@ module WTFChord
|
|
31
32
|
##
|
32
33
|
# Options
|
33
34
|
#
|
34
|
-
options['amount'] =
|
35
|
+
options['amount'] = 5
|
35
36
|
options['output'] = 'default'
|
36
37
|
options['rates'] = false
|
37
38
|
|
@@ -6,19 +6,29 @@ module WTFChord
|
|
6
6
|
|
7
7
|
def rate
|
8
8
|
frets = @fingering.holded_strings.map(&:fret)
|
9
|
-
barre = frets.uniq.keep_if { |o| frets.count(o) >= 2 }
|
10
|
-
barre_strings = barre ? frets.count(barre).pred : 0
|
9
|
+
barre = frets.uniq.keep_if { |o| frets.count(o) >= 2 }
|
10
|
+
barre_strings = barre.min ? frets.count(barre.min).pred : 0
|
11
11
|
|
12
12
|
base_rate = begin
|
13
|
-
Rational(holded_strings - barre_strings,
|
14
|
-
Rational(finger_dist(
|
13
|
+
Rational(holded_strings - barre_strings, 4).to_f +
|
14
|
+
Rational(finger_dist(@fingering.used_strings.map(&:fret)), 4).to_f
|
15
15
|
end
|
16
16
|
|
17
|
+
base_rate += 0.5 if (holded_strings - barre_strings) > 4
|
18
|
+
|
17
19
|
# p [@fingering, holded_strings, barre_strings]
|
18
20
|
|
19
|
-
|
21
|
+
if holded_strings > 4
|
22
|
+
uniq_frets = frets.uniq.size
|
23
|
+
|
24
|
+
if barre.size > 1 && uniq_frets == 3 && frets.count(barre.max) > barre_strings.next
|
25
|
+
base_rate += 1 if complex_barre?(barre.max, frets)
|
26
|
+
elsif barre_strings.next >= 2 || uniq_frets.size > 3
|
27
|
+
base_rate += 1 if complex_barre?(barre.min, frets)
|
28
|
+
end
|
29
|
+
end
|
20
30
|
|
21
|
-
base_rate
|
31
|
+
base_rate + @fingering.extra_complexity
|
22
32
|
end
|
23
33
|
|
24
34
|
private
|
data/lib/wtf_chord/fingering.rb
CHANGED
@@ -4,14 +4,18 @@ require 'wtf_chord/formatter'
|
|
4
4
|
|
5
5
|
module WTFChord
|
6
6
|
class Fingering < Fretboard
|
7
|
-
|
7
|
+
attr_accessor :extra_complexity
|
8
|
+
|
9
|
+
def initialize(guitar, fingers = nil)
|
8
10
|
@capo = guitar.capo
|
9
11
|
@strings = guitar.strings.map(&:dup)
|
12
|
+
@extra_complexity = 0.0
|
13
|
+
set_fingers(fingers) if fingers.is_a?(Array)
|
10
14
|
yield(self) if block_given?
|
11
15
|
end
|
12
16
|
|
13
17
|
def code
|
14
|
-
|
18
|
+
strings.map(&:code).pack("c*")
|
15
19
|
end
|
16
20
|
|
17
21
|
def == other
|
@@ -22,6 +26,14 @@ module WTFChord
|
|
22
26
|
end
|
23
27
|
end
|
24
28
|
|
29
|
+
def eql?(other)
|
30
|
+
super || self == other
|
31
|
+
end
|
32
|
+
|
33
|
+
def hash
|
34
|
+
strings.map(&:code).hash
|
35
|
+
end
|
36
|
+
|
25
37
|
def complexity
|
26
38
|
complexity_counter.rate
|
27
39
|
end
|
@@ -42,6 +54,14 @@ module WTFChord
|
|
42
54
|
@strings.reject(&:dead?)
|
43
55
|
end
|
44
56
|
|
57
|
+
def find_used_string_for(note)
|
58
|
+
used = used_strings
|
59
|
+
if idx = used.index(note)
|
60
|
+
string = used[idx]
|
61
|
+
yield(strings.index(string), string)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
45
65
|
def min_fret
|
46
66
|
holded_strings.min.fret
|
47
67
|
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module WTFChord
|
2
|
+
class Chord
|
3
|
+
end
|
4
|
+
|
5
|
+
class FingeringsGenerator < DelegateClass(Chord)
|
6
|
+
MAX_DIST = 5
|
7
|
+
MAX_FRET = 7
|
8
|
+
|
9
|
+
def fingerings
|
10
|
+
@fingerings ||= []
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
fingerings.clear
|
15
|
+
|
16
|
+
(0...MAX_FRET).each do |from|
|
17
|
+
to = from + MAX_DIST
|
18
|
+
generate(from...to) do |variant|
|
19
|
+
fingerings << set_bass(variant) if filter_variant(variant)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
fingerings.uniq!
|
24
|
+
fingerings.sort_by!(&:complexity)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def generate(fret_range)
|
30
|
+
combinations = WTFChord.guitar.strings.map.with_index do |s, index|
|
31
|
+
fret_range.
|
32
|
+
select { |dist| pitch = s.original + dist; has_note?(pitch) }.
|
33
|
+
tap { |frets| frets << nil if frets.size == 0 }
|
34
|
+
end
|
35
|
+
|
36
|
+
combinations.inject(&:product).each do |fingers|
|
37
|
+
fingers.flatten!
|
38
|
+
|
39
|
+
Fingering.new(WTFChord.guitar, fingers) do |variant|
|
40
|
+
adjust(variant)
|
41
|
+
yield(variant)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def filter_variant(variant)
|
47
|
+
used_strings = variant.used_strings
|
48
|
+
used_notes = used_strings.map(&:note)
|
49
|
+
tones_count = notes.map { |n| used_notes.count(n) }
|
50
|
+
|
51
|
+
!fingerings.include?(variant) &&
|
52
|
+
basetone?(used_strings[0]) &&
|
53
|
+
(
|
54
|
+
(third?(used_strings[1]) || third?(used_strings[2])) ||
|
55
|
+
(last?(used_strings[1]) || last?(used_strings[2]))
|
56
|
+
) &&
|
57
|
+
all_notes?(used_notes) &&
|
58
|
+
used_notes.each_cons(2).none? { |(l, r)| l == r } &&
|
59
|
+
tones_count.all? { |n| tones_count[0] >= n || tones_count[notes.index(third_tone)] >= n } &&
|
60
|
+
variant.complexity <= 2.25
|
61
|
+
end
|
62
|
+
|
63
|
+
def adjust(fingering)
|
64
|
+
while (string = fingering.used_strings[0]) && !basetone?(string)
|
65
|
+
string.dead if !string.dead?
|
66
|
+
break if fingering.used_strings.size == 4
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_bass(variant)
|
71
|
+
if bass?
|
72
|
+
variant.find_used_string_for(original_bass) do |idx, bass_string|
|
73
|
+
try_set_bass_on(variant, idx) or begin
|
74
|
+
4.times do |i|
|
75
|
+
next if i == idx
|
76
|
+
if try_set_bass_on(variant, i)
|
77
|
+
bass_string.dead
|
78
|
+
break
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
variant
|
86
|
+
end
|
87
|
+
|
88
|
+
def try_set_bass_on(variant, idx)
|
89
|
+
bass_string = variant.strings[idx].dup
|
90
|
+
distance = bass_string.distance_to(bass)
|
91
|
+
|
92
|
+
if distance >= 0 && distance < 5 && (distance - variant.min_fret) > -3
|
93
|
+
variant.extra_complexity += 0.5 if distance < variant.min_fret
|
94
|
+
variant.strings[idx].hold_on(distance)
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def all_notes?(used_notes)
|
100
|
+
notes.all? { |n| used_notes.include?(n) }
|
101
|
+
end
|
102
|
+
|
103
|
+
def has_note?(value)
|
104
|
+
value = value.note if !value.is_a?(Note) && value.respond_to?(:note)
|
105
|
+
notes.include?(value)
|
106
|
+
end
|
107
|
+
|
108
|
+
def basetone?(string)
|
109
|
+
string && string.note == notes[0]
|
110
|
+
end
|
111
|
+
|
112
|
+
def third?(string)
|
113
|
+
string && (string.note == third_tone)
|
114
|
+
end
|
115
|
+
|
116
|
+
def last?(string)
|
117
|
+
string && (string.note == notes[-1])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/wtf_chord/formatter.rb
CHANGED
@@ -9,19 +9,21 @@ module WTFChord
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def call(*fingerings)
|
12
|
-
formatter.with_rates(@with_rates)
|
13
|
-
puts (fingerings.map(&f) * f.separator)
|
14
|
-
end
|
12
|
+
formatter.with_rates(@with_rates) { |f| fingerings.map(&f) }
|
15
13
|
end
|
16
14
|
|
17
15
|
alias :[] :call
|
18
16
|
|
17
|
+
def separator
|
18
|
+
formatter.separator
|
19
|
+
end
|
20
|
+
|
19
21
|
def formatter
|
20
|
-
|
22
|
+
Formatters.const_get(formatter_name)
|
21
23
|
end
|
22
24
|
|
23
25
|
def formatter?
|
24
|
-
|
26
|
+
Formatters.const_defined?(formatter_name)
|
25
27
|
end
|
26
28
|
|
27
29
|
def formatter_name
|
@@ -1,25 +1,28 @@
|
|
1
|
+
require 'wtf_chord/helpers/roman_numbers_helper'
|
2
|
+
|
1
3
|
module WTFChord
|
2
4
|
module Formatters
|
3
5
|
class Default < Base
|
4
6
|
OPEN = "|".freeze
|
5
|
-
HORIZ = "
|
7
|
+
HORIZ = "—".freeze
|
6
8
|
SPACE = " ".freeze
|
7
|
-
BULL = "\
|
8
|
-
LATIN = %w(0 I II III IV V VI VII VIII IX X XII XIII).freeze
|
9
|
+
BULL = "\u25C9".freeze
|
9
10
|
NEWLINE = "\n".freeze
|
11
|
+
COMPLEXITY_FORMAT = " (complexity: %.2f)".freeze
|
12
|
+
EMPTY_STRING = Array.new(FRETS, OPEN).freeze
|
10
13
|
|
11
|
-
|
12
|
-
"\n\n".freeze
|
13
|
-
end
|
14
|
+
include RomanNumbersHelper
|
14
15
|
|
15
16
|
def draw
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
"
|
22
|
-
|
17
|
+
<<-EOF.gsub(/^\s+\|/, '')
|
18
|
+
|[ #{head} ] #{capo}
|
19
|
+
| #{border}
|
20
|
+
#{fret_rows.map.with_index { |row, index|
|
21
|
+
" | #{row} #{roman_min_fret if index == 0}"
|
22
|
+
}.join("\n")}
|
23
|
+
| #{border}
|
24
|
+
| #{string_keys}#{rate}
|
25
|
+
EOF
|
23
26
|
end
|
24
27
|
|
25
28
|
private
|
@@ -29,11 +32,15 @@ module WTFChord
|
|
29
32
|
end
|
30
33
|
|
31
34
|
def capo
|
32
|
-
"
|
35
|
+
"(capo #{romanize(@fret.capo)})" if @fret.capo > 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def roman_min_fret
|
39
|
+
" ← #{romanize(min_fret)}" if min_fret > 1
|
33
40
|
end
|
34
41
|
|
35
42
|
def rate
|
36
|
-
|
43
|
+
COMPLEXITY_FORMAT % @fret.complexity if with_rates?
|
37
44
|
end
|
38
45
|
|
39
46
|
def border
|
@@ -41,27 +48,21 @@ module WTFChord
|
|
41
48
|
end
|
42
49
|
|
43
50
|
def string_rows
|
44
|
-
strings.map { |string| draw_string(string
|
51
|
+
strings.map { |string| draw_string(string) }
|
45
52
|
end
|
46
53
|
|
47
54
|
def fret_rows
|
48
|
-
|
49
|
-
|
50
|
-
end
|
55
|
+
return to_enum(__method__) unless block_given?
|
56
|
+
string_rows.transpose.each { |row| yield(row * SPACE) }
|
51
57
|
end
|
52
58
|
|
53
59
|
def string_keys
|
54
60
|
strings.map { |s| s.dead? ? SPACE : "%-2s" % s.key } * " "
|
55
61
|
end
|
56
62
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
def draw_string(fret)
|
62
|
-
Array.new(FRETS, OPEN).tap do |rows|
|
63
|
-
rows[(fret - min_fret.pred).pred] = BULL if fret.to_i > 0
|
64
|
-
end
|
63
|
+
def draw_string(string)
|
64
|
+
EMPTY_STRING.dup.
|
65
|
+
tap { |rows| rows[string.fret - min_fret] = BULL if string.holded? }
|
65
66
|
end
|
66
67
|
end
|
67
68
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "wtf_chord/keyboard"
|
4
|
+
|
5
|
+
module WTFChord
|
6
|
+
module Formatters
|
7
|
+
class Piano < Base
|
8
|
+
def draw
|
9
|
+
[
|
10
|
+
unique_notes.sort.join(" - "),
|
11
|
+
Keyboard.press(*unique_notes.map(&:position))
|
12
|
+
].join("\n\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
def unique_notes
|
16
|
+
strings.reject(&:dead?).each_with_object([[], []]) do |string, (positions, notes)|
|
17
|
+
unless positions.include?(string.note.position)
|
18
|
+
positions << string.note.position
|
19
|
+
notes << string.note
|
20
|
+
end
|
21
|
+
end[1]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/wtf_chord/fretboard.rb
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WTFChord
|
4
|
+
class Keyboard
|
5
|
+
FRAME = <<~EOF
|
6
|
+
┌─┬─┬┬─┬─┬─┬─┬┬─┬┬─┬─┐
|
7
|
+
│ │ ││ │ │ │ ││ ││ │ │
|
8
|
+
│ └┬┘└┬┘ │ └┬┘└┬┘└┬┘ │
|
9
|
+
│ │ │ │ │ │ │ │
|
10
|
+
└──┴──┴──┴──┴──┴──┴──┘
|
11
|
+
EOF
|
12
|
+
|
13
|
+
Key = Struct.new(:offset)
|
14
|
+
class Key
|
15
|
+
singleton_class.attr_accessor :select_symbol
|
16
|
+
singleton_class.alias_method :[], :new
|
17
|
+
|
18
|
+
def select(frame)
|
19
|
+
frame[offset] = self.class.select_symbol
|
20
|
+
frame
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
W = Class.new(Key) { self.select_symbol = "▐▌" }
|
25
|
+
B = Class.new(Key) { self.select_symbol = "█" }
|
26
|
+
|
27
|
+
KEYS = {
|
28
|
+
1 => W[70...72],
|
29
|
+
2 => B[26...27],
|
30
|
+
3 => W[73...75],
|
31
|
+
4 => B[29...30],
|
32
|
+
5 => W[76...78],
|
33
|
+
6 => W[79...81],
|
34
|
+
7 => B[35...36],
|
35
|
+
8 => W[82...84],
|
36
|
+
9 => B[38...39],
|
37
|
+
10 => W[85...87],
|
38
|
+
11 => B[41...42],
|
39
|
+
12 => W[88...90]
|
40
|
+
}
|
41
|
+
|
42
|
+
def self.press(*positions)
|
43
|
+
positions.each_with_object(FRAME.dup) do |pos, frame|
|
44
|
+
KEYS.fetch(pos).select(frame)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/wtf_chord/note.rb
CHANGED
@@ -25,8 +25,9 @@ module WTFChord
|
|
25
25
|
|
26
26
|
def == other
|
27
27
|
case other
|
28
|
-
when String
|
29
|
-
when Integer
|
28
|
+
when String then other.casecmp(@key).zero? || aliases.any? { |a| other.casecmp(a.key).zero? }
|
29
|
+
when Integer then other == @position
|
30
|
+
when Note then other.position == @position
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
data/lib/wtf_chord/pitch.rb
CHANGED
data/lib/wtf_chord/rules.rb
CHANGED
@@ -23,7 +23,9 @@ module WTFChord
|
|
23
23
|
name.match(pattern) do |m|
|
24
24
|
base = chords[m[:name]] || chords["M"]
|
25
25
|
steps.concat(base)
|
26
|
-
|
26
|
+
|
27
|
+
ext = extra[m[:ext]] if m[:ext]
|
28
|
+
steps.concat(ext) if ext
|
27
29
|
end
|
28
30
|
|
29
31
|
steps.tap(&:uniq!)
|
data/lib/wtf_chord/scale.rb
CHANGED
@@ -63,5 +63,11 @@ module WTFChord
|
|
63
63
|
ScaleArray.build(*chromatic_scale).freeze
|
64
64
|
end
|
65
65
|
|
66
|
-
|
66
|
+
def self.guitar=(guitar)
|
67
|
+
Thread.current[:wtf_guitar] = guitar
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.guitar
|
71
|
+
Thread.current[:wtf_guitar] ||= Fretboard.new(*%w(E2 A2 D3 G3 B3 E4))
|
72
|
+
end
|
67
73
|
end
|
data/lib/wtf_chord/version.rb
CHANGED
data/wtf_chord.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_runtime_dependency "methadone"
|
24
24
|
|
25
|
-
spec.add_development_dependency "bundler"
|
26
|
-
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "bundler"
|
26
|
+
spec.add_development_dependency "rake"
|
27
27
|
spec.add_development_dependency "rspec", ">= 3.0", "< 4"
|
28
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wtf_chord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: methadone
|
@@ -30,40 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
- - "<"
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: '2'
|
33
|
+
version: '0'
|
37
34
|
type: :development
|
38
35
|
prerelease: false
|
39
36
|
version_requirements: !ruby/object:Gem::Requirement
|
40
37
|
requirements:
|
41
38
|
- - ">="
|
42
39
|
- !ruby/object:Gem::Version
|
43
|
-
version: '
|
44
|
-
- - "<"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '2'
|
40
|
+
version: '0'
|
47
41
|
- !ruby/object:Gem::Dependency
|
48
42
|
name: rake
|
49
43
|
requirement: !ruby/object:Gem::Requirement
|
50
44
|
requirements:
|
51
45
|
- - ">="
|
52
46
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
54
|
-
- - "<"
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '12'
|
47
|
+
version: '0'
|
57
48
|
type: :development
|
58
49
|
prerelease: false
|
59
50
|
version_requirements: !ruby/object:Gem::Requirement
|
60
51
|
requirements:
|
61
52
|
- - ">="
|
62
53
|
- !ruby/object:Gem::Version
|
63
|
-
version: '
|
64
|
-
- - "<"
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: '12'
|
54
|
+
version: '0'
|
67
55
|
- !ruby/object:Gem::Dependency
|
68
56
|
name: rspec
|
69
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,6 +83,7 @@ files:
|
|
95
83
|
- ".gitignore"
|
96
84
|
- ".rspec"
|
97
85
|
- ".travis.yml"
|
86
|
+
- CHANGELOG.md
|
98
87
|
- Gemfile
|
99
88
|
- README.md
|
100
89
|
- Rakefile
|
@@ -107,12 +96,16 @@ files:
|
|
107
96
|
- lib/wtf_chord/cli.rb
|
108
97
|
- lib/wtf_chord/complexity_counter.rb
|
109
98
|
- lib/wtf_chord/fingering.rb
|
99
|
+
- lib/wtf_chord/fingerings_generator.rb
|
110
100
|
- lib/wtf_chord/formatter.rb
|
111
101
|
- lib/wtf_chord/formatters/base.rb
|
112
102
|
- lib/wtf_chord/formatters/default.rb
|
103
|
+
- lib/wtf_chord/formatters/piano.rb
|
113
104
|
- lib/wtf_chord/formatters/simple.rb
|
114
105
|
- lib/wtf_chord/fretboard.rb
|
115
106
|
- lib/wtf_chord/guitar_string.rb
|
107
|
+
- lib/wtf_chord/helpers/roman_numbers_helper.rb
|
108
|
+
- lib/wtf_chord/keyboard.rb
|
116
109
|
- lib/wtf_chord/note.rb
|
117
110
|
- lib/wtf_chord/pitch.rb
|
118
111
|
- lib/wtf_chord/rules.rb
|
@@ -137,10 +130,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
130
|
- !ruby/object:Gem::Version
|
138
131
|
version: '0'
|
139
132
|
requirements: []
|
140
|
-
|
141
|
-
rubygems_version: 2.5.1
|
133
|
+
rubygems_version: 3.0.3
|
142
134
|
signing_key:
|
143
135
|
specification_version: 4
|
144
136
|
summary: "‘WTF Chord?’ is the Ruby guitar chords generator library."
|
145
137
|
test_files: []
|
146
|
-
has_rdoc:
|