Module: Lydown::Rendering

Defined in:
lib/lydown/rendering.rb,
lib/lydown/rendering/base.rb,
lib/lydown/rendering/music.rb,
lib/lydown/rendering/notes.rb,
lib/lydown/rendering/staff.rb,
lib/lydown/rendering/layout.rb,
lib/lydown/rendering/lyrics.rb,
lib/lydown/rendering/markup.rb,
lib/lydown/rendering/voices.rb,
lib/lydown/rendering/command.rb,
lib/lydown/rendering/figures.rb,
lib/lydown/rendering/literal.rb,
lib/lydown/rendering/repeats.rb,
lib/lydown/rendering/comments.rb,
lib/lydown/rendering/movement.rb,
lib/lydown/rendering/settings.rb,
lib/lydown/rendering/skipping.rb,
lib/lydown/rendering/source_ref.rb

Defined Under Namespace

Modules: Accidentals, Figures, Layout, Markup, Movement, Notes, Octaves, Staff Classes: Barline, Base, BeamClose, BeamOpen, Chord, Command, Comment, Duration, DurationMacro, FiguresSilence, Grace, Literal, Lyrics, Note, RepeatEnd, RepeatStart, RepeatVolta, Rest, Setting, ShortTie, Silence, SlurClose, SlurOpen, SourceRef, StandAloneFigures, Tie, TupletDuration, VoiceSelect

Constant Summary collapse

VOICE_INDEX =
{
  '1' => 'One',
  '2' => 'Two',
  '3' => 'Three',
  '4' => 'Four'
}
LILYPOND_DURATIONS =
{
  '0' => "\\breve",
  'l' => "\\longa",
  '6' => '16',
  '3' => '32'
}
PROOFING_LY_SETTINGS =
<<EOF
    \\paper {
      indent=0\\mm
      oddFooterMarkup=##f
      oddHeaderMarkup=##f
      bookTitleMarkup = ##f
      scoreTitleMarkup = ##f
      page-breaking = #ly:minimal-breaking %#ly:one-line-auto-height-breaking
      top-margin=10\\mm
      bottom-margin=10\\mm
    }

    \\layout {
      \\context {
        \\Score
        \\remove "Bar_number_engraver"
      }  
    }
EOF
SKIP_GRACE =
{type: :literal, content: "\\grace s8"}
SKIP_ON_CMD =
{type: :literal, content: "\\set Score.skipTypesetting = ##t"}
SKIP_OFF_CMD =
{type: :literal, content: "\\set Score.skipTypesetting = ##f"}
SKIP_BARLINE =
{type: :literal, content: "\\bar \"\""}
SKIP_BAR_NUMBERS_CMD =
{type: :literal, content: "\\override Score.BarNumber.break-visibility = ##(#t #t #t) \
\\set Score.barNumberVisibility = #(every-nth-bar-number-visible 1)"}
HIGHLIGHT_NOTES_CMD =
{type: :literal, content: "\\override NoteHead.color = #red"}
NORMAL_NOTES_CMD =
{type: :literal, content: "\\override NoteHead.color = #black"}

Class Method Summary collapse

Class Method Details

.add_includes(filenames, context, key, opts) ⇒ Object



71
72
73
74
# File 'lib/lydown/rendering.rb', line 71

def add_includes(filenames, context, key, opts)
  includes = context.get_setting(key, opts)
  filenames.concat(includes) if includes
end

.add_requires(packages, context, key, opts) ⇒ Object



109
110
111
112
# File 'lib/lydown/rendering.rb', line 109

def add_requires(packages, context, key, opts)
  requires = context.get_setting(key, opts)
  packages.concat(requires) if requires
end

.class_for_event(e) ⇒ Object



28
29
30
31
32
# File 'lib/lydown/rendering.rb', line 28

def class_for_event(e)
  Lydown::Rendering.const_get(e[:type].to_s.camelize)
rescue
  raise LydownError, "Invalid lydown event: #{e.inspect}"
end

.default_part_title(part_name) ⇒ Object



34
35
36
37
38
39
40
# File 'lib/lydown/rendering.rb', line 34

def default_part_title(part_name)
  if part_name =~ /^([^\d]+)(\d+)$/
    "#{$1.titlize} #{$2.to_i.to_roman}"
  else
    part_name.titlize
  end
end

.find_line_idx(stream, line, last = false) ⇒ Object

returns index of first event at or after specified line



79
80
81
82
83
84
85
86
87
88
# File 'lib/lydown/rendering/skipping.rb', line 79

def find_line_idx(stream, line, last = false)
  if last
    stream.reverse_each do |e|
      return stream.index(e) if e[:line] && e[:line] <= line
    end
    nil
  else
    stream.index {|e| e[:line] && e[:line] >= line}
  end
end

.get_set_variables(context, opts = {}) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/lydown/rendering.rb', line 150

def get_set_variables(context, opts = {})
  set = (context.get_merged_setting_tree(:set, opts) || {}).deep!
  
  case context.render_mode
  when :score
    set.deep_merge!(context.get_merged_setting_tree('score/set', opts) || {})
  when :part
    set.deep_merge!(context.get_merged_setting_tree('parts/set', opts) || {})
    set.deep_merge!(context.get_merged_setting_tree("parts/#{context['render_opts/parts']}/set", opts) || {})
  end
  
  set
end

.include_files(context, opts) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/lydown/rendering.rb', line 76

def include_files(context, opts)
  if context.render_mode == :part
    opts = opts.merge(part: context['render_opts/parts'])
  end
  
  filenames = []
  if opts.has_key?(:movement)
    add_includes(filenames, context, :includes, opts)
    case context.render_mode
    when :score
      add_includes(filenames, context, 'score/includes', opts)
    when :part
      add_includes(filenames, context, 'parts/includes', opts)
      if opts[:part]
        add_includes(filenames, context, "parts/#{opts[:part]}/includes", opts)
      end
    end
  else
    # paths to be included at top of lilypond doc should be defined under
    # document/includes
    add_includes(filenames, context, 'document/includes', opts)
  end
  
  filenames.map do |fn|
    case File.extname(fn)
    when '.ely'
      Lydown::Templates.render(fn, context)
    else
      "\\include \"#{File.expand_path(fn)}\""
    end
  end
end

.insert_skip_markers(stream, line_range) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/lydown/rendering/skipping.rb', line 35

def insert_skip_markers(stream, line_range)
  # find indexes of first and last changed lines
  changed_first_idx = find_line_idx(stream, line_range[0])
  changed_last_idx =  find_line_idx(stream, line_range[1], true)
  
  # find index of first line to include
  start_line = line_range.first - 2; start_line = 0 if start_line < 0
  start_idx = find_line_idx(stream, start_line)
  
  # find index of last line to include
  end_line = line_range.last + 3
  end_idx = find_line_idx(stream, end_line)
  
  if end_idx && end_idx > changed_last_idx
    stream.insert(end_idx, SKIP_ON_CMD)
    stream.insert(end_idx, SKIP_GRACE)
  end
  
  if changed_last_idx
    stream.insert(changed_last_idx + 1, NORMAL_NOTES_CMD)
  end
  
  if changed_first_idx
    stream.insert(changed_first_idx, HIGHLIGHT_NOTES_CMD)
  end
  
  if start_line > 0 && start_idx
    # Insert an invisible barline (in order to show bar number for first
    # bar), and show bar numbers on every subsequent barline.
    stream.insert(start_idx, SKIP_BARLINE)
    stream.insert(start_idx, SKIP_BAR_NUMBERS_CMD)

    stream.insert(start_idx, SKIP_OFF_CMD)
    # "Help" lilypond correctly render rhythms when skipping on/off by
    # inserting a silent grace note.
    # See https://code.google.com/p/lilypond/issues/detail?id=1543&q=skiptypesetting&colspec=ID%20Type%20Status%20Stars%20Owner%20Patch%20Needs%20Summary
    stream.insert(start_idx, SKIP_GRACE)

    # Skip beginning
    stream.insert(0, SKIP_ON_CMD)
  end
end

.layout_info(context, opts = {}) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/lydown/rendering.rb', line 136

def layout_info(context, opts = {})
  layout = (context.get_merged_setting_tree(:layout, opts) || {}).deep!
  
  case context.render_mode
  when :score
    layout.deep_merge!(context.get_merged_setting_tree('score/layout', opts) || {})
  when :part
    layout.deep_merge!(context.get_merged_setting_tree('parts/layout', opts) || {})
    layout.deep_merge!(context.get_merged_setting_tree("parts/#{context['render_opts/parts']}/layout", opts) || {})
  end
  
  layout
end

.lyrics_markup(context, opts = {}) ⇒ Object



164
165
166
167
168
169
170
# File 'lib/lydown/rendering.rb', line 164

def lyrics_markup(context, opts = {})
  if context.render_mode == :part
    opts = opts.merge(part: context['render_opts/parts'])
  end
  
  context.get_setting('lyrics_markup', opts)
end

.packages_to_load(context, opts) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/lydown/rendering.rb', line 114

def packages_to_load(context, opts)
  packages = []
  if opts.has_key?(:movement)
    add_requires(packages, context, :requires, opts)
    case context.render_mode
    when :score
      add_requires(packages, context, 'score/requires', opts)
    when :part
      add_requires(packages, context, 'parts/requires', opts)
      if opts[:part]
        add_requires(packages, context, "parts/#{opts[:part]}/requires", opts)
      end
    end
  else
    # paths to be included at top of lilypond doc should be defined under
    # document/requires
    add_requires(packages, context, 'document/requires', opts)
  end
  
  packages.uniq
end

.translate(work, e, lydown_stream, idx) ⇒ Object



23
24
25
26
# File 'lib/lydown/rendering.rb', line 23

def translate(work, e, lydown_stream, idx)
  klass = class_for_event(e)
  klass.new(e, work, lydown_stream, idx).translate
end

.variable_name(opts) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/lydown/rendering.rb', line 58

def variable_name(opts)
  varname = "%s/%s/%s" % [
    opts[:movement],
    opts[:part],
    opts[:stream]
  ]
  
  varname << "/#{opts[:voice]}" if opts[:voice]
  varname << "/#{opts[:idx]}" if opts[:idx]
  
  "\"#{varname}\""
end

.variable_name_infix(infix) ⇒ Object



42
43
44
45
# File 'lib/lydown/rendering.rb', line 42

def variable_name_infix(infix)
  infix.capitalize.gsub(/(\d+)/) {|n| n.to_i.to_roman.upcase}.
    gsub(/[^a-zA-Z]/, '').gsub(/\s+/, '')
end

.voice_variable_name_infix(infix) ⇒ Object



54
55
56
# File 'lib/lydown/rendering.rb', line 54

def voice_variable_name_infix(infix)
  infix.capitalize.gsub(/(\d+)/) {|n| VOICE_INDEX[n]}
end