chords 0.1 → 0.2
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.
- 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.
|