gitara 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Guardfile +8 -0
- data/README.markdown +214 -0
- data/bin/gitara +4 -0
- data/examples/aimee-man-wise-up/aimee-man-wise-up.ly +186 -0
- data/examples/aimee-man-wise-up/aimee-man-wise-up.midi +0 -0
- data/examples/aimee-man-wise-up/aimee-man-wise-up.pdf +0 -0
- data/examples/aimee-man-wise-up/aimee-man-wise-up.rb +170 -0
- data/examples/tab-with-bar.rb +7 -0
- data/examples/tab-with-reused-bar.rb +9 -0
- data/examples/tab.ly +117 -0
- data/examples/tab.rb +43 -0
- data/gitara.gemspec +15 -5
- data/lib/gitara.rb +38 -1
- data/lib/gitara/app.rb +29 -0
- data/lib/gitara/dsl.rb +35 -0
- data/lib/gitara/id_as_word.rb +8 -0
- data/lib/gitara/node/bar.rb +9 -0
- data/lib/gitara/node/bar/voiced_node.rb +13 -0
- data/lib/gitara/node/base.rb +82 -0
- data/lib/gitara/node/base/voiced_node.rb +22 -0
- data/lib/gitara/node/line.rb +6 -0
- data/lib/gitara/node/note_set.rb +6 -0
- data/lib/gitara/node/score.rb +6 -0
- data/lib/gitara/node/tab.rb +21 -0
- data/lib/gitara/pow/base.rb +11 -0
- data/lib/gitara/template/bar.erb +3 -0
- data/lib/gitara/template/line.erb +3 -0
- data/lib/gitara/template/score.erb +3 -0
- data/lib/gitara/template/tab.erb +91 -0
- data/lib/gitara/template/voice.erb +1 -0
- data/lib/gitara/version.rb +1 -1
- data/lib/gitara/voice.rb +20 -0
- data/spec/lib/gitara/app_spec.rb +12 -0
- data/spec/lib/gitara/dsl_spec.rb +173 -0
- data/spec/lib/gitara/node/bar/voiced_node_spec.rb +17 -0
- data/spec/lib/gitara/node/bar_spec.rb +22 -0
- data/spec/lib/gitara/node/base/voiced_node_spec.rb +35 -0
- data/spec/lib/gitara/node/base_spec.rb +201 -0
- data/spec/lib/gitara/node/note_set_spec.rb +4 -0
- data/spec/lib/gitara/node/tab_spec.rb +57 -0
- data/spec/lib/gitara/voice_spec.rb +31 -0
- data/spec/lib/gitara_spec.rb +23 -0
- data/spec/spec_helper.rb +27 -0
- metadata +217 -10
data/examples/tab.ly
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
\version "2.12.3"
|
2
|
+
\include "english.ly"
|
3
|
+
|
4
|
+
%-----------------------------------------------------------------------
|
5
|
+
% Bars
|
6
|
+
|
7
|
+
vOneBarBeganIt = { \skip 4 <a\3 c'\2 >8 <g\3>8 \skip 4 <g\3 c'\2 >8 <g\3>8 }
|
8
|
+
vTwoBarBeganIt = { <f\4>4 <f\4>4 <c\5>4 <e\4>4 }
|
9
|
+
|
10
|
+
vOneBarItsNot = { r8 <g'\1>4 <a\3>16 <g'\1>8 <g\3>16 <e'\1>4 <g\3>16( <a\3>16) }
|
11
|
+
vTwoBarItsNot = { <f\4>4 <f\4>4 <c\5>4 <e\4>4 }
|
12
|
+
|
13
|
+
vOneBarItThough = { r8 <d'\2>16( <c'\2>16) r8 <a\3>16 <c'\2>16 r8 <d'\2>16( <c'\2>16) r8 <a\3>8 }
|
14
|
+
vTwoBarItThough = { <f\4>4 <f\4>4 <d\4>4 <d\4>4 }
|
15
|
+
|
16
|
+
vOneBarWhatYouThought = { r8 <g'\1>4 <a\3>16 <g'\1>8 <g\3>16 <e'\1>4 <g\3>8 }
|
17
|
+
vTwoBarWhatYouThought = { <f\4>4 <f\4>4 <c\5>4 <e\4>4 }
|
18
|
+
|
19
|
+
vOneBarWhenYouFirst = { <e'\1>8 <d'\2>16( <c'\2>8) <a\3>8. <c'\2>4 <a\3>8 <c'\2>8 }
|
20
|
+
vTwoBarWhenYouFirst = { <a,\5>4 <a,\5>8 <e\4>4 <a,\5>4 \skip 8 }
|
21
|
+
|
22
|
+
vOneBarYouGot = { <g'\1>8 <a\3>8 <g'\1>8 <a\3>16 <g'\1>8 <g\3>16 <e'\1>4 <g\3>16( <a\3>16) }
|
23
|
+
vTwoBarYouGot = { <f\4>4 <f\4>4 <c\5>4 <e\4>4 }
|
24
|
+
|
25
|
+
|
26
|
+
%-----------------------------------------------------------------------
|
27
|
+
% Lines
|
28
|
+
|
29
|
+
vOneLineVerseOneLineTwo = { \vOneBarYouGot \vOneBarWhatYouThought \vOneBarWhenYouFirst \vOneBarItThough \break }
|
30
|
+
vTwoLineVerseOneLineTwo = { \vTwoBarYouGot \vTwoBarWhatYouThought \vTwoBarWhenYouFirst \vTwoBarItThough \break }
|
31
|
+
|
32
|
+
vOneLineTabOneScoreEightLineOne = { \vOneBarItsNot \vOneBarWhatYouThought \vOneBarWhenYouFirst \vOneBarBeganIt \break }
|
33
|
+
vTwoLineTabOneScoreEightLineOne = { \vTwoBarItsNot \vTwoBarWhatYouThought \vTwoBarWhenYouFirst \vTwoBarBeganIt \break }
|
34
|
+
|
35
|
+
|
36
|
+
%-----------------------------------------------------------------------
|
37
|
+
% Scores
|
38
|
+
|
39
|
+
vOneScoreTabOneScoreEight = { \vOneLineTabOneScoreEightLineOne \vOneLineVerseOneLineTwo }
|
40
|
+
vTwoScoreTabOneScoreEight = { \vTwoLineTabOneScoreEightLineOne \vTwoLineVerseOneLineTwo }
|
41
|
+
|
42
|
+
|
43
|
+
%-----------------------------------------------------------------------
|
44
|
+
% Voices
|
45
|
+
|
46
|
+
vOne = { \vOneScoreTabOneScoreEight }
|
47
|
+
|
48
|
+
vTwo = { \vTwoScoreTabOneScoreEight }
|
49
|
+
|
50
|
+
|
51
|
+
\score {
|
52
|
+
\new StaffGroup <<
|
53
|
+
\new Staff <<
|
54
|
+
\tempo 4 = 75
|
55
|
+
\clef "treble_8"
|
56
|
+
|
57
|
+
\new Voice {
|
58
|
+
\voiceOne
|
59
|
+
\vOne
|
60
|
+
}
|
61
|
+
\new Voice {
|
62
|
+
\voiceTwo
|
63
|
+
\vTwo
|
64
|
+
}
|
65
|
+
>>
|
66
|
+
|
67
|
+
\new TabStaff <<
|
68
|
+
\new TabVoice {
|
69
|
+
\slurUp
|
70
|
+
\vOne
|
71
|
+
}
|
72
|
+
\new TabVoice {
|
73
|
+
\slurUp
|
74
|
+
\vTwo
|
75
|
+
}
|
76
|
+
>>
|
77
|
+
>>
|
78
|
+
|
79
|
+
\layout {
|
80
|
+
\context { \Staff
|
81
|
+
\override TimeSignature #'style = #'numbered
|
82
|
+
\override StringNumber #'transparent = ##t
|
83
|
+
}
|
84
|
+
\context { \TabStaff
|
85
|
+
\override TimeSignature #'style = #'numbered
|
86
|
+
}
|
87
|
+
\context { \Voice
|
88
|
+
\remove Slur_engraver
|
89
|
+
}
|
90
|
+
\context { \TabVoice
|
91
|
+
\remove Dots_engraver
|
92
|
+
\remove Stem_engraver
|
93
|
+
\remove Rest_engraver
|
94
|
+
}
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
% showLastLength = R1*4
|
99
|
+
\score {
|
100
|
+
\new Staff \with {midiInstrument = #"acoustic guitar (nylon)"} <<
|
101
|
+
\tempo 4 = 75
|
102
|
+
\clef "treble_8"
|
103
|
+
|
104
|
+
\new Voice {
|
105
|
+
\unfoldRepeats {
|
106
|
+
\vOne
|
107
|
+
}
|
108
|
+
}
|
109
|
+
\new Voice {
|
110
|
+
\unfoldRepeats {
|
111
|
+
\vTwo
|
112
|
+
}
|
113
|
+
}
|
114
|
+
>>
|
115
|
+
|
116
|
+
\midi {}
|
117
|
+
}
|
data/examples/tab.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
Gitara.define do
|
2
|
+
bar :BeganIt do
|
3
|
+
notes "/skip 4 <a/3 c'/2 >8 <g/3>8 /skip 4 <g/3 c'/2 >8 <g/3>8"
|
4
|
+
notes "<f/4>4 <f/4>4 <c/5>4 <e/4>4"
|
5
|
+
end
|
6
|
+
|
7
|
+
bar :ItsNot do
|
8
|
+
notes "r8 <g'/1>4 <a/3>16 <g'/1>8 <g/3>16 <e'/1>4 <g/3>16( <a/3>16)"
|
9
|
+
notes "<f/4>4 <f/4>4 <c/5>4 <e/4>4"
|
10
|
+
end
|
11
|
+
|
12
|
+
bar :ItThough do
|
13
|
+
notes "r8 <d'/2>16( <c'/2>16) r8 <a/3>16 <c'/2>16 r8 <d'/2>16( <c'/2>16) r8 <a/3>8"
|
14
|
+
notes "<f/4>4 <f/4>4 <d/4>4 <d/4>4"
|
15
|
+
end
|
16
|
+
|
17
|
+
bar :WhatYouThought do
|
18
|
+
notes "r8 <g'/1>4 <a/3>16 <g'/1>8 <g/3>16 <e'/1>4 <g/3>8"
|
19
|
+
notes "<f/4>4 <f/4>4 <c/5>4 <e/4>4"
|
20
|
+
end
|
21
|
+
|
22
|
+
bar :WhenYouFirst do
|
23
|
+
notes "<e'/1>8 <d'/2>16( <c'/2>8) <a/3>8. <c'/2>4 <a/3>8 <c'/2>8"
|
24
|
+
notes "<a,/5>4 <a,/5>8 <e/4>4 <a,/5>4 /skip 8"
|
25
|
+
end
|
26
|
+
|
27
|
+
bar :YouGot do
|
28
|
+
notes "<g'/1>8 <a/3>8 <g'/1>8 <a/3>16 <g'/1>8 <g/3>16 <e'/1>4 <g/3>16( <a/3>16)"
|
29
|
+
notes "<f/4>4 <f/4>4 <c/5>4 <e/4>4"
|
30
|
+
end
|
31
|
+
|
32
|
+
line :VerseOneLineTwo do
|
33
|
+
bar :YouGot, :WhatYouThought, :WhenYouFirst, :ItThough
|
34
|
+
end
|
35
|
+
|
36
|
+
score do
|
37
|
+
line do
|
38
|
+
bar :ItsNot, :WhatYouThought, :WhenYouFirst, :BeganIt
|
39
|
+
end
|
40
|
+
|
41
|
+
line :VerseOneLineTwo
|
42
|
+
end
|
43
|
+
end
|
data/gitara.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["George Mendoza"]
|
9
9
|
s.email = ["gsmendoza@gmail.com"]
|
10
10
|
s.homepage = ""
|
11
|
-
s.summary = "
|
12
|
-
s.description = "
|
11
|
+
s.summary = "Ruby + Lilypond = pretty guitar tablatures."
|
12
|
+
s.description = "A Ruby DSL for generating Lilypond guitar tablatures."
|
13
13
|
|
14
14
|
s.rubyforge_project = "gitara"
|
15
15
|
|
@@ -18,7 +18,17 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
s.add_runtime_dependency "active_support"
|
22
|
+
s.add_runtime_dependency "erubis"
|
23
|
+
s.add_runtime_dependency "linguistics"
|
24
|
+
s.add_runtime_dependency "parslet"
|
25
|
+
s.add_runtime_dependency "pow"
|
26
|
+
s.add_runtime_dependency "thor"
|
27
|
+
s.add_runtime_dependency "valuable"
|
28
|
+
|
29
|
+
s.add_development_dependency "guard"
|
30
|
+
s.add_development_dependency "guard-rspec"
|
31
|
+
s.add_development_dependency "pry"
|
32
|
+
s.add_development_dependency "rb-inotify"
|
33
|
+
s.add_development_dependency "rspec"
|
24
34
|
end
|
data/lib/gitara.rb
CHANGED
@@ -1,5 +1,42 @@
|
|
1
|
+
require "active_support/inflector"
|
2
|
+
require "erubis"
|
3
|
+
require "linguistics"
|
4
|
+
require "parslet"
|
5
|
+
require "pow"
|
6
|
+
require "thor"
|
7
|
+
require "valuable"
|
8
|
+
|
9
|
+
require "gitara/app"
|
10
|
+
require "gitara/dsl"
|
11
|
+
require "gitara/id_as_word"
|
12
|
+
require "gitara/node/base"
|
13
|
+
require "gitara/node/base/voiced_node"
|
14
|
+
require "gitara/node/bar"
|
15
|
+
require "gitara/node/bar/voiced_node"
|
16
|
+
require "gitara/node/line"
|
17
|
+
require "gitara/node/note_set"
|
18
|
+
require "gitara/node/score"
|
19
|
+
require "gitara/node/tab"
|
20
|
+
require "gitara/pow/base"
|
1
21
|
require "gitara/version"
|
22
|
+
require "gitara/voice"
|
23
|
+
|
24
|
+
Linguistics::use :en
|
2
25
|
|
3
26
|
module Gitara
|
4
|
-
|
27
|
+
def self.define(&block)
|
28
|
+
@tab = Dsl.new(:node => Node::Tab.new).tap {|dsl|
|
29
|
+
dsl.instance_eval(&block) if block_given?
|
30
|
+
}.node
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.render(path, object)
|
34
|
+
template = (Pow!('gitara/template') / "#{path}.erb")
|
35
|
+
erb = Erubis::Eruby.new(template.read!)
|
36
|
+
erb.evaluate(object)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.tab
|
40
|
+
@tab
|
41
|
+
end
|
5
42
|
end
|
data/lib/gitara/app.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Gitara
|
2
|
+
class App < Thor
|
3
|
+
desc "export PATH [OPTIONS]...", "Export PATH to lilypond and friends."
|
4
|
+
|
5
|
+
method_option "target-directory",
|
6
|
+
:aliases => "-t",
|
7
|
+
:default => '.',
|
8
|
+
:desc => "Export files to TARGET-DIRECTORY",
|
9
|
+
:type => :string
|
10
|
+
|
11
|
+
method_option "run-lilypond",
|
12
|
+
:aliases => "-l",
|
13
|
+
:default => true,
|
14
|
+
:desc => "Run lilypond program after generating lilypond file", :default => true,
|
15
|
+
:type => :boolean
|
16
|
+
|
17
|
+
def export(source_path)
|
18
|
+
load source_path
|
19
|
+
tab = Gitara.tab
|
20
|
+
lilypond_name = Pow(source_path).name(false) + '.ly'
|
21
|
+
lilypond_path = Pow(options['target-directory']) / lilypond_name
|
22
|
+
lilypond_path.write(Gitara.render('tab', tab))
|
23
|
+
|
24
|
+
if options['run-lilypond']
|
25
|
+
`lilypond -o #{lilypond_path.parent / lilypond_path.name(false)} #{lilypond_path}`
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/gitara/dsl.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Gitara
|
2
|
+
class Dsl < Valuable
|
3
|
+
has_value :node
|
4
|
+
|
5
|
+
def add(child, &block)
|
6
|
+
self.node.add child
|
7
|
+
Dsl.new(:node => child).instance_eval(&block) if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_names(options = {}, &block)
|
11
|
+
names = options[:names].empty? ? [nil] : options[:names]
|
12
|
+
node_class = options[:node_class]
|
13
|
+
|
14
|
+
names.map{|name| node_class.new(:name => name) }.each do |child|
|
15
|
+
add child, &block
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def bar(*names, &block)
|
20
|
+
add_names :names => names, :node_class => Node::Bar, &block
|
21
|
+
end
|
22
|
+
|
23
|
+
def line(*names, &block)
|
24
|
+
add_names :names => names, :node_class => Node::Line, &block
|
25
|
+
end
|
26
|
+
|
27
|
+
def notes(value)
|
28
|
+
add Node::NoteSet.new(:value => value)
|
29
|
+
end
|
30
|
+
|
31
|
+
def score(*names, &block)
|
32
|
+
add_names :names => names, :node_class => Node::Score, &block
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Gitara
|
2
|
+
module Node
|
3
|
+
class Base < Valuable
|
4
|
+
include IdAsWord
|
5
|
+
|
6
|
+
has_value :name
|
7
|
+
has_value :id, :default => 1
|
8
|
+
has_value :parent
|
9
|
+
has_value :value
|
10
|
+
|
11
|
+
def add(child)
|
12
|
+
own_children << child
|
13
|
+
child.id = own_children.size
|
14
|
+
child.parent = self
|
15
|
+
end
|
16
|
+
|
17
|
+
def children
|
18
|
+
if own_children.empty?
|
19
|
+
definition ? definition.own_children : []
|
20
|
+
else
|
21
|
+
own_children
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def children=(values)
|
26
|
+
values.each do |child|
|
27
|
+
add child
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def definition?
|
32
|
+
! own_children.empty?
|
33
|
+
end
|
34
|
+
|
35
|
+
def definition_of?(target)
|
36
|
+
self.definition? && self.name == target.name && self.class == target.class
|
37
|
+
end
|
38
|
+
|
39
|
+
def definitions(klass)
|
40
|
+
self.is_a?(klass) && self.definition? ? [self] : self.own_children.map{|child| child.definitions(klass) }.flatten
|
41
|
+
end
|
42
|
+
|
43
|
+
def name
|
44
|
+
attributes[:name] || "#{parent && parent.name}#{self.class.to_s.split('::').last}#{self.id_as_word}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def own_children
|
48
|
+
@children ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
def root
|
52
|
+
parent.nil? ? self : parent.root
|
53
|
+
end
|
54
|
+
|
55
|
+
def value
|
56
|
+
attributes[:value].gsub('/', "\\")
|
57
|
+
end
|
58
|
+
|
59
|
+
def voiced_as(arg)
|
60
|
+
if arg.is_a?(Voice)
|
61
|
+
self.class::VoicedNode.new(:node => self, :voice => arg)
|
62
|
+
elsif arg.is_a?(Array)
|
63
|
+
arg.map{|voice| self.voiced_as(voice) }
|
64
|
+
else
|
65
|
+
raise ArgumentError
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def definition(target = self)
|
72
|
+
if self.definition_of?(target)
|
73
|
+
self
|
74
|
+
else
|
75
|
+
result = parent.own_children.detect{|node| node.definition_of?(target) }
|
76
|
+
result ? result : parent.definition(target)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|