head_music 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/TODO.md +105 -1
- data/lib/head_music/data/clefs.yml +3 -1
- data/lib/head_music/data/instrument_families.yml +229 -0
- data/lib/head_music/data/instruments.yml +835 -116
- data/lib/head_music/instrument.rb +90 -10
- data/lib/head_music/instrument_family.rb +69 -0
- data/lib/head_music/locales/de.yml +3 -3
- data/lib/head_music/locales/en.yml +6 -8
- data/lib/head_music/locales/es.yml +2 -2
- data/lib/head_music/locales/fr.yml +1 -1
- data/lib/head_music/locales/it.yml +3 -3
- data/lib/head_music/locales/ru.yml +2 -2
- data/lib/head_music/staff.rb +4 -1
- data/lib/head_music/version.rb +1 -1
- data/lib/head_music.rb +1 -1
- metadata +5 -4
- data/lib/head_music/grand_staff.rb +0 -56
@@ -1,23 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
3
|
+
# A musical instrument.
|
4
|
+
# An instrument object can be assigned to a staff object.
|
5
|
+
# Attributes:
|
6
|
+
# name_key: the name of the instrument
|
7
|
+
# alias_name_keys: an array of alternative names for the instrument
|
8
|
+
# orchestra_section_key: the section of the orchestra (e.g. "strings")
|
9
|
+
# family_key: the key for the family of the instrument (e.g. "saxophone")
|
10
|
+
# classification_keys: an array of classification_keys
|
11
|
+
# transposition: the number of semitones between the written and the sounding pitch (optional, default: 0)
|
12
|
+
# default_clefs: the default clef or system of clefs for the instrument
|
13
|
+
# - [treble] for instruments that use the treble clef
|
14
|
+
# - [treble, bass] for instruments that use the grand staff
|
15
|
+
# notation:
|
16
|
+
# a hash of default and alternative notation systems,
|
17
|
+
# each with a staffs key with an array of hashes
|
18
|
+
# including clef and transposition (where applicable)
|
19
|
+
# Associations:
|
20
|
+
# family: the family of the instrument (e.g. "saxophone")
|
21
|
+
# orchestra_section: the section of the orchestra (e.g. "strings")
|
4
22
|
class HeadMusic::Instrument
|
5
23
|
include HeadMusic::Named
|
6
24
|
|
7
25
|
INSTRUMENTS = YAML.load_file(File.expand_path("data/instruments.yml", __dir__)).freeze
|
8
26
|
|
9
27
|
def self.get(name)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
new(name)
|
28
|
+
result = get_by_name(name) || get_by_name(key_for_name(name)) || get_by_alias(name)
|
29
|
+
result || new(name)
|
14
30
|
end
|
15
31
|
|
16
32
|
def self.all
|
17
|
-
|
33
|
+
HeadMusic::InstrumentFamily.all
|
34
|
+
@all ||=
|
35
|
+
INSTRUMENTS.map { |key, _data| get(key) }.sort_by(&:name)
|
18
36
|
end
|
19
37
|
|
20
|
-
attr_reader
|
38
|
+
attr_reader(
|
39
|
+
:name_key, :alias_name_keys,
|
40
|
+
:family_key, :orchestra_section_key,
|
41
|
+
:notation, :classification_keys,
|
42
|
+
:fundamental_pitch_spelling, :transposition,
|
43
|
+
:default_staffs, :default_clefs
|
44
|
+
)
|
21
45
|
|
22
46
|
def ==(other)
|
23
47
|
to_s == other.to_s
|
@@ -29,6 +53,36 @@ class HeadMusic::Instrument
|
|
29
53
|
I18n.translate(name_key, scope: [:instruments], locale: locale)
|
30
54
|
end
|
31
55
|
|
56
|
+
def family
|
57
|
+
return unless family_key
|
58
|
+
|
59
|
+
HeadMusic::InstrumentFamily.get(family_key)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if the instrument sounds at a different pitch than written.
|
63
|
+
def transposing?
|
64
|
+
transposition != 0
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns true if the instrument sounds at a different register than written.
|
68
|
+
def transposing_at_the_octave?
|
69
|
+
transposing? && transposition % 12 == 0
|
70
|
+
end
|
71
|
+
|
72
|
+
def single_staff?
|
73
|
+
default_staffs.length == 1
|
74
|
+
end
|
75
|
+
|
76
|
+
def multiple_staffs?
|
77
|
+
default_staffs.length > 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def pitched?
|
81
|
+
return false if default_clefs.compact.uniq == ["percussion"]
|
82
|
+
|
83
|
+
default_clefs.any?
|
84
|
+
end
|
85
|
+
|
32
86
|
private_class_method :new
|
33
87
|
|
34
88
|
private
|
@@ -65,11 +119,37 @@ class HeadMusic::Instrument
|
|
65
119
|
end
|
66
120
|
|
67
121
|
def initialize_data_from_record(record)
|
122
|
+
initialize_family(record)
|
123
|
+
inherit_family_attributes(record)
|
124
|
+
initialize_names(record)
|
125
|
+
initialize_attributes(record)
|
126
|
+
end
|
127
|
+
|
128
|
+
def initialize_family(record)
|
129
|
+
@family_key = record["family_key"]
|
130
|
+
@family = HeadMusic::InstrumentFamily.get(family_key)
|
131
|
+
end
|
132
|
+
|
133
|
+
def inherit_family_attributes(record)
|
134
|
+
return unless family
|
135
|
+
|
136
|
+
@orchestra_section_key = family.orchestra_section_key
|
137
|
+
@classification_keys = family.classification_keys || []
|
138
|
+
end
|
139
|
+
|
140
|
+
def initialize_names(record)
|
68
141
|
@name_key = record["name_key"].to_sym
|
69
|
-
@family = record["family"]
|
70
|
-
@standard_staves = record["standard_staves"] || []
|
71
|
-
@classifications = record["classifications"] || []
|
72
142
|
self.name = I18n.translate(name_key, scope: "instruments", locale: "en", default: inferred_name)
|
143
|
+
@alias_name_keys = record["alias_name_keys"] || []
|
144
|
+
end
|
145
|
+
|
146
|
+
def initialize_attributes(record)
|
147
|
+
@orchestra_section_key ||= record["orchestra_section_key"]
|
148
|
+
@classification_keys = [@classification_keys, record["classification_keys"]].flatten.compact.uniq
|
149
|
+
@fundamental_pitch_spelling = record["fundamental_pitch_spelling"]
|
150
|
+
@default_staffs = (record.dig("notation", "default", "staffs") || [])
|
151
|
+
@default_clefs = @default_staffs.map { |staff| staff["clef"] }
|
152
|
+
@transposition = @default_staffs&.first&.[]("transposition") || 0
|
73
153
|
end
|
74
154
|
|
75
155
|
def inferred_name
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# An *InstrumentFamily* is a species of instrument
|
2
|
+
# that may exist in a variety of keys or other variations.
|
3
|
+
# For example, _saxophone_ is an instrument family, while
|
4
|
+
# _alto saxophone_ and _baritone saxophone_ are specific instruments.
|
5
|
+
class HeadMusic::InstrumentFamily
|
6
|
+
include HeadMusic::Named
|
7
|
+
|
8
|
+
INSTRUMENT_FAMILIES =
|
9
|
+
YAML.load_file(File.expand_path("data/instrument_families.yml", __dir__)).freeze
|
10
|
+
|
11
|
+
attr_reader :name_key, :classification_keys, :orchestra_section_key, :default_staffs
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
def self.get(name)
|
15
|
+
result = get_by_name(name) || get_by_name(key_for_name(name))
|
16
|
+
result || new(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.all
|
20
|
+
@all ||=
|
21
|
+
INSTRUMENT_FAMILIES.map { |key, _data| get(key) }.sort_by(&:name)
|
22
|
+
end
|
23
|
+
|
24
|
+
private_class_method :new
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def initialize(name)
|
29
|
+
record = record_for_name(name)
|
30
|
+
if record
|
31
|
+
initialize_data_from_record(record)
|
32
|
+
else
|
33
|
+
self.name = name.to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def record_for_name(name)
|
38
|
+
record_for_key(HeadMusic::Utilities::HashKey.for(name)) ||
|
39
|
+
record_for_key(key_for_name(name))
|
40
|
+
end
|
41
|
+
|
42
|
+
def key_for_name(name)
|
43
|
+
INSTRUMENT_FAMILIES.each do |key, _data|
|
44
|
+
I18n.config.available_locales.each do |locale|
|
45
|
+
translation = I18n.t("instruments.#{key}", locale: locale)
|
46
|
+
return key if translation.downcase == name.downcase
|
47
|
+
end
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def record_for_key(key)
|
53
|
+
INSTRUMENT_FAMILIES.each do |name_key, data|
|
54
|
+
return data.merge!("name_key" => name_key) if name_key.to_s == key.to_s
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize_data_from_record(record)
|
60
|
+
@name_key = record["name_key"].to_sym
|
61
|
+
@orchestra_section_key = record["orchestra_section_key"]
|
62
|
+
@classification_keys = record["classification_keys"] || []
|
63
|
+
self.name = I18n.translate(name_key, scope: "instruments", locale: "en", default: inferred_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def inferred_name
|
67
|
+
name_key.to_s.tr("_", " ")
|
68
|
+
end
|
69
|
+
end
|
@@ -6,7 +6,7 @@ de:
|
|
6
6
|
baritone_f_clef: Baritonschlüssel
|
7
7
|
bass_clef: Bassschlüssel
|
8
8
|
c_clef: C-Schlüssel
|
9
|
-
|
9
|
+
vocal_tenor_clef: oktavierten Violinschlüssel
|
10
10
|
contrabass_clef: contrabass clef
|
11
11
|
countertenor_clef: countertenor clef
|
12
12
|
double_treble_clef: double treble clef
|
@@ -61,10 +61,10 @@ de:
|
|
61
61
|
bagpipe: Dudelsack
|
62
62
|
baritone_horn: Baritonhorn
|
63
63
|
baritone_voice: Bariton
|
64
|
-
bass_voice: Bass
|
65
64
|
bass_clarinet: Bassklarinette
|
66
65
|
bass_drum: Grosse Trommel
|
67
66
|
bass_trombone: Bass Posaune
|
67
|
+
bass_voice: Bass
|
68
68
|
bassoon: Fagott
|
69
69
|
castanets: Kastagnetten
|
70
70
|
celesta: Celesta
|
@@ -78,7 +78,7 @@ de:
|
|
78
78
|
english_horn: Englischhorn
|
79
79
|
euphonium: Euphonium
|
80
80
|
fiddle: Geige
|
81
|
-
|
81
|
+
flugelhorn: Flügelhorn
|
82
82
|
flute: Flöte
|
83
83
|
french_horn: Horn
|
84
84
|
glockenspiel: Glockenspiel
|
@@ -6,7 +6,7 @@ en:
|
|
6
6
|
baritone_f_clef: baritone F-clef
|
7
7
|
bass_clef: bass clef
|
8
8
|
c_clef: C-clef
|
9
|
-
|
9
|
+
vocal_tenor_clef: vocal tenor clef
|
10
10
|
contrabass_clef: contrabass clef
|
11
11
|
countertenor_clef: countertenor clef
|
12
12
|
double_treble_clef: double treble clef
|
@@ -92,7 +92,6 @@ en:
|
|
92
92
|
bass_drum: bass drum
|
93
93
|
bass_guitar: bass guitar
|
94
94
|
bass_oboe: bass oboe
|
95
|
-
bass_drum: bass drum
|
96
95
|
bass_trombone: bass trombone
|
97
96
|
bass_tuba: bass tuba
|
98
97
|
bass_voice: bass
|
@@ -102,21 +101,20 @@ en:
|
|
102
101
|
cello: cello
|
103
102
|
chimes: chimes
|
104
103
|
cimbalom: cimbalom
|
105
|
-
cowbell: cowbell
|
106
104
|
clarinet: clarinet
|
107
|
-
clavichord: clavichord
|
108
105
|
clash_cymbals: clash cymbals
|
109
|
-
|
110
|
-
contrabassoon: contrabassoon
|
106
|
+
clavichord: clavichord
|
111
107
|
contra_bass_clarinet: contra bass clarinet
|
108
|
+
contrabassoon: contrabassoon
|
112
109
|
cor_anglais: cor anglais
|
110
|
+
cornet: cornet
|
113
111
|
cowbell: cowbell
|
114
|
-
cymbal: cymbal
|
115
112
|
crotales: crotales
|
113
|
+
cymbal: cymbal
|
116
114
|
double_bass: double bass
|
117
115
|
english_horn: English horn
|
118
116
|
euphonium: euphonium
|
119
|
-
|
117
|
+
flugelhorn: flugelhorn
|
120
118
|
flute: flute
|
121
119
|
french_horn: French horn
|
122
120
|
glockenspiel: glockenspiel
|
@@ -6,7 +6,7 @@ es:
|
|
6
6
|
baritone_f_clef: clave de fa en tercera
|
7
7
|
bass_clef: clave de bajo
|
8
8
|
c_clef: clave de do
|
9
|
-
|
9
|
+
vocal_tenor_clef: vocal tenor clef
|
10
10
|
contrabass_clef: clave de contrabajo
|
11
11
|
countertenor_clef: clave de contratenor
|
12
12
|
double_treble_clef: double clave de sol
|
@@ -72,7 +72,7 @@ es:
|
|
72
72
|
double_bass: contrabajo
|
73
73
|
english_horn: Cuerno inglés
|
74
74
|
euphonium: euphonium
|
75
|
-
|
75
|
+
flugelhorn: fliscorno
|
76
76
|
flute: flauta
|
77
77
|
french_horn: corno
|
78
78
|
glockenspiel: campanólogo
|
@@ -6,7 +6,7 @@ fr:
|
|
6
6
|
baritone_f_clef: clé de fa 3e
|
7
7
|
bass_clef: clé de basse
|
8
8
|
c_clef: clé d'ut
|
9
|
-
|
9
|
+
vocal_tenor_clef: vocal tenor clef
|
10
10
|
contrabass_clef: clé de contrebasse
|
11
11
|
countertenor_clef: clé de contre-ténor
|
12
12
|
double_treble_clef: double clé de sol
|
@@ -6,7 +6,7 @@ it:
|
|
6
6
|
baritone_f_clef: chiave di baritono
|
7
7
|
bass_clef: chiave di basso
|
8
8
|
c_clef: chiave di do
|
9
|
-
|
9
|
+
vocal_tenor_clef: chiave di sol in ottava
|
10
10
|
contrabass_clef: chiave di contrabbasso
|
11
11
|
countertenor_clef: chiave di controtenore
|
12
12
|
double_treble_clef: chiave di sol in ottava
|
@@ -76,7 +76,7 @@ it:
|
|
76
76
|
double_bass: contrabasso
|
77
77
|
english_horn: corno inglese
|
78
78
|
euphonium: eufonio
|
79
|
-
|
79
|
+
flugelhorn: flicorno
|
80
80
|
flute: flauto
|
81
81
|
french_horn: corno
|
82
82
|
glockenspiel: glockenspiel
|
@@ -91,9 +91,9 @@ it:
|
|
91
91
|
natural_horn: corno naturale
|
92
92
|
oboe: oboe
|
93
93
|
oboe_d_amore: oboe d'amore
|
94
|
-
piccolo: ottavino
|
95
94
|
organ: organo
|
96
95
|
piano: piano
|
96
|
+
piccolo: ottavino
|
97
97
|
recorder: flauto dolce
|
98
98
|
saxophone: sassofono
|
99
99
|
snare_drum: tamburo rullante
|
@@ -32,7 +32,7 @@ ru:
|
|
32
32
|
double_bass: kontrabas
|
33
33
|
english_horn: angliiskii rozhok
|
34
34
|
euphonium: evfonium
|
35
|
-
|
35
|
+
flugelhorn: fliugel'gorn
|
36
36
|
flute: fleita
|
37
37
|
french_horn: Gorn
|
38
38
|
glockenspiel: kolokol'chiki
|
@@ -67,4 +67,4 @@ ru:
|
|
67
67
|
violoncello: violonchel'
|
68
68
|
voice: golos
|
69
69
|
xylophone: ksilofon
|
70
|
-
zither: tsitra
|
70
|
+
zither: tsitra
|
data/lib/head_music/staff.rb
CHANGED
@@ -5,11 +5,14 @@ class HeadMusic::Staff
|
|
5
5
|
DEFAULT_LINE_COUNT = 5
|
6
6
|
|
7
7
|
attr_reader :default_clef, :line_count, :instrument
|
8
|
-
alias_method :clef, :default_clef
|
9
8
|
|
10
9
|
def initialize(default_clef, instrument: nil, line_count: nil)
|
11
10
|
@default_clef = HeadMusic::Clef.get(default_clef)
|
12
11
|
@line_count = line_count || DEFAULT_LINE_COUNT
|
13
12
|
@instrument = HeadMusic::Instrument.get(instrument) if instrument
|
14
13
|
end
|
14
|
+
|
15
|
+
def clef
|
16
|
+
default_clef || instrument&.default_staffs&.first
|
17
|
+
end
|
15
18
|
end
|
data/lib/head_music/version.rb
CHANGED
data/lib/head_music.rb
CHANGED
@@ -39,8 +39,8 @@ require "head_music/circle"
|
|
39
39
|
require "head_music/clef"
|
40
40
|
require "head_music/consonance"
|
41
41
|
require "head_music/diatonic_interval"
|
42
|
-
require "head_music/grand_staff"
|
43
42
|
require "head_music/harmonic_interval"
|
43
|
+
require "head_music/instrument_family"
|
44
44
|
require "head_music/instrument"
|
45
45
|
require "head_music/interval_cycle"
|
46
46
|
require "head_music/key_signature"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: head_music
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob Head
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -132,11 +132,12 @@ files:
|
|
132
132
|
- lib/head_music/content/rhythmic_value.rb
|
133
133
|
- lib/head_music/content/voice.rb
|
134
134
|
- lib/head_music/data/clefs.yml
|
135
|
+
- lib/head_music/data/instrument_families.yml
|
135
136
|
- lib/head_music/data/instruments.yml
|
136
137
|
- lib/head_music/diatonic_interval.rb
|
137
|
-
- lib/head_music/grand_staff.rb
|
138
138
|
- lib/head_music/harmonic_interval.rb
|
139
139
|
- lib/head_music/instrument.rb
|
140
|
+
- lib/head_music/instrument_family.rb
|
140
141
|
- lib/head_music/interval_cycle.rb
|
141
142
|
- lib/head_music/key_signature.rb
|
142
143
|
- lib/head_music/letter_name.rb
|
@@ -234,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
235
|
- !ruby/object:Gem::Version
|
235
236
|
version: '0'
|
236
237
|
requirements: []
|
237
|
-
rubygems_version: 3.
|
238
|
+
rubygems_version: 3.4.12
|
238
239
|
signing_key:
|
239
240
|
specification_version: 4
|
240
241
|
summary: The rudiments of western music theory.
|
@@ -1,56 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# A grand staff is a group of staves for a single instrument, such as a piano.
|
4
|
-
class HeadMusic::GrandStaff
|
5
|
-
GRAND_STAVES = {
|
6
|
-
piano: {
|
7
|
-
instrument: :piano,
|
8
|
-
staves: [
|
9
|
-
{clef: :treble_clef, for: :right_hand},
|
10
|
-
{clef: :bass_clef, for: :left_hand}
|
11
|
-
]
|
12
|
-
},
|
13
|
-
organ: {
|
14
|
-
instrument: :organ,
|
15
|
-
staves: [
|
16
|
-
{clef: :treble_clef, for: :right_hand},
|
17
|
-
{clef: :bass_clef, for: :left_hand},
|
18
|
-
{clef: :bass_clef, for: :pedals}
|
19
|
-
]
|
20
|
-
}
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
def self.get(name)
|
24
|
-
@grand_staves ||= {}
|
25
|
-
hash_key = HeadMusic::Utilities::HashKey.for(name)
|
26
|
-
return nil unless GRAND_STAVES.key?(hash_key)
|
27
|
-
|
28
|
-
@grand_staves[hash_key] ||= new(hash_key)
|
29
|
-
end
|
30
|
-
|
31
|
-
attr_reader :identifier, :data
|
32
|
-
|
33
|
-
def initialize(name)
|
34
|
-
@identifier = HeadMusic::Utilities::HashKey.for(name)
|
35
|
-
@data = GRAND_STAVES[identifier]
|
36
|
-
end
|
37
|
-
|
38
|
-
def instrument
|
39
|
-
@instrument ||= HeadMusic::Instrument.get(data[:instrument])
|
40
|
-
end
|
41
|
-
|
42
|
-
def staves
|
43
|
-
@staves ||=
|
44
|
-
data[:staves].map do |staff|
|
45
|
-
HeadMusic::Staff.new(staff[:clef], instrument: instrument)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def brace_staves_index_first
|
50
|
-
0
|
51
|
-
end
|
52
|
-
|
53
|
-
def brace_staves_index_last
|
54
|
-
1
|
55
|
-
end
|
56
|
-
end
|