chords 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README +24 -0
- data/bin/chords +5 -0
- data/lib/chords.rb +3 -0
- data/lib/chords/chord.rb +52 -0
- data/lib/chords/chord_factory.rb +33 -0
- data/lib/chords/command_line.rb +115 -0
- data/lib/chords/fingering.rb +132 -0
- data/lib/chords/fretboard.rb +68 -0
- data/lib/chords/note.rb +66 -0
- data/lib/chords/text_formatter.rb +34 -0
- metadata +65 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Antti Hakala
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
Chords
|
2
|
+
======
|
3
|
+
A chord generator lib for guitar-like instruments.
|
4
|
+
|
5
|
+
|
6
|
+
Running irb under chords/lib:
|
7
|
+
|
8
|
+
>> require 'chords'
|
9
|
+
=> true
|
10
|
+
>> include Chords
|
11
|
+
=> Object
|
12
|
+
>> Fretboard.standard.print E.major, :duplicates => 3, :max_fret_distance => 2
|
13
|
+
|
14
|
+
E: --0----0----0----0---12----7----0---12----7--
|
15
|
+
B: --9----0----0---12---12----9---12---12----9--
|
16
|
+
G: --9----9----1---13---13----9---13---13----9--
|
17
|
+
D: --9----9----2---14---14----9---14---14----9--
|
18
|
+
A: -11---11----2---14---14----7---14---14----7--
|
19
|
+
E: --0----0----0----0----0----0---12---12----7--
|
20
|
+
|
21
|
+
Total 9 fingerings.
|
22
|
+
|
23
|
+
=> nil
|
24
|
+
>>
|
data/bin/chords
ADDED
data/lib/chords.rb
ADDED
data/lib/chords/chord.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'chords/note'
|
2
|
+
|
3
|
+
module Chords
|
4
|
+
|
5
|
+
class Chord
|
6
|
+
attr_reader :notes
|
7
|
+
attr_accessor :root
|
8
|
+
|
9
|
+
def initialize(*notes)
|
10
|
+
@notes = notes.flatten
|
11
|
+
@root = @notes.first
|
12
|
+
end
|
13
|
+
|
14
|
+
def first_inversion
|
15
|
+
raise "Not enough notes for inversion" if notes.size < 3
|
16
|
+
inversion = Chord.new(@notes[1], @notes[2], @root, *@notes[3..-1])
|
17
|
+
inversion.root = @root
|
18
|
+
inversion
|
19
|
+
end
|
20
|
+
|
21
|
+
def second_inversion
|
22
|
+
raise "Not enough notes for inversion" if notes.size < 3
|
23
|
+
inversion = Chord.new(@notes[2], @root, @notes[1], *@notes[3..-1])
|
24
|
+
inversion.root = @root
|
25
|
+
inversion
|
26
|
+
end
|
27
|
+
|
28
|
+
def sixth
|
29
|
+
Chord.new(@notes + [@root + 9])
|
30
|
+
end
|
31
|
+
|
32
|
+
def seventh
|
33
|
+
Chord.new(@notes + [@root + 10])
|
34
|
+
end
|
35
|
+
|
36
|
+
def add9
|
37
|
+
Chord.new(@notes + [@root + 14])
|
38
|
+
end
|
39
|
+
|
40
|
+
def add11
|
41
|
+
Chord.new(@notes + [@root + 17])
|
42
|
+
end
|
43
|
+
|
44
|
+
def bass(note)
|
45
|
+
chord = Chord.new([note] + @notes)
|
46
|
+
chord.root = self.root
|
47
|
+
chord
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'chords/chord'
|
2
|
+
|
3
|
+
module Chords
|
4
|
+
module ChordFactory
|
5
|
+
extend self
|
6
|
+
CHORDS = {:major => [4,7],
|
7
|
+
:minor => [3,7],
|
8
|
+
:five => [7],
|
9
|
+
:sus2 => [2, 7],
|
10
|
+
:sus4 => [5, 7],
|
11
|
+
:aug => [4, 8],
|
12
|
+
:dim => [3, 6],
|
13
|
+
:minus5 => [4, 6]}
|
14
|
+
|
15
|
+
def new_chord(root, key)
|
16
|
+
raise "No chord with key #{key}" unless CHORDS.has_key?(key)
|
17
|
+
notes = [root]
|
18
|
+
notes += CHORDS[key].map{|interval| root + interval}
|
19
|
+
Chord.new(notes)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Extensions to Note so we can say for example 'E.major' to create a chord
|
24
|
+
module NoteExt
|
25
|
+
ChordFactory::CHORDS.keys.each do |key|
|
26
|
+
define_method key do
|
27
|
+
ChordFactory.new_chord(self, key)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
NOTES.flatten.each{|note_name| Chords.const_get(note_name).extend NoteExt}
|
33
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'chords'
|
3
|
+
|
4
|
+
module Chords
|
5
|
+
class CommandLineParser
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@args = args
|
9
|
+
@frets = Chords::Fretboard::DEFAULT_FRETS
|
10
|
+
@duplicates = 0
|
11
|
+
@max_fret_distance = Chords::Fingering::DEFAULT_MAX_FRET_DISTANCE
|
12
|
+
@tuning = Chords::Fretboard::TUNINGS[:standard]
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
@opts = OptionParser.new
|
17
|
+
@opts.banner = "Usage: chords [options] CHORD"
|
18
|
+
|
19
|
+
@opts.on("-l", "--list",
|
20
|
+
"List tunings and chords.") do
|
21
|
+
puts tunings_and_chords
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
@opts.on("-f", "--frets NUMBER_OF_FRETS",
|
26
|
+
Integer,
|
27
|
+
"Number of frets, i.e. max position in fingerings.") do |frets|
|
28
|
+
@frets = frets
|
29
|
+
end
|
30
|
+
|
31
|
+
@opts.on("-d", "--duplicates DUPLICATES",
|
32
|
+
Integer,
|
33
|
+
"Number of duplicated notes.") do |duplicates|
|
34
|
+
@duplicates = duplicates
|
35
|
+
end
|
36
|
+
|
37
|
+
@opts.on("-m", "--max-fret-distance MAX_FRET_DISTANCE",
|
38
|
+
Integer,
|
39
|
+
"Maximum distance between positions in fingering.") do |mfd|
|
40
|
+
@max_fret_distance = mfd
|
41
|
+
end
|
42
|
+
|
43
|
+
@opts.on("-t", "--tuning TUNING",
|
44
|
+
"Tuning to use. See -l for list of available tunings and chords.") do |t|
|
45
|
+
if !Chords::Fretboard::TUNINGS.has_key?(t.to_sym)
|
46
|
+
raise OptionParser::ParseError.new("Invalid tuning")
|
47
|
+
end
|
48
|
+
@tuning = Chords::Fretboard::TUNINGS[t.to_sym]
|
49
|
+
end
|
50
|
+
|
51
|
+
@opts.on_tail("-h", "--help", examples) do
|
52
|
+
puts @opts
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
print_chord @opts.parse!(@args)
|
57
|
+
rescue OptionParser::ParseError => e
|
58
|
+
puts e.message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def print_chord(args)
|
65
|
+
if args.size == 1
|
66
|
+
chord_str = args.first
|
67
|
+
chord_str =~ /^(.{1,2})\.(.*)/
|
68
|
+
|
69
|
+
if $1 and $2 and Chords::NOTES.flatten.include?($1)
|
70
|
+
note = Chords.const_get($1)
|
71
|
+
begin
|
72
|
+
chord = note.class_eval($2)
|
73
|
+
Fretboard.new(@tuning, @frets).print(chord,
|
74
|
+
{:duplicates => @duplicates,
|
75
|
+
:max_fret_distance => @max_fret_distance})
|
76
|
+
return
|
77
|
+
rescue NameError => e # invalid chord variation
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
puts "Invalid chord."
|
83
|
+
puts @opts
|
84
|
+
end
|
85
|
+
|
86
|
+
def tunings_and_chords
|
87
|
+
out = "Tunings:\n=======\n"
|
88
|
+
Chords::Fretboard::TUNINGS.each do |key, value|
|
89
|
+
out += "#{key.to_s.ljust(12, ' ')}: #{value.map{|n| n.class.to_s.gsub(/.*::/,'')}.join(',')}\n"
|
90
|
+
end
|
91
|
+
out += "\nChords:\n======\n"
|
92
|
+
Chords::ChordFactory::CHORDS.each do |key, value|
|
93
|
+
out += "#{key}\n"
|
94
|
+
end
|
95
|
+
out += "\nYou can also use following modifiers on chords:\n"+
|
96
|
+
"* first_inversion\n"+
|
97
|
+
"* second_inversion\n"+
|
98
|
+
"* sixth\n"+
|
99
|
+
"* seventh\n"+
|
100
|
+
"* add9\n"+
|
101
|
+
"* add11\n"+
|
102
|
+
"* bass(NOTE)\n"
|
103
|
+
end
|
104
|
+
|
105
|
+
def examples
|
106
|
+
"\n\nExamples:\n"+
|
107
|
+
"$ chords E.major.add9\n"+
|
108
|
+
"$ chords \"C.major.bass(G)\"\n"+
|
109
|
+
"$ chords -d 3 As.dim\n"+
|
110
|
+
"$ chords -t dadgad D.minor.first_inversion\n"+
|
111
|
+
"\nSharp notes are followed by 's' and flats by 'b', e.g. 'Gs' and 'Eb'."
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Chords
|
4
|
+
|
5
|
+
class Fingering
|
6
|
+
DEFAULT_MAX_FRET_DISTANCE = 3
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@positions, :[], :==, :inspect, :each, :each_with_index
|
11
|
+
|
12
|
+
def initialize(fretboard, positions=nil)
|
13
|
+
@fretboard = fretboard
|
14
|
+
@positions = positions || ([nil] * @fretboard.open_notes.size)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find_variations(fretboard, chord, opts={})
|
18
|
+
fingering = Fingering.new(fretboard)
|
19
|
+
fingerings = fingering.expand(chord.notes.first)
|
20
|
+
|
21
|
+
chord.notes[1..-1].each do |note|
|
22
|
+
fingerings = fingerings.map{|f| f.expand(note, opts)}.flatten(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
(opts[:duplicates] || 0).times do
|
26
|
+
fingerings = fingerings.map{|f| f.add_duplicate(opts)}.flatten(1).uniq
|
27
|
+
end
|
28
|
+
|
29
|
+
fingerings
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns all fingering variations of this fingering
|
33
|
+
# expanded with note.
|
34
|
+
# Expanded note wil be on a higher string than existing notes.
|
35
|
+
# It will also be the highest note.
|
36
|
+
def expand(note, opts={})
|
37
|
+
max_fret_distance = opts[:max_fret_distance] || DEFAULT_MAX_FRET_DISTANCE
|
38
|
+
|
39
|
+
fingerings = []
|
40
|
+
|
41
|
+
((highest_used_string+1)..(@positions.size-1)).each do |str_i|
|
42
|
+
new_note_positions(note, str_i, max_fret_distance).each do |pos|
|
43
|
+
if (@fretboard.open_notes[str_i] + pos) > highest_note
|
44
|
+
new_positions = @positions.dup
|
45
|
+
new_positions[str_i] = pos
|
46
|
+
fingerings << Fingering.new(@fretboard, new_positions)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
fingerings
|
52
|
+
end
|
53
|
+
|
54
|
+
# returns variations of this fingering with one duplicate note added
|
55
|
+
def add_duplicate(opts={})
|
56
|
+
return [] if unused_strings < 1
|
57
|
+
max_fret_distance = opts[:max_fret_distance] || DEFAULT_MAX_FRET_DISTANCE
|
58
|
+
|
59
|
+
fingerings = []
|
60
|
+
|
61
|
+
@positions.each_with_index do |pos, i|
|
62
|
+
next unless pos.nil?
|
63
|
+
|
64
|
+
each_note do |note|
|
65
|
+
new_note_positions(note, i, max_fret_distance).each do |pos|
|
66
|
+
new_positions = @positions.dup
|
67
|
+
new_positions[i] = pos
|
68
|
+
fingerings << Fingering.new(@fretboard, new_positions)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
fingerings
|
74
|
+
end
|
75
|
+
|
76
|
+
def each_note
|
77
|
+
@positions.each_with_index do |pos, i|
|
78
|
+
yield((@fretboard.open_notes[i] + pos).class) unless pos.nil?
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def eql?(other)
|
83
|
+
self.hash == other.hash
|
84
|
+
end
|
85
|
+
|
86
|
+
def hash
|
87
|
+
@positions.hash
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def new_note_positions(note, string_index, max_fret_distance)
|
93
|
+
positions = []
|
94
|
+
open_note = @fretboard.open_notes[string_index]
|
95
|
+
pos = note.new(open_note.octave).value - open_note.value
|
96
|
+
pos += 12 if pos < 0
|
97
|
+
|
98
|
+
while pos <= @fretboard.frets
|
99
|
+
positions << pos if distance(pos) <= max_fret_distance
|
100
|
+
pos += 12
|
101
|
+
end
|
102
|
+
positions
|
103
|
+
end
|
104
|
+
|
105
|
+
def distance(position)
|
106
|
+
pos_arr = (@positions + [position]).select{|p| !p.nil? && p > 0}
|
107
|
+
(pos_arr.max || 0) - (pos_arr.min || 0)
|
108
|
+
end
|
109
|
+
|
110
|
+
def unused_strings
|
111
|
+
@fretboard.open_notes.size - @positions.compact.size
|
112
|
+
end
|
113
|
+
|
114
|
+
def highest_used_string
|
115
|
+
indices = []
|
116
|
+
@positions.each_with_index do |pos,i|
|
117
|
+
indices << i unless pos.nil?
|
118
|
+
end
|
119
|
+
indices.max || -1
|
120
|
+
end
|
121
|
+
|
122
|
+
def highest_note
|
123
|
+
notes = []
|
124
|
+
@positions.each_with_index do |pos,i|
|
125
|
+
notes << @fretboard.open_notes[i] + pos unless pos.nil?
|
126
|
+
end
|
127
|
+
notes.max || (@fretboard.open_notes.min - 1)
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'chords/chord_factory'
|
2
|
+
require 'chords/fingering'
|
3
|
+
require 'chords/text_formatter'
|
4
|
+
|
5
|
+
module Chords
|
6
|
+
|
7
|
+
class Fretboard
|
8
|
+
DEFAULT_FRETS = 14
|
9
|
+
|
10
|
+
# See http://en.wikipedia.org/wiki/Guitar_tunings
|
11
|
+
TUNINGS = {
|
12
|
+
# Some usual ones
|
13
|
+
:standard => [E.new, A.new, D.new, G.new(1), B.new(1), E.new(2)],
|
14
|
+
:drop_d => [D.new(-1), A.new, D.new, G.new(1), B.new(1), E.new(2)],
|
15
|
+
:ddd => [D.new(-1), A.new, D.new, G.new(1), B.new(1), D.new(1)], # double-dropped D
|
16
|
+
:dadgad => [D.new(-1), A.new, D.new, G.new(1), A.new(1), D.new(1)],
|
17
|
+
|
18
|
+
# Major open tunings
|
19
|
+
:open_e => [E.new, B.new, E.new(1), Gs.new(1), B.new(1), E.new(2)],
|
20
|
+
:open_d => [D.new(-1), A.new, D.new, Fs.new(1), A.new(1), D.new(1)],
|
21
|
+
:open_a => [E.new, A.new, E.new(1), A.new(1), Cs.new(1), E.new(2)],
|
22
|
+
:open_g => [D.new(-1), G.new, D.new, G.new(1), B.new(1), D.new(1)],
|
23
|
+
:open_c => [C.new(-1), G.new, C.new, G.new(1), C.new(1), E.new(2)],
|
24
|
+
|
25
|
+
# Cross-note tunings
|
26
|
+
:cross_e => [E.new, B.new, E.new(1), G.new(1), B.new(1), E.new(2)],
|
27
|
+
:cross_d => [D.new(-1), A.new, D.new, F.new(1), A.new(1), D.new(1)],
|
28
|
+
:cross_a => [E.new, A.new, E.new(1), A.new(1), C.new(1), E.new(2)],
|
29
|
+
:cross_g => [D.new(-1), G.new, D.new, G.new(1), Bb.new(1), D.new(1)],
|
30
|
+
:cross_c => [C.new(-1), G.new, C.new, G.new(1), C.new(1), Eb.new(1)],
|
31
|
+
|
32
|
+
# Modal tunings
|
33
|
+
:cacgce => [C.new(-1), A.new, C.new, G.new(1), C.new(1), E.new(2)],
|
34
|
+
:e_modal => [E.new, B.new, E.new(1), E.new(1), B.new(1), E.new(2)],
|
35
|
+
:g_modal => [G.new, G.new, D.new, G.new(1), B.new(1), D.new(1)],
|
36
|
+
:gsus2 => [D.new(-1), G.new, D.new, G.new(1), A.new(1), D.new(1)],
|
37
|
+
:c15 => [C.new(-1), G.new, D.new, G.new(1), C.new(1), D.new(1)],
|
38
|
+
|
39
|
+
# "Extended chord" tunings
|
40
|
+
:dmaj7 => [D.new(-1), A.new, D.new, Fs.new(1), A.new(1), Cs.new(1)]
|
41
|
+
}
|
42
|
+
|
43
|
+
attr_reader :frets, :open_notes
|
44
|
+
|
45
|
+
def initialize(open_notes, frets)
|
46
|
+
@open_notes, @frets = open_notes, frets
|
47
|
+
@formatter = TextFormatter.new(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.method_missing(meth, *args)
|
51
|
+
if TUNINGS.has_key?(meth)
|
52
|
+
Fretboard.new(TUNINGS[meth], (args.first || DEFAULT_FRETS))
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def find(chord, opts={})
|
59
|
+
Fingering.find_variations(self, chord, opts)
|
60
|
+
end
|
61
|
+
|
62
|
+
def print(chord, opts={})
|
63
|
+
fingerings = find(chord, opts)
|
64
|
+
@formatter.print(fingerings)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
data/lib/chords/note.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
module Chords
|
3
|
+
NOTES = ['E', 'F', ['Fs', 'Gb'], 'G', ['Gs', 'Ab'], 'A',
|
4
|
+
['As', 'Bb'], ['B', 'H'], 'C', ['Cs', 'Db'], 'D', ['Ds', 'Eb']]
|
5
|
+
|
6
|
+
module Note
|
7
|
+
include Comparable
|
8
|
+
attr_reader :interval, :name, :octave
|
9
|
+
|
10
|
+
def self.create_by_value(value)
|
11
|
+
octave = value / 12
|
12
|
+
interval = value % 12
|
13
|
+
Chords.const_get([NOTES[interval]].flatten.first).new(octave)
|
14
|
+
end
|
15
|
+
|
16
|
+
def value
|
17
|
+
(@octave * 12) + @interval
|
18
|
+
end
|
19
|
+
|
20
|
+
def <=>(other)
|
21
|
+
if other.respond_to?(:value)
|
22
|
+
value <=> other.value
|
23
|
+
else
|
24
|
+
value <=> other
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def coerce(other)
|
29
|
+
if other.respond_to?(:value)
|
30
|
+
[other.value, value]
|
31
|
+
else
|
32
|
+
[other, value]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def name; self.class.to_s.gsub(/^.*::/, '') end
|
37
|
+
|
38
|
+
def +(other); Note.create_by_value(value + other) end
|
39
|
+
def -(other); Note.create_by_value(value - other) end
|
40
|
+
end
|
41
|
+
|
42
|
+
# E.g. E + 3 => G
|
43
|
+
module NoteClassArithmetic
|
44
|
+
def +(interval)
|
45
|
+
note = NOTES.detect{|n| [n].flatten.include?(self.to_s.gsub(/^.*::/, ''))}
|
46
|
+
idx = NOTES.index(note) + interval
|
47
|
+
idx = idx % NOTES.size
|
48
|
+
Chords.const_get [NOTES[idx]].flatten.first
|
49
|
+
end
|
50
|
+
def -(interval); self + (-interval) end
|
51
|
+
end
|
52
|
+
|
53
|
+
NOTES.each_with_index do |names, i|
|
54
|
+
names = [names].flatten
|
55
|
+
names.each do |n|
|
56
|
+
eval %Q(class #{n}
|
57
|
+
include Note
|
58
|
+
extend NoteClassArithmetic
|
59
|
+
|
60
|
+
def initialize(octave=0)
|
61
|
+
@interval, @octave = #{i}, octave
|
62
|
+
end
|
63
|
+
end)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module Chords
|
3
|
+
|
4
|
+
class TextFormatter
|
5
|
+
|
6
|
+
def initialize(fretboard)
|
7
|
+
@fretboard = fretboard
|
8
|
+
end
|
9
|
+
|
10
|
+
def print(fingerings)
|
11
|
+
rows = [""] * @fretboard.open_notes.size
|
12
|
+
|
13
|
+
fingerings.each do |fingering|
|
14
|
+
fingering.each_with_index do |position,i|
|
15
|
+
rows[i] += "-#{position ? position.to_s.rjust(2,'-') : '-X'}--"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
idx = 0
|
20
|
+
while rows.first.length > idx
|
21
|
+
parts = []
|
22
|
+
rows.each_with_index do |row, i|
|
23
|
+
parts << "#{@fretboard.open_notes[i].name.rjust(2, ' ')}: " + row[idx...(idx+75)]
|
24
|
+
end
|
25
|
+
puts "\n" + (parts.reverse.join("\n")) + "\n\n"
|
26
|
+
idx += 75
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "Total #{fingerings.size} fingerings."
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chords
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Antti Hakala
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-25 00:00:00 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: "Chords is a chord generator for guitar-like instruments. Handy for special tunings. "
|
17
|
+
email: antti.hakala@gmail.com
|
18
|
+
executables:
|
19
|
+
- chords
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- README
|
26
|
+
- MIT-LICENSE
|
27
|
+
- bin/chords
|
28
|
+
- lib/chords/chord.rb
|
29
|
+
- lib/chords/chord_factory.rb
|
30
|
+
- lib/chords/command_line.rb
|
31
|
+
- lib/chords/fingering.rb
|
32
|
+
- lib/chords/fretboard.rb
|
33
|
+
- lib/chords/note.rb
|
34
|
+
- lib/chords/text_formatter.rb
|
35
|
+
- lib/chords.rb
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/ander/chords
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.3.5
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Chord generator for guitar-like instruments.
|
64
|
+
test_files: []
|
65
|
+
|