Class: Musicality::Note

Inherits:
Object
  • Object
show all
Includes:
Parseable, Packable, Validatable
Defined in:
lib/musicality/notation/model/note.rb,
lib/musicality/printing/lilypond/note_engraving.rb,
lib/musicality/notation/parsing/convenience_methods.rb

Constant Summary collapse

SMALLEST_PIECE =
Rational(1,256)
PARSER =
Parsing::NoteParser.new
CONVERSION_METHOD =
:to_note

Constants included from Parseable

Parseable::DEFAULT_SPLIT_PATTERN

Constants included from Packable

Packable::PACKED_CLASS_KEY

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Parseable

included

Methods included from Validatable

#errors, #invalid?, #valid?, #validatables, #validate

Methods included from Packable

#class_str, included, #init_params, #pack, pack_val, recover_class, unpack_val

Constructor Details

#initialize(duration, pitches = [], links: {}, articulation: Articulations::NORMAL, marks: []) ⇒ Note

Returns a new instance of Note.



12
13
14
15
16
17
18
19
20
21
# File 'lib/musicality/notation/model/note.rb', line 12

def initialize duration, pitches = [], links: {}, articulation: Articulations::NORMAL, marks: []
  self.duration = duration
  if !pitches.is_a? Enumerable
    pitches = [ pitches ]
  end
  @pitches = Set.new(pitches).sort
  @links = links
  @articulation = articulation
  @marks = marks
end

Instance Attribute Details

#articulationObject

Returns the value of attribute articulation.



10
11
12
# File 'lib/musicality/notation/model/note.rb', line 10

def articulation
  @articulation
end

#durationObject

Returns the value of attribute duration.



9
10
11
# File 'lib/musicality/notation/model/note.rb', line 9

def duration
  @duration
end

Returns the value of attribute links.



9
10
11
# File 'lib/musicality/notation/model/note.rb', line 9

def links
  @links
end

#marksObject (readonly)

Returns the value of attribute marks.



9
10
11
# File 'lib/musicality/notation/model/note.rb', line 9

def marks
  @marks
end

#pitchesObject (readonly)

Returns the value of attribute pitches.



9
10
11
# File 'lib/musicality/notation/model/note.rb', line 9

def pitches
  @pitches
end

Class Method Details

.add_note_method(name, dur) ⇒ Object



126
127
128
129
130
# File 'lib/musicality/notation/model/note.rb', line 126

def self.add_note_method(name, dur)
  self.class.send(:define_method,name.to_sym) do |pitches = [], links: {}, articulation: Articulations::NORMAL, marks: []|
    Note.new(dur, pitches, articulation: articulation, links: links, marks: marks)
  end
end

Instance Method Details

#==(other) ⇒ Object



40
41
42
43
44
45
46
# File 'lib/musicality/notation/model/note.rb', line 40

def == other
  return (@duration == other.duration) &&
  (self.pitches == other.pitches) &&
  (@links.to_a.sort == other.links.to_a.sort) &&
  (@articulation == other.articulation) &&
  (@marks == marks)
end

#begins_slur?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/musicality/notation/model/note.rb', line 92

def begins_slur?
  marks.count {|m| m.is_a?(Mark::Slur::Begin) } > 0
end

#check_durationObject



27
28
29
30
31
# File 'lib/musicality/notation/model/note.rb', line 27

def check_duration
  if duration <= 0
    raise RangeError, "Duration is non-positive: #{duration}"
  end
end

#check_methodsObject



23
24
25
# File 'lib/musicality/notation/model/note.rb', line 23

def check_methods
  [ :check_duration, :check_pitches ]
end

#check_pitchesObject



33
34
35
36
37
38
# File 'lib/musicality/notation/model/note.rb', line 33

def check_pitches
  non_pitches = @pitches.select {|p| !p.is_a?(Pitch) }
  if non_pitches.any?
    raise TypeError, "Found non-pitches: #{non_pitches}"
  end
end

#cloneObject



48
49
50
# File 'lib/musicality/notation/model/note.rb', line 48

def clone
  Marshal.load(Marshal.dump(self))
end

#ends_slur?Boolean

Returns:

  • (Boolean)


96
97
98
# File 'lib/musicality/notation/model/note.rb', line 96

def ends_slur?
  marks.count {|m| m.is_a?(Mark::Slur::End) } > 0
end

#fractional_subdurs(smallest_piece) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/musicality/printing/lilypond/note_engraving.rb', line 77

def fractional_subdurs smallest_piece
  remaining = @duration - @duration.to_i    
  pieces = []
  i = 0
  while((current_dur = Rational(1,2<<i)) >= smallest_piece)
    if remaining >= current_dur
      pieces.push current_dur
      remaining -= current_dur
    end
    i += 1
  end

  unless remaining.zero?
    raise RuntimeError, "Non-zero remainder #{remaining}"
  end

  return pieces
end

#mark_accented!Object



76
77
78
# File 'lib/musicality/notation/model/note.rb', line 76

def mark_accented!
  @articulation = Articulations::ACCENT
end

#resize(duration) ⇒ Object



58
59
60
61
62
# File 'lib/musicality/notation/model/note.rb', line 58

def resize duration
  new_note = self.clone
  new_note.duration = duration
  return new_note
end

#tie_to(pitches) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/musicality/notation/model/note.rb', line 64

def tie_to pitches
  new_note = self.clone
  if pitches.is_a? Pitch
    pitches = [pitches]
  end

  pitches.each do |pitch|
    new_note.links[pitch] = Link::Tie.new
  end
  return new_note
end

#to_lilypond(sharpit = false, begins_triplet: false, ends_triplet: false) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
# File 'lib/musicality/printing/lilypond/note_engraving.rb', line 6

def to_lilypond sharpit = false, begins_triplet: false, ends_triplet: false
  subdurs = [1]*@duration.to_i + fractional_subdurs(SMALLEST_PIECE)

  piece_strs = []
  pitches_to_strs = Hash[ pitches.map {|p| [p,p.to_lilypond(sharpit)] }]
  while subdurs.any?
    subdur = subdurs.shift
    dur_str = subdur.denominator.to_s
    if subdurs.any? && subdur == subdurs.first*2
      dur_str += "."
      subdurs.shift
    end
    last = subdurs.empty?

    piece_str = if pitches.any?
      if last
        # figure if ties are needed on per-pitch basis, based on note links
        if pitches_to_strs.size == 1
          p, p_str = pitches_to_strs.first
          needs_tie = links.include?(p) && links[p].is_a?(Link::Tie)
          p_str + dur_str + (needs_tie ? "~" : "")
        else
          p_strs = pitches_to_strs.map do |p,p_str|
            if links.include?(p) && links[p].is_a?(Link::Tie)
              p_str + "~"
            else
              p_str
            end
          end
          "<#{p_strs.join(" ")}>" + dur_str
        end
      else
        str = if pitches.size == 1
          pitches_to_strs.values.first
        else
          "<#{pitches_to_strs.values.join(" ")}>"
        end
        str + dur_str + "~"
      end
    else
      "r" + dur_str
    end

    piece_strs.push piece_str
  end

  if pitches.any?
    if articulation != Articulations::NORMAL
      piece_strs[0] += "-" + ARTICULATION_SYMBOLS[articulation]
    end

    if begins_slur?
      piece_strs[-1] += MARK_SYMBOLS[Mark::Slur::Begin]
    end

    if ends_slur?
      piece_strs[-1] += MARK_SYMBOLS[Mark::Slur::End]
    end
  end

  if begins_triplet
    piece_strs[0].prepend("\\tuplet 3/2 {")
  end

  if ends_triplet
    piece_strs[-1].concat("}")
  end

  return piece_strs.join(" ")
end

#to_sObject



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/musicality/notation/model/note.rb', line 100

def to_s
  d = @duration.to_r
  if d.denominator == 1
    dur_str = "#{d.numerator}"
  elsif d.numerator == 1
    dur_str = "/#{d.denominator}"
  else
    dur_str = d.to_s
  end

  pitch_links_str = @pitches.map do |p|
    if @links.has_key?(p)
      p.to_s + @links[p].to_s
    else
      p.to_s
    end
  end.join(",")

  art_str = ARTICULATION_SYMBOLS[@articulation] || ""

  begin_marks_str = marks.select {|m| m.begins? }.map {|m| m.to_s }.join
  end_marks_str = marks.select {|m| m.ends? }.map {|m| m.to_s }.join

  return begin_marks_str + dur_str + pitch_links_str + art_str + end_marks_str
end

#transpose(diff) ⇒ Object



80
81
82
# File 'lib/musicality/notation/model/note.rb', line 80

def transpose diff
  self.clone.transpose! diff
end

#transpose!(diff) ⇒ Object



84
85
86
87
88
89
90
# File 'lib/musicality/notation/model/note.rb', line 84

def transpose! diff
  @pitches = @pitches.map {|pitch| pitch.transpose(diff) }
  @links = Hash[ @links.map do |k,v|
    [ k.transpose(diff), v.transpose(diff) ]
  end ]
  return self
end