Class: HeadMusic::Style::Guidelines::MixSpeciesTextures

Inherits:
HeadMusic::Style::Guideline show all
Includes:
HeadMusic::Style::Guideline::BarSpan
Defined in:
lib/head_music/style/guidelines/mix_species_textures.rb

Overview

Fifth species is the free mixture of the other four, so no one of their textures may run on: Salzer and Schachter allow "two, or at most, two-and-a-half measures of a single note value" (pp. 101-102). Configurable via the maximum_run: option.

Strong, although MostlyConjunct's precedent makes a proportion weak: a proportion of steps measures the character of a line that is already some species, while a run of one texture decides whether the line is fifth species at all.

Constant Summary collapse

MAXIMUM_RUN =
2
SINGLE_SPECIES_TEXTURES =
i[whole half quarter ligature].freeze

Instance Method Summary collapse

Constructor Details

This class inherits a constructor from HeadMusic::Style::Guideline

Instance Method Details

#attack_texture(attacks, bar_number) ⇒ Object (private)

A note that fills its bar is the first-species texture however it is notated, so the dotted whole of a triple-meter cantus counts.



69
70
71
72
73
74
75
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 69

def attack_texture(attacks, bar_number)
  return :whole if attacks.one? && fills_bar?(attacks.first, bar_number)
  return :half if attacks.all? { |note| undotted?(note, "half") }
  return :quarter if attacks.all? { |note| undotted?(note, "quarter") || undotted?(note, "eighth") }

  :florid
end

#body_bar_numbers ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#classify(bar_number) ⇒ Object (private)

A mixed bar is florid; an empty bar is the rest rules' fault. Either one ends a run without counting toward one.



45
46
47
48
49
50
51
52
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 45

def classify(bar_number)
  tail = tail_into(bar_number)
  attacks = notes_in_bar(bar_number)
  return tail ? tail_texture(tail, attacks, bar_number) : :empty if attacks.empty?
  return tail_texture(tail, attacks, bar_number) if tail

  attack_texture(attacks, bar_number)
end

#downbeat_of(bar_number) ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#fills_bar?(note, bar_number) ⇒ Boolean (private)

Returns:

  • (Boolean)


77
78
79
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 77

def fills_bar?(note, bar_number)
  note.position == downbeat_of(bar_number) && note.next_position >= downbeat_of(bar_number + 1)
end

#first_bar_number ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#last_bar_number ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#long_runs ⇒ Object (private)

The last bar is a whole note by another rule, so it is not counted.



32
33
34
35
36
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 32

def long_runs
  body_bar_numbers
    .chunk_while { |bar_number, next_bar_number| texture(bar_number) == texture(next_bar_number) }
    .select { |run| SINGLE_SPECIES_TEXTURES.include?(texture(run.first)) && run.length > maximum_run }
end

#mark_bar(bar_number) ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#marks ⇒ Object



19
20
21
22
23
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 19

def marks
  return [] if notes.empty?

  long_runs.flatten.map { |bar_number| mark_bar(bar_number) }
end

#maximum_run ⇒ Object (private)



27
28
29
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 27

def maximum_run
  options.fetch(:maximum_run) { self.class::MAXIMUM_RUN }
end

#middle_bar_numbers ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#notes_in_bar(bar_number) ⇒ Object (protected) Originally defined in module HeadMusic::Style::Guideline::BarSpan

#tail_into(bar_number) ⇒ Object (private)



54
55
56
57
58
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 54

def tail_into(bar_number)
  downbeat = downbeat_of(bar_number)
  held = voice.note_at(downbeat)
  held if held && held.position < downbeat
end

#tail_texture(tail, attacks, bar_number) ⇒ Object (private)



60
61
62
63
64
65
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 60

def tail_texture(tail, attacks, bar_number)
  return :whole if attacks.empty? && tail.next_position >= downbeat_of(bar_number + 1)
  return :ligature if attacks.any? && attacks.all? { |note| undotted?(note, "half") }

  :florid
end

#texture(bar_number) ⇒ Object (private)



38
39
40
41
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 38

def texture(bar_number)
  @textures ||= {}
  @textures[bar_number] ||= classify(bar_number)
end

#undotted?(note, unit_name) ⇒ Boolean (private)

Returns:

  • (Boolean)


81
82
83
# File 'lib/head_music/style/guidelines/mix_species_textures.rb', line 81

def undotted?(note, unit_name)
  note.rhythmic_value.unit_name == unit_name && note.rhythmic_value.dots.zero?
end