Class: HeadMusic::Content::Composition
- Inherits:
-
Object
- Object
- HeadMusic::Content::Composition
show all
- Defined in:
- lib/head_music/content/composition.rb
Overview
A composition is musical content.
Defined Under Namespace
Classes: HashDeserializer
Constant Summary
collapse
- SCHEMA_VERSION =
1
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#add_comment(text, position = nil) ⇒ Object
-
#add_voice(role: nil) ⇒ Object
-
#bars(last = latest_bar_number) ⇒ Object
-
#bars_to_h ⇒ Object
private
Iterates the raw sparse array (not the public #bars slice, which loses the number offset), pairing each non-default bar with its number.
-
#cantus_firmus_voice ⇒ Object
-
#change_key_signature(bar_number, key_signature) ⇒ Object
-
#change_meter(bar_number, meter) ⇒ Object
-
#counterpoint_voice ⇒ Object
-
#earliest_bar_number ⇒ Object
-
#ensure_attributes(name, key_signature, meter) ⇒ Object
private
-
#first_allocated_bar_number ⇒ Object
private
Bars can be allocated below the voices’ earliest bar (e.g. a key or meter change in a pickup bar), so the earliest bar reflects those allocations too.
-
#initialize(name: nil, key_signature: nil, meter: nil, composer: nil, origin: nil, comments: nil) ⇒ Composition
constructor
A new instance of Composition.
-
#key_signature_at(bar_number) ⇒ Object
-
#last_key_signature_change(bar_number) ⇒ Object
private
-
#last_meter_change(bar_number) ⇒ Object
private
-
#latest_bar_number ⇒ Object
-
#meter_at(bar_number) ⇒ Object
-
#to_abc(**options) ⇒ Object
-
#to_h ⇒ Object
-
#to_json(*_args) ⇒ Object
-
#to_musicxml ⇒ Object
-
#to_s ⇒ Object
Constructor Details
#initialize(name: nil, key_signature: nil, meter: nil, composer: nil, origin: nil, comments: nil) ⇒ Composition
Returns a new instance of Composition.
18
19
20
21
22
23
24
|
# File 'lib/head_music/content/composition.rb', line 18
def initialize(name: nil, key_signature: nil, meter: nil, composer: nil, origin: nil, comments: nil)
ensure_attributes(name, key_signature, meter)
@composer = composer
@origin = origin
@voices = []
@comments = Array().map { |text| HeadMusic::Content::Comment.new(self, text) }
end
|
Instance Attribute Details
Returns the value of attribute comments.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def
@comments
end
|
#composer ⇒ Object
Returns the value of attribute composer.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def composer
@composer
end
|
#key_signature ⇒ Object
Returns the value of attribute key_signature.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def key_signature
@key_signature
end
|
#meter ⇒ Object
Returns the value of attribute meter.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def meter
@meter
end
|
#name ⇒ Object
Returns the value of attribute name.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def name
@name
end
|
#origin ⇒ Object
Returns the value of attribute origin.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def origin
@origin
end
|
#voices ⇒ Object
Returns the value of attribute voices.
8
9
10
|
# File 'lib/head_music/content/composition.rb', line 8
def voices
@voices
end
|
Class Method Details
.from_json(json) ⇒ Object
14
15
16
|
# File 'lib/head_music/content/composition.rb', line 14
def self.from_json(json)
from_h(JSON.parse(json))
end
|
Instance Method Details
31
32
33
34
|
# File 'lib/head_music/content/composition.rb', line 31
def (text, position = nil)
@comments << HeadMusic::Content::Comment.new(self, text, position)
@comments.last
end
|
#add_voice(role: nil) ⇒ Object
26
27
28
29
|
# File 'lib/head_music/content/composition.rb', line 26
def add_voice(role: nil)
@voices << HeadMusic::Content::Voice.new(composition: self, role: role)
@voices.last
end
|
#bars(last = latest_bar_number) ⇒ Object
46
47
48
49
50
51
52
53
|
# File 'lib/head_music/content/composition.rb', line 46
def bars(last = latest_bar_number)
@bars ||= []
first = [earliest_bar_number, last].min
(first..last).each do |bar_number|
@bars[bar_number] ||= HeadMusic::Content::Bar.new(self)
end
@bars[first..last]
end
|
#bars_to_h ⇒ Object
Iterates the raw sparse array (not the public #bars slice, which loses the
number offset), pairing each non-default bar with its number.
135
136
137
138
139
140
141
142
143
144
|
# File 'lib/head_music/content/composition.rb', line 135
def bars_to_h
(@bars || []).each_with_index.filter_map do |bar, number|
next if bar.nil?
bar_hash = bar.to_h
next if bar_hash.empty?
{"number" => number}.merge(bar_hash)
end
end
|
#cantus_firmus_voice ⇒ Object
71
72
73
|
# File 'lib/head_music/content/composition.rb', line 71
def cantus_firmus_voice
voices.detect(&:cantus_firmus?)
end
|
#change_key_signature(bar_number, key_signature) ⇒ Object
55
56
57
|
# File 'lib/head_music/content/composition.rb', line 55
def change_key_signature(bar_number, key_signature)
bars(bar_number).last.key_signature = key_signature
end
|
#change_meter(bar_number, meter) ⇒ Object
59
60
61
|
# File 'lib/head_music/content/composition.rb', line 59
def change_meter(bar_number, meter)
bars(bar_number).last.meter = meter
end
|
#counterpoint_voice ⇒ Object
75
76
77
|
# File 'lib/head_music/content/composition.rb', line 75
def counterpoint_voice
voices.reject(&:cantus_firmus?).first
end
|
#earliest_bar_number ⇒ Object
63
64
65
|
# File 'lib/head_music/content/composition.rb', line 63
def earliest_bar_number
[voices.map(&:earliest_bar_number), first_allocated_bar_number, 1].flatten.compact.min
end
|
#ensure_attributes(name, key_signature, meter) ⇒ Object
#first_allocated_bar_number ⇒ Object
Bars can be allocated below the voices’ earliest bar (e.g. a key or meter
change in a pickup bar), so the earliest bar reflects those allocations too.
113
114
115
|
# File 'lib/head_music/content/composition.rb', line 113
def first_allocated_bar_number
(@bars || []).index { |bar| !bar.nil? }
end
|
#key_signature_at(bar_number) ⇒ Object
41
42
43
44
|
# File 'lib/head_music/content/composition.rb', line 41
def key_signature_at(bar_number)
key_signature_change = last_key_signature_change(bar_number)
key_signature_change ? key_signature_change.key_signature : key_signature
end
|
#last_key_signature_change(bar_number) ⇒ Object
129
130
131
|
# File 'lib/head_music/content/composition.rb', line 129
def last_key_signature_change(bar_number)
bars(bar_number)[earliest_bar_number..bar_number].reverse.detect(&:key_signature)
end
|
#last_meter_change(bar_number) ⇒ Object
124
125
126
127
|
# File 'lib/head_music/content/composition.rb', line 124
def last_meter_change(bar_number)
bar_number = [bar_number, earliest_bar_number].max
bars(bar_number)[earliest_bar_number..bar_number].reverse.detect(&:meter)
end
|
#latest_bar_number ⇒ Object
67
68
69
|
# File 'lib/head_music/content/composition.rb', line 67
def latest_bar_number
[voices.map(&:latest_bar_number), 1].flatten.max
end
|
#meter_at(bar_number) ⇒ Object
36
37
38
39
|
# File 'lib/head_music/content/composition.rb', line 36
def meter_at(bar_number)
meter_change = last_meter_change(bar_number)
meter_change ? meter_change.meter : meter
end
|
#to_abc(**options) ⇒ Object
83
84
85
|
# File 'lib/head_music/content/composition.rb', line 83
def to_abc(**options)
HeadMusic::Notation::ABC.render(self, **options)
end
|
#to_h ⇒ Object
91
92
93
94
95
96
97
98
99
100
101
102
103
|
# File 'lib/head_music/content/composition.rb', line 91
def to_h
{
"schema_version" => SCHEMA_VERSION,
"name" => name,
"key_signature" => key_signature.name,
"meter" => meter.to_s,
"composer" => composer&.to_s,
"origin" => origin&.to_s,
"voices" => voices.map(&:to_h),
"bars" => bars_to_h,
"comments" => .map(&:to_h)
}
end
|
#to_json(*_args) ⇒ Object
105
106
107
|
# File 'lib/head_music/content/composition.rb', line 105
def to_json(*_args)
to_h.to_json
end
|
#to_s ⇒ Object
79
80
81
|
# File 'lib/head_music/content/composition.rb', line 79
def to_s
"#{name} — #{voices.count} #{(voices.count == 1) ? "voice" : "voices"}"
end
|