wtf_chord 0.2.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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:
|