wtf_chord 0.1.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 +7 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +18 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/exe/wtfchord +8 -0
- data/lib/wtf_chord/chord.rb +56 -0
- data/lib/wtf_chord/drawer.rb +79 -0
- data/lib/wtf_chord/fingerboard.rb +102 -0
- data/lib/wtf_chord/guitar_string.rb +57 -0
- data/lib/wtf_chord/note.rb +58 -0
- data/lib/wtf_chord/pitch.rb +83 -0
- data/lib/wtf_chord/rules.rb +40 -0
- data/lib/wtf_chord/scale.rb +67 -0
- data/lib/wtf_chord/version.rb +3 -0
- data/lib/wtf_chord.rb +25 -0
- data/lib/wtf_chord.yml +27 -0
- data/wtf_chord.gemspec +24 -0
- metadata +126 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cec796fdac73df072faef8d89c98f6fc37aded7f
|
4
|
+
data.tar.gz: 17772eb9d7105e1445ff40e6873c37d12914cca7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 945c5110c7e52340a4eca72385710d9cfa031cb647c2a2f8ae8207af7179467f3dc72059dc42bcf8b64083d0338644f018f85f5be551d53e4b722d3cd76f5e63
|
7
|
+
data.tar.gz: ba92873418bb4224f7dbf841501b02694c2fda562ea86030b4c918364b004c74b8f9d3bca6eb38168aeaf939934829ddc6186b44d09d79bc2daa86e01143a89e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in wtf_chord.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
# To use a debugger
|
7
|
+
gem 'byebug', group: [:development, :test]
|
8
|
+
|
9
|
+
group :development, :test do
|
10
|
+
# You need these
|
11
|
+
gem "rspec"
|
12
|
+
gem "pry"
|
13
|
+
end
|
14
|
+
|
15
|
+
group :development do
|
16
|
+
# You don't need these, but I use them
|
17
|
+
gem "awesome_print"
|
18
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# WTFChord
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/wtf_chord`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'wtf_chord'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install wtf_chord
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
$ wtfchord F#dim
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. Run `bundle exec wtf_chord` to use the gem in this directory, ignoring other installed copies of this gem.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/wtf_chord.
|
36
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "wtf_chord"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require "pry"
|
11
|
+
Pry.start
|
data/bin/setup
ADDED
data/exe/wtfchord
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
module WTFChord
|
2
|
+
class Chord
|
3
|
+
MAX_DIST = 5
|
4
|
+
MAX_FRET = 7
|
5
|
+
|
6
|
+
attr_reader :pitch, :steps, :notes, :name
|
7
|
+
|
8
|
+
def initialize(note, name)
|
9
|
+
@pitch = note.is_a?(Pitch) ? note : WTFChord.note(note)
|
10
|
+
@name = "#{@pitch.key}#{name}".freeze
|
11
|
+
@steps = Array(WTFChord.rules[name])
|
12
|
+
@notes = @steps.map { |dist| (@pitch + dist).note }.tap(&:uniq!)
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"#{name} (#{@notes.map(&:key) * ' - '})"
|
17
|
+
end
|
18
|
+
|
19
|
+
def fingerings(limit = 3)
|
20
|
+
list = []
|
21
|
+
(0..MAX_FRET).each do |from_fret|
|
22
|
+
fingering = get_fingering(from_fret)
|
23
|
+
|
24
|
+
next if list.include?(fingering)
|
25
|
+
|
26
|
+
if all_notes?(fingering.used_strings)
|
27
|
+
list << fingering
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
list.sort_by!(&:complexity)[0...limit].sort_by!(&:min_fret)
|
32
|
+
end
|
33
|
+
|
34
|
+
def draw_fingerings(limit = 3)
|
35
|
+
puts (fingerings(limit).map(&:draw) * "\n\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def get_fingering(from_fret = 0)
|
39
|
+
GUITAR.dup.tap do |guitar|
|
40
|
+
guitar.strings.each do |s|
|
41
|
+
fret = (from_fret..(from_fret+MAX_DIST)).detect { |dist| @notes.include?((s.original + dist).note) }
|
42
|
+
fret ? s.hold_on(fret) : s.dead
|
43
|
+
end
|
44
|
+
|
45
|
+
while guitar.used_strings[0] && guitar.used_strings[0].note != @notes[0]
|
46
|
+
guitar.used_strings.detect { |x| !x.dead? && x.note != @notes[0] }&.dead
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def all_notes?(strings)
|
52
|
+
snotes = strings.map(&:note).tap(&:uniq!)
|
53
|
+
@notes.all? { |n| snotes.include?(n) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module WTFChord
|
4
|
+
class Drawer
|
5
|
+
FRETS = 6
|
6
|
+
OPEN = "|".freeze
|
7
|
+
HORIZ = "–".freeze
|
8
|
+
SPACE = " ".freeze
|
9
|
+
BULL = "\u2022".freeze
|
10
|
+
LATIN = %w(0 I II III IV V VI VII VIII IX X XII XIII).freeze
|
11
|
+
NEWLINE = "\n".freeze
|
12
|
+
|
13
|
+
extend Forwardable
|
14
|
+
def_delegators :@fret, :strings
|
15
|
+
|
16
|
+
def initialize(fret, newline: nil)
|
17
|
+
@fret = fret
|
18
|
+
@newline = newline || NEWLINE
|
19
|
+
end
|
20
|
+
|
21
|
+
def draw
|
22
|
+
[
|
23
|
+
"[ #{head} ]#{capo}",
|
24
|
+
" #{border}",
|
25
|
+
*fret_rows,
|
26
|
+
" #{border}",
|
27
|
+
" #{string_keys}"
|
28
|
+
] * @newline
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def min_fret
|
34
|
+
@min_fret = begin
|
35
|
+
frets = strings.map(&:fret).tap(&:compact!)
|
36
|
+
_min = frets.min.to_i || 1
|
37
|
+
_min > 2 ? _min : 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def head
|
42
|
+
@fret.fingers * SPACE
|
43
|
+
end
|
44
|
+
|
45
|
+
def capo
|
46
|
+
" (capo #{to_latin @fret.capo})" if @fret.capo > 0
|
47
|
+
end
|
48
|
+
|
49
|
+
def border
|
50
|
+
@border ||= HORIZ * 3 * strings.length
|
51
|
+
end
|
52
|
+
|
53
|
+
def string_rows
|
54
|
+
strings.map { |string| draw_string(string.fret) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def fret_rows
|
58
|
+
string_rows.transpose.map! { |row| " #{row * SPACE} " }.tap do |rows|
|
59
|
+
rows[0] << " #{to_latin min_fret}" if min_fret > 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def string_keys
|
64
|
+
strings.map { |s| s.dead? ? SPACE : "%-2s" % s.key } * " "
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def to_latin(num)
|
70
|
+
LATIN[num]
|
71
|
+
end
|
72
|
+
|
73
|
+
def draw_string(fret)
|
74
|
+
Array.new(FRETS, OPEN).tap do |rows|
|
75
|
+
rows[(fret - min_fret.pred).pred] = BULL if fret.to_i > 0
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'wtf_chord/guitar_string'
|
2
|
+
require 'wtf_chord/drawer'
|
3
|
+
|
4
|
+
module WTFChord
|
5
|
+
class Fingerboard
|
6
|
+
attr_reader :strings, :capo
|
7
|
+
|
8
|
+
def initialize(*strings)
|
9
|
+
@capo = 0
|
10
|
+
@strings = strings.map! do |string|
|
11
|
+
GuitarString.new(string, @capo)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def [] idx
|
16
|
+
case idx
|
17
|
+
when 1..@strings.length then @strings[-idx]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_capo(capo = 0)
|
22
|
+
@capo = capo
|
23
|
+
@strings.each do |string|
|
24
|
+
string.set_capo(@capo)
|
25
|
+
end
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def fingers
|
30
|
+
strings.map { |string| string.dead? ? "×" : string.fret }
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
"[ #{fingers * " "} ]"
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_dup(other)
|
38
|
+
super
|
39
|
+
@strings = other.strings.map(&:dup)
|
40
|
+
@code = nil
|
41
|
+
@complexity = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_fingers(fingers)
|
45
|
+
fingers.each_with_index do |f, i|
|
46
|
+
f.nil? ? @strings[i].dead : @strings[i].hold_on(f)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def with_fingers(*fingers)
|
51
|
+
strings_was = @strings.dup
|
52
|
+
set_fingers(fingers)
|
53
|
+
yield(self)
|
54
|
+
self
|
55
|
+
ensure
|
56
|
+
@strings = strings_was
|
57
|
+
end
|
58
|
+
|
59
|
+
def draw
|
60
|
+
Drawer.new(self).draw
|
61
|
+
end
|
62
|
+
|
63
|
+
def count_holded_strings
|
64
|
+
strings.count(&:holded?)
|
65
|
+
end
|
66
|
+
|
67
|
+
def holded_strings
|
68
|
+
strings.select(&:holded?)
|
69
|
+
end
|
70
|
+
|
71
|
+
def used_strings
|
72
|
+
strings.reject(&:dead?)
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect
|
76
|
+
to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
def code
|
80
|
+
@code ||= strings.map(&:code).pack("c*")
|
81
|
+
end
|
82
|
+
|
83
|
+
def complexity
|
84
|
+
@complexity ||= count_holded_strings * finger_dist
|
85
|
+
end
|
86
|
+
|
87
|
+
def finger_dist
|
88
|
+
frets = holded_strings.map(&:fret)
|
89
|
+
((frets.max - frets.min).nonzero? || 1)
|
90
|
+
end
|
91
|
+
|
92
|
+
def min_fret
|
93
|
+
holded_strings.map(&:fret).min
|
94
|
+
end
|
95
|
+
|
96
|
+
def == other
|
97
|
+
case other
|
98
|
+
when Fingerboard then other.code == code
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'wtf_chord/pitch'
|
2
|
+
|
3
|
+
module WTFChord
|
4
|
+
class GuitarString < DelegateClass(WTFChord::Pitch)
|
5
|
+
attr_reader :capo, :fret, :original
|
6
|
+
|
7
|
+
def initialize(note, capo = 0)
|
8
|
+
@original = WTFChord.note(note)
|
9
|
+
@fret = nil
|
10
|
+
@capo = capo
|
11
|
+
super(@original + capo)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize_dup(other)
|
15
|
+
super
|
16
|
+
__setobj__(@original.dup)
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_capo(capo)
|
20
|
+
@capo = capo
|
21
|
+
calculate_note!
|
22
|
+
end
|
23
|
+
|
24
|
+
def hold_on(fret)
|
25
|
+
@fret = fret
|
26
|
+
calculate_note!
|
27
|
+
end
|
28
|
+
|
29
|
+
def open
|
30
|
+
@fret = 0
|
31
|
+
calculate_note!
|
32
|
+
end
|
33
|
+
|
34
|
+
def dead
|
35
|
+
@fret = nil
|
36
|
+
calculate_note!
|
37
|
+
end
|
38
|
+
|
39
|
+
def dead?
|
40
|
+
@fret.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def holded?
|
44
|
+
!dead? && @fret > 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def code
|
48
|
+
dead? ? -1 : to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def calculate_note!
|
54
|
+
__setobj__(@original + @capo + (@fret || 0))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module WTFChord
|
2
|
+
class Note
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
attr_reader :key, :name, :position
|
6
|
+
|
7
|
+
def initialize(key, position = nil)
|
8
|
+
if /^(?<note>[a-h])(?<sign>#|b)?$/i =~ key
|
9
|
+
note.upcase!
|
10
|
+
@key = "#{note}#{sign}".freeze
|
11
|
+
@name = "#{DIATONIC[note]}#{SIGNS[sign]}".freeze
|
12
|
+
@position = position
|
13
|
+
else
|
14
|
+
raise ArgumentError
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@key.sub(/[b#]$/, SIGNS)
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"#@name (#{to_s}: #@position)"
|
24
|
+
end
|
25
|
+
|
26
|
+
def == other
|
27
|
+
case other
|
28
|
+
when String then other.casecmp(@key).zero?
|
29
|
+
when Integer, Note then other == @position
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def <=> other
|
34
|
+
case other
|
35
|
+
when Note then @position <=> other.position
|
36
|
+
when Integer then @position <=> other
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def aliases
|
41
|
+
@aliases ||= SCALE.select { |t| t.position == @position && t.key != @key }
|
42
|
+
end
|
43
|
+
|
44
|
+
def semitone?
|
45
|
+
@key.end_with?(FLAT, SHARP)
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_octave(octave)
|
49
|
+
Pitch.new(self, octave)
|
50
|
+
end
|
51
|
+
|
52
|
+
def chord(name)
|
53
|
+
with_octave.chord(name)
|
54
|
+
end
|
55
|
+
|
56
|
+
alias :[] :with_octave
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'scanf'
|
2
|
+
require 'wtf_chord/note'
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module WTFChord
|
6
|
+
class Pitch
|
7
|
+
extend Forwardable
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
def_delegators :@note, :key
|
11
|
+
attr_reader :note, :octave
|
12
|
+
|
13
|
+
def initialize(note, octave = nil)
|
14
|
+
raise ArgumentError unless note.is_a?(Note)
|
15
|
+
|
16
|
+
@note = note
|
17
|
+
@octave = octave || 4
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_str
|
21
|
+
"#{@note.key}#{octave}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"#{@note.to_s}#{octave}"
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :inspect :to_s
|
29
|
+
|
30
|
+
def move(amount)
|
31
|
+
return self if amount.zero?
|
32
|
+
|
33
|
+
to_octave, to_pos = (to_i + amount).divmod(12)
|
34
|
+
|
35
|
+
if to_pos.zero?
|
36
|
+
to_octave -= 1
|
37
|
+
to_pos = 12
|
38
|
+
end
|
39
|
+
|
40
|
+
SCALE[to_pos][to_octave]
|
41
|
+
end
|
42
|
+
|
43
|
+
alias :+ :move
|
44
|
+
|
45
|
+
def - amount
|
46
|
+
move -amount
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_i
|
50
|
+
(@octave * 12) + @note.position
|
51
|
+
end
|
52
|
+
|
53
|
+
def == other
|
54
|
+
case other
|
55
|
+
when Integer, Pitch then other == to_i
|
56
|
+
when String then other.casecmp(to_str).zero?
|
57
|
+
when Note then other == @note
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def <=> other
|
62
|
+
case other
|
63
|
+
when Note then @note <=> other
|
64
|
+
when Integer then to_i <=> other
|
65
|
+
when Pitch then to_i <=> other.to_i
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def chord(name)
|
70
|
+
Chord.new(self, name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
NOTE_FSTR ||= "%2[A-Ha-h#b]%d".freeze
|
75
|
+
|
76
|
+
def self.note(val)
|
77
|
+
case val
|
78
|
+
when String
|
79
|
+
key, octave = val.scanf(NOTE_FSTR)
|
80
|
+
SCALE[key][octave || 4] if key
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'wtf_chord/chord'
|
3
|
+
|
4
|
+
module WTFChord
|
5
|
+
class Rules
|
6
|
+
attr_reader :path, :chords, :extra, :pattern
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
rules = YAML.load_file(@path)
|
11
|
+
@chords = rules[:chords]
|
12
|
+
@extra = rules[:extra]
|
13
|
+
@pattern = /
|
14
|
+
(?<name>#{Regexp.union(@chords.keys.sort_by(&:length).reverse!)})?
|
15
|
+
(?<ext>#{Regexp.union(@extra.keys.sort_by(&:length).reverse!)})?
|
16
|
+
/x
|
17
|
+
end
|
18
|
+
|
19
|
+
def find(name = nil)
|
20
|
+
steps = []
|
21
|
+
name ||= ""
|
22
|
+
|
23
|
+
name.match(pattern) do |m|
|
24
|
+
base = chords[m[:name]] || chords["M"]
|
25
|
+
steps.concat(base)
|
26
|
+
steps.concat(extra[m[:ext]]) if m[:ext] && m[:ext].length <= (6 - steps.length)
|
27
|
+
end
|
28
|
+
|
29
|
+
steps.tap(&:uniq!)
|
30
|
+
end
|
31
|
+
|
32
|
+
alias :[] :find
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.chord(name)
|
36
|
+
name.match /^(?<note>[A-H][b#]?)(?<name>.+)?$/ do |m|
|
37
|
+
Chord.new(m[:note], m[:name])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'wtf_chord/note'
|
2
|
+
require 'wtf_chord/pitch'
|
3
|
+
require 'wtf_chord/fingerboard'
|
4
|
+
|
5
|
+
module WTFChord
|
6
|
+
class ScaleArray < Array
|
7
|
+
attr_reader :last_position
|
8
|
+
|
9
|
+
def self.build(*list)
|
10
|
+
tones = list.flat_map.with_index do |keys, position|
|
11
|
+
keys.split("|").map! { |key| Note.new(key, position.next) }
|
12
|
+
end
|
13
|
+
new(tones)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(list)
|
17
|
+
@last_position = list.map(&:position).sort![-1]
|
18
|
+
super(list)
|
19
|
+
end
|
20
|
+
|
21
|
+
def [] idx
|
22
|
+
case idx
|
23
|
+
when 0 then nil
|
24
|
+
when Integer then super(index(idx < 0 ? last_position + idx.next : idx))
|
25
|
+
when String then super(index(idx))
|
26
|
+
else super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private_constant :ScaleArray
|
32
|
+
|
33
|
+
FLAT ||= "b".freeze
|
34
|
+
SHARP ||= "#".freeze
|
35
|
+
SIGNS ||= { FLAT => "\u266D".freeze, SHARP => "\u266F".freeze }.freeze
|
36
|
+
|
37
|
+
DIATONIC = {
|
38
|
+
"C" => "Do",
|
39
|
+
"D" => "Re",
|
40
|
+
"E" => "Mi",
|
41
|
+
"F" => "Fa",
|
42
|
+
"G" => "Sol",
|
43
|
+
"A" => "La",
|
44
|
+
"B" => "Si",
|
45
|
+
"H" => "Si"
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
SCALE ||= begin
|
49
|
+
chromatic_scale = %W(
|
50
|
+
C
|
51
|
+
C#|Db
|
52
|
+
D
|
53
|
+
D#|Eb
|
54
|
+
E
|
55
|
+
F
|
56
|
+
F#|Gb
|
57
|
+
G
|
58
|
+
G#|Ab
|
59
|
+
A
|
60
|
+
Bb|A#
|
61
|
+
B|H
|
62
|
+
)
|
63
|
+
ScaleArray.build(*chromatic_scale).freeze
|
64
|
+
end
|
65
|
+
|
66
|
+
GUITAR ||= Fingerboard.new(*%w(E2 A2 D3 G3 B3 E4))
|
67
|
+
end
|
data/lib/wtf_chord.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "wtf_chord/scale"
|
2
|
+
require "wtf_chord/rules"
|
3
|
+
require "wtf_chord/version"
|
4
|
+
|
5
|
+
module WTFChord
|
6
|
+
DEFAULTS = {
|
7
|
+
:rules_file => File.expand_path("../wtf_chord.yml", __FILE__)
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
class << self
|
11
|
+
attr_accessor :rules
|
12
|
+
|
13
|
+
def config # :yields:
|
14
|
+
yield(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rules_file= value
|
18
|
+
self.rules = Rules.new(File.expand_path(value))
|
19
|
+
end
|
20
|
+
|
21
|
+
def rules
|
22
|
+
@rules ||= Rules.new(DEFAULTS[:rules_file])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/wtf_chord.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
:chords:
|
2
|
+
"M": [0, 4, 7]
|
3
|
+
"m": [0, 3, 7]
|
4
|
+
"aug": [0, 4, 6]
|
5
|
+
"dim": [0, 3, 6]
|
6
|
+
"sus2": [0, 2, 7]
|
7
|
+
"sus4": [0, 5, 7]
|
8
|
+
"6": [0, 4, 7, 9]
|
9
|
+
"m6": [0, 3, 7, 9]
|
10
|
+
"7": [0, 4, 7, 10]
|
11
|
+
"7M": [0, 4, 7, 11]
|
12
|
+
"m7": [0, 3, 7, 10]
|
13
|
+
"m7M": [0, 3, 7, 11]
|
14
|
+
"dim7": [0, 3, 6, 9]
|
15
|
+
"7sus2": [0, 2, 7, 10]
|
16
|
+
"7sus4": [0, 5, 7, 10]
|
17
|
+
"7Msus2": [0, 2, 7, 11]
|
18
|
+
"7Msus4": [0, 5, 7, 11]
|
19
|
+
"5": [0, 7]
|
20
|
+
|
21
|
+
:extra:
|
22
|
+
"9": [14]
|
23
|
+
"11": [17]
|
24
|
+
"13": [21]
|
25
|
+
"add9": [10, 14]
|
26
|
+
"add11": [10, 14, 17]
|
27
|
+
"add13": [10, 14, 17, 21]
|
data/wtf_chord.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'wtf_chord/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "wtf_chord"
|
8
|
+
spec.version = WTFChord::VERSION
|
9
|
+
spec.authors = ["Anton"]
|
10
|
+
spec.email = ["anton.estum@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{WTF Chord? is a Ruby Gem for generating guitar chords.}
|
13
|
+
spec.description = %q{WTF Chord? provides the library and CLI-tool for generating guitar chords.}
|
14
|
+
spec.homepage = "https://github.com/estum/wtf_chord"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", ">= 1.11", "< 2"
|
22
|
+
spec.add_development_dependency "rake", ">= 10.0", "< 12"
|
23
|
+
spec.add_development_dependency "rspec", ">= 3.0", "< 4"
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wtf_chord
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Anton
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.11'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rake
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '10.0'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '12'
|
43
|
+
type: :development
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '10.0'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '12'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '3.0'
|
60
|
+
- - "<"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '4'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.0'
|
70
|
+
- - "<"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '4'
|
73
|
+
description: WTF Chord? provides the library and CLI-tool for generating guitar chords.
|
74
|
+
email:
|
75
|
+
- anton.estum@gmail.com
|
76
|
+
executables:
|
77
|
+
- wtfchord
|
78
|
+
extensions: []
|
79
|
+
extra_rdoc_files: []
|
80
|
+
files:
|
81
|
+
- ".gitignore"
|
82
|
+
- ".rspec"
|
83
|
+
- ".travis.yml"
|
84
|
+
- Gemfile
|
85
|
+
- README.md
|
86
|
+
- Rakefile
|
87
|
+
- bin/console
|
88
|
+
- bin/setup
|
89
|
+
- exe/wtfchord
|
90
|
+
- lib/wtf_chord.rb
|
91
|
+
- lib/wtf_chord.yml
|
92
|
+
- lib/wtf_chord/chord.rb
|
93
|
+
- lib/wtf_chord/drawer.rb
|
94
|
+
- lib/wtf_chord/fingerboard.rb
|
95
|
+
- lib/wtf_chord/guitar_string.rb
|
96
|
+
- lib/wtf_chord/note.rb
|
97
|
+
- lib/wtf_chord/pitch.rb
|
98
|
+
- lib/wtf_chord/rules.rb
|
99
|
+
- lib/wtf_chord/scale.rb
|
100
|
+
- lib/wtf_chord/version.rb
|
101
|
+
- wtf_chord.gemspec
|
102
|
+
homepage: https://github.com/estum/wtf_chord
|
103
|
+
licenses: []
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.5.1
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: WTF Chord? is a Ruby Gem for generating guitar chords.
|
125
|
+
test_files: []
|
126
|
+
has_rdoc:
|