Class: HeadMusic::Instruments::Instrument

Inherits:
Object
  • Object
show all
Includes:
Named
Defined in:
lib/head_music/instruments/instrument.rb

Overview

A specific musical instrument instance with a selected variant. Represents an actual playable instrument with its transposition and configuration.

Examples: trumpet_in_c = HeadMusic::Instruments::Instrument.get("trumpet_in_c") trumpet_in_c = HeadMusic::Instruments::Instrument.get("trumpet", "in_c") clarinet = HeadMusic::Instruments::Instrument.get("clarinet") # uses default Bb variant

Attributes accessible via delegation to instrument_type and variant: name: display name including variant (e.g. "Trumpet in C") transposition: sounding transposition in semitones clefs: array of clefs for this instrument pitch_designation: the pitch designation for transposing instruments

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(instrument_type, variant) ⇒ Instrument

Returns a new instance of Instrument.



39
40
41
42
43
# File 'lib/head_music/instruments/instrument.rb', line 39

def initialize(instrument_type, variant)
  @instrument_type = instrument_type
  @variant = variant
  initialize_name
end

Instance Attribute Details

#alias_name_keysObject (readonly) Originally defined in module Named

Returns the value of attribute alias_name_keys.

#instrument_typeObject (readonly)

Returns the value of attribute instrument_type.



20
21
22
# File 'lib/head_music/instruments/instrument.rb', line 20

def instrument_type
  @instrument_type
end

#name_keyObject (readonly) Originally defined in module Named

Returns the value of attribute name_key.

#variantObject (readonly)

Returns the value of attribute variant.



20
21
22
# File 'lib/head_music/instruments/instrument.rb', line 20

def variant
  @variant
end

Class Method Details

.find_variant(instrument_type, variant_key) ⇒ Object (private)



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/head_music/instruments/instrument.rb', line 148

def self.find_variant(instrument_type, variant_key)
  return instrument_type.default_variant unless variant_key

  # Convert to symbol for comparison
  variant_sym = variant_key.to_sym

  # Find the variant by key
  variants = instrument_type.variants || []
  variant = variants.find { |v| v.key == variant_sym }
  variant || instrument_type.default_variant
end

.get(type_or_name, variant_key = nil) ⇒ Instrument

Factory method to get an Instrument instance

Parameters:

  • type_or_name (String, Symbol)

    instrument type name or full name with variant

  • variant_key (String, Symbol, nil) (defaults to: nil)

    optional variant key if not included in name

Returns:

  • (Instrument)

    instrument instance with specified or default variant



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/head_music/instruments/instrument.rb', line 26

def self.get(type_or_name, variant_key = nil)
  return type_or_name if type_or_name.is_a?(self)

  type_name, parsed_variant_key = parse_instrument_name(type_or_name)
  variant_key ||= parsed_variant_key

  instrument_type = HeadMusic::Instruments::InstrumentType.get(type_name)
  return nil unless instrument_type&.name_key

  variant = find_variant(instrument_type, variant_key)
  new(instrument_type, variant)
end

.parse_instrument_name(name) ⇒ Object (private)



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/head_music/instruments/instrument.rb', line 118

def self.parse_instrument_name(name)
  name_str = name.to_s

  # Check for variant patterns like "trumpet_in_e_flat"
  if name_str =~ /(.+)_in_([a-g])_(flat|sharp)$/i
    type_name = Regexp.last_match(1)
    note = Regexp.last_match(2).downcase
    accidental = Regexp.last_match(3)
    variant_key = :"in_#{note}_#{accidental}"
    [type_name, variant_key]
  # Check for variant patterns like "trumpet_in_c" or "clarinet_in_a" or "trumpet_in_eb"
  elsif name_str =~ /(.+)_in_([a-g][b#]?)$/i
    type_name = Regexp.last_match(1)
    variant_note = Regexp.last_match(2).downcase
    # Convert "eb" to "e_flat", "bb" to "b_flat", etc.
    if variant_note.end_with?("b") && variant_note.length == 2
      note_letter = variant_note[0]
      variant_key = :"in_#{note_letter}_flat"
    elsif variant_note.end_with?("#") && variant_note.length == 2
      note_letter = variant_note[0]
      variant_key = :"in_#{note_letter}_sharp"
    else
      variant_key = :"in_#{variant_note}"
    end
    [type_name, variant_key]
  else
    [name_str, nil]
  end
end

Instance Method Details

#==(other) ⇒ Object



88
89
90
91
92
# File 'lib/head_music/instruments/instrument.rb', line 88

def ==(other)
  return false unless other.is_a?(self.class)

  instrument_type == other.instrument_type && variant == other.variant
end

#default_clefsObject



56
57
58
# File 'lib/head_music/instruments/instrument.rb', line 56

def default_clefs
  default_staves&.map(&:clef) || []
end

#default_stavesObject



52
53
54
# File 'lib/head_music/instruments/instrument.rb', line 52

def default_staves
  default_staff_scheme&.staves || []
end

#ensure_localized_name(name:, locale_code: Locale::DEFAULT_CODE, abbreviation: nil) ⇒ Object Originally defined in module Named

#format_pitch_name(pitch_designation) ⇒ Object (private)



112
113
114
115
116
# File 'lib/head_music/instruments/instrument.rb', line 112

def format_pitch_name(pitch_designation)
  # Format the pitch designation for display
  # e.g. "Bb" -> "B♭", "C" -> "C", "Eb" -> "E♭"
  pitch_designation.to_s.gsub("b", "").gsub("#", "")
end

#initialize_nameObject (private)



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/head_music/instruments/instrument.rb', line 100

def initialize_name
  if variant.default? || !pitch_designation
    self.name = instrument_type.name
  elsif pitch_designation
    pitch_name = format_pitch_name(pitch_designation)
    self.name = "#{instrument_type.name} in #{pitch_name}"
  else
    variant_name = variant.key.to_s.tr("_", " ")
    self.name = "#{instrument_type.name} (#{variant_name})"
  end
end

#localized_name(locale_code: Locale::DEFAULT_CODE) ⇒ Object Originally defined in module Named

#localized_name_in_default_localeObject (private) Originally defined in module Named

#localized_name_in_locale_matching_language(locale) ⇒ Object (private) Originally defined in module Named

#localized_name_in_matching_locale(locale) ⇒ Object (private) Originally defined in module Named

#localized_namesObject Originally defined in module Named

Returns an array of LocalizedName instances that are synonymous with the name.

#multiple_staves?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/head_music/instruments/instrument.rb', line 78

def multiple_staves?
  default_staves.length > 1
end

#name(locale_code: Locale::DEFAULT_CODE) ⇒ Object Originally defined in module Named

#name=(name) ⇒ Object Originally defined in module Named

#pitched?Boolean

Returns:

  • (Boolean)


82
83
84
85
86
# File 'lib/head_music/instruments/instrument.rb', line 82

def pitched?
  return false if default_clefs.compact.uniq == [HeadMusic::Rudiment::Clef.get("neutral_clef")]

  default_clefs.any?
end

#single_staff?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/head_music/instruments/instrument.rb', line 74

def single_staff?
  default_staves.length == 1
end

#sounding_transpositionObject Also known as: default_sounding_transposition



60
61
62
# File 'lib/head_music/instruments/instrument.rb', line 60

def sounding_transposition
  default_staves&.first&.sounding_transposition || 0
end

#to_sObject



94
95
96
# File 'lib/head_music/instruments/instrument.rb', line 94

def to_s
  name
end

#transposing?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'lib/head_music/instruments/instrument.rb', line 66

def transposing?
  sounding_transposition != 0
end

#transposing_at_the_octave?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'lib/head_music/instruments/instrument.rb', line 70

def transposing_at_the_octave?
  transposing? && sounding_transposition % 12 == 0
end