chords 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +9 -1
- data/bin/chords +1 -1
- data/lib/chords/chord.rb +26 -9
- data/lib/chords/chord_factory.rb +1 -1
- data/lib/chords/{command_line.rb → command_line_parser.rb} +7 -2
- data/lib/chords/fingering.rb +12 -0
- data/lib/chords/fretboard.rb +12 -5
- data/lib/chords/note.rb +6 -5
- data/lib/chords/pdf_formatter.rb +97 -0
- data/lib/chords/text_formatter.rb +2 -2
- metadata +28 -8
data/README
CHANGED
@@ -2,9 +2,17 @@ Chords
|
|
2
2
|
======
|
3
3
|
A chord generator lib for guitar-like instruments.
|
4
4
|
|
5
|
+
Using via gem (hosted at gemcutter.org)
|
6
|
+
---------------------------------------
|
7
|
+
|
8
|
+
$ sudo gem install chords
|
9
|
+
$ chords -h
|
5
10
|
|
6
|
-
Running irb under chords/lib:
|
7
11
|
|
12
|
+
Running irb under chords/lib:
|
13
|
+
-----------------------------
|
14
|
+
>> require 'rubygems'
|
15
|
+
=> true
|
8
16
|
>> require 'chords'
|
9
17
|
=> true
|
10
18
|
>> include Chords
|
data/bin/chords
CHANGED
data/lib/chords/chord.rb
CHANGED
@@ -6,47 +6,64 @@ module Chords
|
|
6
6
|
attr_reader :notes
|
7
7
|
attr_accessor :root
|
8
8
|
|
9
|
-
def initialize(*notes)
|
9
|
+
def initialize(title_parts, *notes)
|
10
|
+
@title_parts = title_parts
|
10
11
|
@notes = notes.flatten
|
11
12
|
@root = @notes.first
|
12
13
|
end
|
13
14
|
|
14
15
|
def first_inversion
|
15
16
|
raise "Not enough notes for inversion" if notes.size < 3
|
16
|
-
inversion = Chord.new(@
|
17
|
+
inversion = Chord.new(@title_parts.dup + ['1st inv.'],
|
18
|
+
@notes[1], @notes[2], @root, *@notes[3..-1])
|
17
19
|
inversion.root = @root
|
18
20
|
inversion
|
19
21
|
end
|
20
22
|
|
21
23
|
def second_inversion
|
22
24
|
raise "Not enough notes for inversion" if notes.size < 3
|
23
|
-
inversion = Chord.new(@
|
25
|
+
inversion = Chord.new(@title_parts.dup + ['2nd inv.'],
|
26
|
+
@notes[2], @root, @notes[1], *@notes[3..-1])
|
24
27
|
inversion.root = @root
|
25
28
|
inversion
|
26
29
|
end
|
27
30
|
|
28
31
|
def sixth
|
29
|
-
Chord.new(@notes + [@root + 9])
|
32
|
+
chord = Chord.new(@title_parts.dup + ['6th'], @notes + [@root + 9])
|
33
|
+
chord.root = @root
|
34
|
+
chord
|
30
35
|
end
|
31
36
|
|
32
37
|
def seventh
|
33
|
-
Chord.new(@notes + [@root + 10])
|
38
|
+
chord = Chord.new(@title_parts.dup + ['7th'], @notes + [@root + 10])
|
39
|
+
chord.root = @root
|
40
|
+
chord
|
34
41
|
end
|
35
42
|
|
36
43
|
def add9
|
37
|
-
Chord.new(@notes + [@root + 14])
|
44
|
+
chord = Chord.new(@title_parts.dup + ['add9'], @notes + [@root + 14])
|
45
|
+
chord.root = @root
|
46
|
+
chord
|
38
47
|
end
|
39
48
|
|
40
49
|
def add11
|
41
|
-
Chord.new(@notes + [@root + 17])
|
50
|
+
chord = Chord.new(@title_parts.dup + ['add11'], @notes + [@root + 17])
|
51
|
+
chord.root = @root
|
52
|
+
chord
|
42
53
|
end
|
43
54
|
|
44
55
|
def bass(note)
|
45
|
-
chord = Chord.new([note] + @notes)
|
46
|
-
chord.root =
|
56
|
+
chord = Chord.new(@title_parts.dup + ["(bass #{note.title})"], [note] + @notes)
|
57
|
+
chord.root = @root
|
47
58
|
chord
|
48
59
|
end
|
49
60
|
|
61
|
+
def title
|
62
|
+
ret = @root.title
|
63
|
+
ret += " #{@title_parts.join(' ')}" unless @title_parts.empty?
|
64
|
+
ret
|
65
|
+
end
|
66
|
+
|
50
67
|
end
|
51
68
|
|
52
69
|
end
|
data/lib/chords/chord_factory.rb
CHANGED
@@ -10,6 +10,7 @@ module Chords
|
|
10
10
|
@duplicates = 0
|
11
11
|
@max_fret_distance = Chords::Fingering::DEFAULT_MAX_FRET_DISTANCE
|
12
12
|
@tuning = Chords::Fretboard::TUNINGS[:standard]
|
13
|
+
@formatter = Chords::TextFormatter
|
13
14
|
end
|
14
15
|
|
15
16
|
def parse
|
@@ -48,6 +49,10 @@ module Chords
|
|
48
49
|
@tuning = Chords::Fretboard::TUNINGS[t.to_sym]
|
49
50
|
end
|
50
51
|
|
52
|
+
@opts.on("--pdf", "Output to pdf.") do
|
53
|
+
@formatter = Chords::PDFFormatter
|
54
|
+
end
|
55
|
+
|
51
56
|
@opts.on_tail("-h", "--help", examples) do
|
52
57
|
puts @opts
|
53
58
|
end
|
@@ -67,10 +72,10 @@ module Chords
|
|
67
72
|
chord_str =~ /^(.{1,2})\.(.*)/
|
68
73
|
|
69
74
|
if $1 and $2 and Chords::NOTES.flatten.include?($1)
|
70
|
-
note = Chords.const_get($1)
|
71
75
|
begin
|
76
|
+
note = Chords.const_get($1)
|
72
77
|
chord = note.class_eval($2)
|
73
|
-
Fretboard.new(@tuning, @frets).print(chord,
|
78
|
+
Fretboard.new(@tuning, @frets, @formatter).print(chord,
|
74
79
|
{:duplicates => @duplicates,
|
75
80
|
:max_fret_distance => @max_fret_distance})
|
76
81
|
return
|
data/lib/chords/fingering.rb
CHANGED
@@ -87,6 +87,18 @@ module Chords
|
|
87
87
|
@positions.hash
|
88
88
|
end
|
89
89
|
|
90
|
+
# return an array of relative positions
|
91
|
+
def relative(max_fret_dist)
|
92
|
+
not_open = @positions.select{|p| !p.nil? and p > 0}
|
93
|
+
|
94
|
+
if not_open.max <= (max_fret_dist + 1)
|
95
|
+
@positions
|
96
|
+
else
|
97
|
+
diff = (not_open.min - 1)
|
98
|
+
@positions.map{|p| (p.nil? or p == 0) ? p : p - diff}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
90
102
|
private
|
91
103
|
|
92
104
|
def new_note_positions(note, string_index, max_fret_distance)
|
data/lib/chords/fretboard.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'chords/chord_factory'
|
2
2
|
require 'chords/fingering'
|
3
3
|
require 'chords/text_formatter'
|
4
|
+
require 'chords/pdf_formatter'
|
4
5
|
|
5
6
|
module Chords
|
6
7
|
|
@@ -37,19 +38,25 @@ module Chords
|
|
37
38
|
:c15 => [C.new(-1), G.new, D.new, G.new(1), C.new(1), D.new(1)],
|
38
39
|
|
39
40
|
# "Extended chord" tunings
|
40
|
-
:dmaj7 => [D.new(-1), A.new, D.new, Fs.new(1), A.new(1), Cs.new(1)]
|
41
|
+
:dmaj7 => [D.new(-1), A.new, D.new, Fs.new(1), A.new(1), Cs.new(1)],
|
42
|
+
|
43
|
+
# Other instruments
|
44
|
+
:mandolin => [G.new, D.new, A.new(1), E.new(2)],
|
45
|
+
:ukulele => [G.new, C.new, E.new(1), A.new(1)],
|
46
|
+
:banjo => [G.new, D.new, G.new(1), B.new(1), D.new(1)]
|
41
47
|
}
|
42
48
|
|
43
49
|
attr_reader :frets, :open_notes
|
44
50
|
|
45
|
-
def initialize(open_notes, frets)
|
51
|
+
def initialize(open_notes, frets, formatter_class=TextFormatter)
|
46
52
|
@open_notes, @frets = open_notes, frets
|
47
|
-
@formatter =
|
53
|
+
@formatter = formatter_class.new(self)
|
48
54
|
end
|
49
55
|
|
50
56
|
def self.method_missing(meth, *args)
|
51
57
|
if TUNINGS.has_key?(meth)
|
52
|
-
Fretboard.new(TUNINGS[meth], (args
|
58
|
+
Fretboard.new(TUNINGS[meth], (args[0] || DEFAULT_FRETS),
|
59
|
+
args[1] || TextFormatter)
|
53
60
|
else
|
54
61
|
super
|
55
62
|
end
|
@@ -61,7 +68,7 @@ module Chords
|
|
61
68
|
|
62
69
|
def print(chord, opts={})
|
63
70
|
fingerings = find(chord, opts)
|
64
|
-
@formatter.print(fingerings)
|
71
|
+
@formatter.print(chord.title, fingerings, opts)
|
65
72
|
end
|
66
73
|
|
67
74
|
end
|
data/lib/chords/note.rb
CHANGED
@@ -5,7 +5,7 @@ module Chords
|
|
5
5
|
|
6
6
|
module Note
|
7
7
|
include Comparable
|
8
|
-
attr_reader :interval, :
|
8
|
+
attr_reader :interval, :octave
|
9
9
|
|
10
10
|
def self.create_by_value(value)
|
11
11
|
octave = value / 12
|
@@ -33,14 +33,14 @@ module Chords
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
|
36
|
+
def title; self.class.title end
|
37
|
+
|
38
38
|
def +(other); Note.create_by_value(value + other) end
|
39
39
|
def -(other); Note.create_by_value(value - other) end
|
40
40
|
end
|
41
41
|
|
42
42
|
# E.g. E + 3 => G
|
43
|
-
module
|
43
|
+
module NoteClassMethods
|
44
44
|
def +(interval)
|
45
45
|
note = NOTES.detect{|n| [n].flatten.include?(self.to_s.gsub(/^.*::/, ''))}
|
46
46
|
idx = NOTES.index(note) + interval
|
@@ -48,6 +48,7 @@ module Chords
|
|
48
48
|
Chords.const_get [NOTES[idx]].flatten.first
|
49
49
|
end
|
50
50
|
def -(interval); self + (-interval) end
|
51
|
+
def title; self.to_s.gsub(/^.*::/, '').sub('s', '#') end
|
51
52
|
end
|
52
53
|
|
53
54
|
NOTES.each_with_index do |names, i|
|
@@ -55,7 +56,7 @@ module Chords
|
|
55
56
|
names.each do |n|
|
56
57
|
eval %Q(class #{n}
|
57
58
|
include Note
|
58
|
-
extend
|
59
|
+
extend NoteClassMethods
|
59
60
|
|
60
61
|
def initialize(octave=0)
|
61
62
|
@interval, @octave = #{i}, octave
|
@@ -0,0 +1,97 @@
|
|
1
|
+
|
2
|
+
require 'prawn'
|
3
|
+
require 'prawn/layout'
|
4
|
+
|
5
|
+
module Chords
|
6
|
+
|
7
|
+
class PDFFormatter
|
8
|
+
|
9
|
+
def initialize(fretboard)
|
10
|
+
@fretboard = fretboard
|
11
|
+
end
|
12
|
+
|
13
|
+
def print(title, fingerings, opts={})
|
14
|
+
@pdf = Prawn::Document.new(:top_margin => 50)
|
15
|
+
@pdf.draw_text "#{title}", :at => [@pdf.margin_box.left, @pdf.margin_box.top + 30]
|
16
|
+
@max_dist = opts[:max_fret_distance] || Fingering::DEFAULT_MAX_FRET_DISTANCE
|
17
|
+
|
18
|
+
if fingerings.empty?
|
19
|
+
@pdf.text 'No fingerings found.'
|
20
|
+
else
|
21
|
+
@pdf.define_grid(:columns => 4, :rows => 6, :gutter => 40)
|
22
|
+
|
23
|
+
fingerings.each_with_index do |f, i|
|
24
|
+
print_fingering(f, i)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@pdf.render_file('chords.pdf')
|
29
|
+
puts "Wrote chords.pdf"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def string_dist(box)
|
35
|
+
box.width / (@fretboard.open_notes.size - 1)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fret_dist(box)
|
39
|
+
box.height / (@max_dist + 1)
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_box(i)
|
43
|
+
@pdf.start_new_page if @pdf.page_count <= i / 24
|
44
|
+
i_on_page = i % 24
|
45
|
+
@pdf.grid(i_on_page / 4, i_on_page % 4)
|
46
|
+
end
|
47
|
+
|
48
|
+
def fretboard_text(str, x, y)
|
49
|
+
x_adj = str.length > 1 ? -6 : -3
|
50
|
+
@pdf.draw_text(str, :at => [x + x_adj, y])
|
51
|
+
end
|
52
|
+
|
53
|
+
def print_fretboard(i)
|
54
|
+
box = get_box(i)
|
55
|
+
|
56
|
+
@pdf.mask(:line_width) do
|
57
|
+
@pdf.line_width 3
|
58
|
+
@pdf.stroke_line box.top_left, box.top_right
|
59
|
+
end
|
60
|
+
|
61
|
+
@pdf.stroke_line box.bottom_left, box.bottom_right
|
62
|
+
|
63
|
+
@fretboard.open_notes.each_with_index do |note, note_i|
|
64
|
+
x = box.left + note_i*string_dist(box)
|
65
|
+
@pdf.stroke_line [x, box.top], [x, box.bottom]
|
66
|
+
fretboard_text(note.title, box.left + note_i*string_dist(box), box.bottom - 11)
|
67
|
+
end
|
68
|
+
|
69
|
+
@max_dist.times do |n|
|
70
|
+
y = box.top - ((n+1) * fret_dist(box))
|
71
|
+
@pdf.stroke_line [box.left, y], [box.right, y]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def print_fingering(fingering, i)
|
76
|
+
print_fretboard(i)
|
77
|
+
|
78
|
+
box = get_box(i)
|
79
|
+
|
80
|
+
rad = ([fret_dist(box), string_dist(box)].min / 2) - 4
|
81
|
+
|
82
|
+
fingering.each_with_index do |pos, pos_i|
|
83
|
+
fretboard_text((pos || 'x').to_s, box.left + pos_i*string_dist(box), box.top + 4)
|
84
|
+
|
85
|
+
next if [nil, 0].include?(pos)
|
86
|
+
|
87
|
+
@pdf.fill_and_stroke do
|
88
|
+
@pdf.circle_at [box.left + (pos_i * (string_dist(box))),
|
89
|
+
box.top + (fret_dist(box) / 2) -
|
90
|
+
(fingering.relative(@max_dist)[pos_i] * fret_dist(box))],
|
91
|
+
:radius => rad
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -7,7 +7,7 @@ module Chords
|
|
7
7
|
@fretboard = fretboard
|
8
8
|
end
|
9
9
|
|
10
|
-
def print(fingerings)
|
10
|
+
def print(title, fingerings, opts={})
|
11
11
|
rows = [""] * @fretboard.open_notes.size
|
12
12
|
|
13
13
|
fingerings.each do |fingering|
|
@@ -20,7 +20,7 @@ module Chords
|
|
20
20
|
while rows.first.length > idx
|
21
21
|
parts = []
|
22
22
|
rows.each_with_index do |row, i|
|
23
|
-
parts << "#{@fretboard.open_notes[i].
|
23
|
+
parts << "#{@fretboard.open_notes[i].title.rjust(2, ' ')}: " + row[idx...(idx+75)]
|
24
24
|
end
|
25
25
|
puts "\n" + (parts.reverse.join("\n")) + "\n\n"
|
26
26
|
idx += 75
|
metadata
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chords
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
version: "0.2"
|
5
9
|
platform: ruby
|
6
10
|
authors:
|
7
11
|
- Antti Hakala
|
@@ -9,10 +13,23 @@ autorequire:
|
|
9
13
|
bindir: bin
|
10
14
|
cert_chain: []
|
11
15
|
|
12
|
-
date: 2010-
|
16
|
+
date: 2010-03-31 00:00:00 +03:00
|
13
17
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
20
|
+
name: prawn
|
21
|
+
prerelease: false
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 0
|
28
|
+
- 8
|
29
|
+
- 4
|
30
|
+
version: 0.8.4
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
16
33
|
description: "Chords is a chord generator for guitar-like instruments. Handy for special tunings. "
|
17
34
|
email: antti.hakala@gmail.com
|
18
35
|
executables:
|
@@ -27,10 +44,11 @@ files:
|
|
27
44
|
- bin/chords
|
28
45
|
- lib/chords/chord.rb
|
29
46
|
- lib/chords/chord_factory.rb
|
30
|
-
- lib/chords/
|
47
|
+
- lib/chords/command_line_parser.rb
|
31
48
|
- lib/chords/fingering.rb
|
32
49
|
- lib/chords/fretboard.rb
|
33
50
|
- lib/chords/note.rb
|
51
|
+
- lib/chords/pdf_formatter.rb
|
34
52
|
- lib/chords/text_formatter.rb
|
35
53
|
- lib/chords.rb
|
36
54
|
has_rdoc: true
|
@@ -46,18 +64,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
64
|
requirements:
|
47
65
|
- - ">="
|
48
66
|
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
49
69
|
version: "0"
|
50
|
-
version:
|
51
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
71
|
requirements:
|
53
72
|
- - ">="
|
54
73
|
- !ruby/object:Gem::Version
|
74
|
+
segments:
|
75
|
+
- 0
|
55
76
|
version: "0"
|
56
|
-
version:
|
57
77
|
requirements: []
|
58
78
|
|
59
79
|
rubyforge_project:
|
60
|
-
rubygems_version: 1.3.
|
80
|
+
rubygems_version: 1.3.6
|
61
81
|
signing_key:
|
62
82
|
specification_version: 3
|
63
83
|
summary: Chord generator for guitar-like instruments.
|