Class: HeadMusic::Time::KeySignatureEvent

Inherits:
Object
  • Object
show all
Defined in:
lib/head_music/time/key_signature_event.rb

Overview

A key signature change at a musical position.

The event carries two things, because neither derives the other:

  • the signature, as fifths (sharps positive, flats negative), which is what is printed at the clef;
  • optionally a tonal context, a Key or a Mode, which is the analytical claim about what that signature means.

A signature underdetermines its interpretation -- two sharps is D major, B minor, E dorian, or A mixolydian -- so the interpretation is optional. And the interpretation does not determine the signature either, because the two legitimately diverge: C dorian written in cantus mollis takes the parallel minor's three flats and naturalizes the sixth, so the collection is two flats while the signature is three.

Fifths rather than a KeySignature because a KeySignature cannot be built from a bare signature -- naming three flats means naming an interpretation of it, after which the stored tonic and quality are wrong whenever the tonal context disagrees. Fifths is also exactly what MusicXML stores.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(position, signature, tonal_context: nil) ⇒ KeySignatureEvent

Returns a new instance of KeySignatureEvent.

Parameters:

Raises:

  • (ArgumentError)


42
43
44
45
46
47
48
# File 'lib/head_music/time/key_signature_event.rb', line 42

def initialize(position, signature, tonal_context: nil)
  raise ArgumentError, "signature must be an Integer number of fifths, got #{signature.inspect}" unless signature.is_a?(Integer)

  @position = position
  @signature = signature
  @tonal_context = tonal_context
end

Instance Attribute Details

#position ⇒ MusicalPosition

Returns where the change occurs.

Returns:



28
29
30
# File 'lib/head_music/time/key_signature_event.rb', line 28

def position
  @position
end

#signature ⇒ Integer (readonly)

Returns fifths: sharps positive, flats negative.

Returns:

  • (Integer) —

    fifths: sharps positive, flats negative



31
32
33
# File 'lib/head_music/time/key_signature_event.rb', line 31

def signature
  @signature
end

#tonal_context ⇒ HeadMusic::Rudiment::DiatonicContext, ... (readonly)

The interpretation of the signature: usually a Key or a Mode, and a KeySignature where the scale type is one neither subclass can hold.



37
38
39
# File 'lib/head_music/time/key_signature_event.rb', line 37

def tonal_context
  @tonal_context
end

Class Method Details

.fifths_of(key_signature) ⇒ Integer

Returns the fifths a key signature prints.

Returns:

  • (Integer) —

    the fifths a key signature prints



79
80
81
# File 'lib/head_music/time/key_signature_event.rb', line 79

def self.fifths_of(key_signature)
  key_signature.num_sharps - key_signature.num_flats
end

Instance Method Details

#agreeing_interpretation ⇒ Object (private)



89
90
91
92
93
# File 'lib/head_music/time/key_signature_event.rb', line 89

def agreeing_interpretation
  return nil if tonal_context.nil?

  key_signature if self.class.fifths_of(key_signature) == signature
end

#conventional_reading ⇒ Object (private)



106
107
108
# File 'lib/head_music/time/key_signature_event.rb', line 106

def conventional_reading
  HeadMusic::Rudiment::KeySignature.get(HeadMusic::Rudiment::Key.for_fifths(signature).name)
end

#key_signature ⇒ HeadMusic::Rudiment::KeySignature

The key signature in the sense the rest of the gem means it: the collection plus, where one was claimed, the tonic and scale type that a style guideline needs in order to know what the first degree is.

Falls back to the conventional reading of the signature when no interpretation was given.



58
59
60
# File 'lib/head_music/time/key_signature_event.rb', line 58

def key_signature
  @key_signature ||= tonal_context ? HeadMusic::Rudiment::KeySignature.get(tonal_context.name) : printed_key_signature
end

#printed_key_signature ⇒ HeadMusic::Rudiment::KeySignature

What is printed at the clef.

The interpretation, where it agrees with the signature -- so a flow in D dorian still prints as dorian, which LilyPond can say.

Where the two diverge the signature wins, because the signature is by definition what is printed. The tonic is kept if an ordinary key on it prints that signature, so C dorian in cantus mollis prints as C minor rather than as its relative major; failing that, the conventional reading of the signature is used.



74
75
76
# File 'lib/head_music/time/key_signature_event.rb', line 74

def printed_key_signature
  @printed_key_signature ||= agreeing_interpretation || same_tonic_key || conventional_reading
end

#same_tonic_key ⇒ Object (private)

An ordinary major or minor key on the interpretation's own tonic that prints this signature, which keeps the tonic the music is actually in.



97
98
99
100
101
102
103
104
# File 'lib/head_music/time/key_signature_event.rb', line 97

def same_tonic_key
  return nil if tonal_context.nil?

  HeadMusic::Rudiment::Key::QUALITIES.filter_map { |quality|
    candidate = HeadMusic::Rudiment::KeySignature.get("#{tonal_context.tonic_spelling} #{quality}")
    candidate if self.class.fifths_of(candidate) == signature
  }.first
end

#to_s ⇒ Object



83
84
85
# File 'lib/head_music/time/key_signature_event.rb', line 83

def to_s
  [signature, tonal_context].compact.join(" ")
end