head_music 0.24.0 → 0.24.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46aae73acea5e3331b19a08a02ded3c00fea5731e920207fc9d2b61dd9ebc359
4
- data.tar.gz: c2cfdbb52641ad27bd96308fe99da5f59b2e66efdf2f40a4932e8bc6552510c4
3
+ metadata.gz: 0abe4bcc9d5eda30350352723a69a500c1a072d08e39de64d9266a25953771fa
4
+ data.tar.gz: 25a2b86a64a5c4f31cb75c5de988c59cad7e0a271a78adcde5e3cc2f040b88ef
5
5
  SHA512:
6
- metadata.gz: 23a9cc4013e6058892effeeaf940947a90ce2e5950eb4c3c622b7036fecbaa14dd4cd4aa5d5dbe5b6ffdefe1ce8e37681b1d8d040e135b1a4c737fca23c84fa6
7
- data.tar.gz: 15d30c56ea0b08db8bde6b0d69b38a9605751b370086e6a66db5a74029b1f4c87ab408b0e567e092f0e2418d4f65e1dfe5644358c0fd0f6f676548466b5544ec
6
+ metadata.gz: 670ca78b363ae669a73e4159b46e5465439824f1e051f15f8846acce3a6790352ac64b33cd41e82d6a602e3ef3582a6e224b9fd4da0fb5fbc85263c82d3e26c7
7
+ data.tar.gz: 1407f5414eb3842a61745c9943f50f14d866c1318d1a93d9c21ed296d57d5a61ba850f3c6b562c94ca20a9b23ada2098dc445af44184baed3969bfe3c29ab2bb
@@ -24,8 +24,9 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.required_ruby_version = '>= 2.4.0'
26
26
 
27
- spec.add_runtime_dependency 'activesupport', '~> 5.0'
27
+ spec.add_runtime_dependency 'activesupport', '> 5.0'
28
28
  spec.add_runtime_dependency 'humanize', '~> 1.3'
29
+ spec.add_runtime_dependency 'i18n', '~> 1.8'
29
30
 
30
31
  spec.add_development_dependency 'bundler', '~> 1.13'
31
32
  spec.add_development_dependency 'rake', '>= 12.3.3'
@@ -12,6 +12,19 @@ require 'head_music/version'
12
12
  require 'active_support/core_ext/module/delegation'
13
13
  require 'active_support/core_ext/string/access'
14
14
  require 'humanize'
15
+ require 'i18n'
16
+ require 'i18n/backend/fallbacks'
17
+
18
+ I18n::Backend::Simple.include I18n::Backend::Fallbacks
19
+ I18n.load_path << Dir[File.join(File.dirname(__dir__), 'lib', 'head_music', 'locales', '*.yml')]
20
+ I18n.config.available_locales = %i[en de fr it es en_US en_GB]
21
+ I18n.default_locale = :en
22
+ I18n.fallbacks[:de] = %i[de en_GB en]
23
+ I18n.fallbacks[:en_US] = %i[en_US en en_GB]
24
+ I18n.fallbacks[:en_GB] = %i[en_GB en en_US]
25
+ I18n.fallbacks[:es] = %i[es en]
26
+ I18n.fallbacks[:fr] = %i[fr en_GB en]
27
+ I18n.fallbacks[:it] = %i[it en_GB en]
15
28
 
16
29
  # utilities
17
30
  require 'head_music/utilities/hash_key'
@@ -50,6 +63,7 @@ require 'head_music/scale'
50
63
  require 'head_music/scale_degree'
51
64
  require 'head_music/scale_type'
52
65
  require 'head_music/sign'
66
+ require 'head_music/solmization'
53
67
  require 'head_music/sonority'
54
68
  require 'head_music/spelling'
55
69
  require 'head_music/staff'
@@ -6,6 +6,7 @@ class HeadMusic::ChromaticInterval
6
6
 
7
7
  private_class_method :new
8
8
 
9
+ # TODO: include the Named module
9
10
  NAMES = %w[
10
11
  perfect_unison minor_second major_second minor_third major_third perfect_fourth tritone perfect_fifth
11
12
  minor_sixth major_sixth minor_seventh major_seventh perfect_octave
@@ -1,93 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'yaml'
4
+
3
5
  # A clef assigns pitches to the lines and spaces of a staff.
4
6
  class HeadMusic::Clef
5
7
  include HeadMusic::Named
6
8
 
7
- CLEFS = [
8
- {
9
- pitch: 'G4', line: 2,
10
- names: %w[treble G-clef],
11
- modern: true,
12
- unicode: '𝄞', html_entity: '&#119070;',
13
- },
14
- {
15
- pitch: 'G4', line: 1,
16
- names: ['French', 'French violin'],
17
- unicode: '𝄞', html_entity: '&#119070;',
18
- },
19
- {
20
- pitch: 'G3', line: 2,
21
- names: ['choral tenor', 'tenor', 'tenor G-clef'],
22
- modern: true,
23
- unicode: '𝄠', html_entity: '&#119072;',
24
- },
25
- {
26
- pitch: 'F3', line: 3,
27
- names: ['baritone'],
28
- unicode: '𝄢', html_entity: '&#119074;',
29
- },
30
- {
31
- pitch: 'F3', line: 4,
32
- names: %w[bass F-clef],
33
- modern: true,
34
- unicode: '𝄢', html_entity: '&#119074;',
35
- },
36
- {
37
- pitch: 'F3', line: 5,
38
- names: ['sub-bass'],
39
- unicode: '𝄢', html_entity: '&#119074;',
40
- },
41
- {
42
- pitch: 'C4', line: 1,
43
- names: ['soprano'],
44
- unicode: '𝄡', html_entity: '&#119073;',
45
- },
46
- {
47
- pitch: 'C4', line: 2,
48
- names: ['mezzo-soprano'],
49
- unicode: '𝄡', html_entity: '&#119073;',
50
- },
51
- {
52
- pitch: 'C4', line: 3,
53
- names: %w[alto viola counter-tenor countertenor C-clef],
54
- modern: true,
55
- unicode: '𝄡', html_entity: '&#119073;',
56
- },
57
- {
58
- pitch: 'C4', line: 4,
59
- names: ['tenor', 'tenor C-clef'],
60
- modern: true,
61
- unicode: '𝄡', html_entity: '&#119073;',
62
- },
63
- {
64
- pitch: 'C4', line: 5,
65
- names: ['baritone', 'baritone C-clef'],
66
- unicode: '𝄡', html_entity: '&#119073;',
67
- },
68
- {
69
- pitch: nil, line: 3,
70
- names: %w[neutral percussion],
71
- modern: true,
72
- unicode: '𝄥', html_entity: '&#119077;',
73
- },
74
- ].freeze
9
+ RECORDS = YAML.load_file(File.expand_path('data/clefs.yml', __dir__)).freeze
75
10
 
76
11
  def self.get(name)
77
12
  get_by_name(name)
78
13
  end
79
14
 
80
- attr_reader :pitch, :line, :musical_symbol
15
+ attr_reader :pitch, :line, :musical_symbols
81
16
 
82
17
  delegate :ascii, :html_entity, :unicode, to: :musical_symbol
83
18
 
84
- def initialize(name)
85
- @name = name.to_s
86
- clef_data = CLEFS.detect { |clef| clef[:names].map(&:downcase).include?(name.downcase) }
87
- @pitch = HeadMusic::Pitch.get(clef_data[:pitch])
88
- @line = clef_data[:line]
89
- @modern = clef_data[:modern]
90
- @musical_symbol = HeadMusic::MusicalSymbol.new(clef_data.slice(:ascii, :html_entity, :unicode))
19
+ def musical_symbol
20
+ musical_symbols.first
91
21
  end
92
22
 
93
23
  def clef_type
@@ -115,6 +45,59 @@ class HeadMusic::Clef
115
45
  end
116
46
 
117
47
  def ==(other)
118
- to_s == other.to_s
48
+ HeadMusic::Utilities::HashKey.for(self) == HeadMusic::Utilities::HashKey.for(other)
49
+ end
50
+
51
+ def name(locale_code: Locale::DEFAULT_CODE)
52
+ I18n.translate(name_key, scope: :clefs, locale: locale_code)
53
+ end
54
+
55
+ private_class_method :new
56
+
57
+ private
58
+
59
+ def initialize(name)
60
+ record = record_for_name(name)
61
+ initialize_data_from_record(record)
62
+ end
63
+
64
+ def record_for_name(name)
65
+ name = name.to_s.strip
66
+ key = HeadMusic::Utilities::HashKey.for(name)
67
+ RECORDS.detect do |record|
68
+ name_keys = name_keys_from_record(record)
69
+ name_keys.include?(key) || name_key_translations(name_keys).include?(name)
70
+ end
71
+ end
72
+
73
+ def name_keys_from_record(record)
74
+ ([record[:name_key]] + [record[:alias_name_keys]]).flatten.compact.uniq.map(&:to_sym)
75
+ end
76
+
77
+ def name_key_translations(name_keys)
78
+ name_keys.map do |name_key|
79
+ I18n.config.available_locales.map do |locale_code|
80
+ I18n.translate(name_key, scope: :clefs, locale: locale_code)
81
+ end.flatten.uniq.compact
82
+ end.flatten.uniq.compact
83
+ end
84
+
85
+ def initialize_data_from_record(record)
86
+ initialize_keys_from_record(record)
87
+ @pitch = HeadMusic::Pitch.get(record[:pitch])
88
+ @line = record[:line]
89
+ @modern = record[:modern]
90
+ initialize_musical_symbols(record[:symbols])
91
+ end
92
+
93
+ def initialize_keys_from_record(record)
94
+ @name_key = record[:name_key]
95
+ @alias_name_keys = [record[:alias_name_keys]].flatten.compact
96
+ end
97
+
98
+ def initialize_musical_symbols(list)
99
+ @musical_symbols = (list || []).map do |symbol_data|
100
+ HeadMusic::MusicalSymbol.new(symbol_data.slice(:ascii, :html_entity, :unicode))
101
+ end
119
102
  end
120
103
  end
@@ -0,0 +1,126 @@
1
+ ---
2
+ - :pitch: G4
3
+ :line: 2
4
+ :modern: true
5
+ :name_key: treble_clef
6
+ :alias_name_keys:
7
+ - g_clef
8
+ - second_line_g_clef
9
+ - violin_clef
10
+ :symbols:
11
+ - :unicode: "\U0001D11E"
12
+ :html_entity: "&#119070;"
13
+ - :pitch: G4
14
+ :line: 1
15
+ :name_key: french_violin_clef
16
+ :alias_name_keys:
17
+ - french_clef
18
+ - first_line_g_clef
19
+ :symbols:
20
+ - :unicode: "\U0001D11E"
21
+ :html_entity: "&#119070;"
22
+ - :pitch: G3
23
+ :line: 2
24
+ :modern: true
25
+ :name_key: choral_tenor_clef
26
+ :alias_name_keys:
27
+ - tenor_clef
28
+ - tenor_g_clef
29
+ :symbols:
30
+ - :unicode: "\U0001D120"
31
+ :html_entity: "&#119072;"
32
+ - :pitch: G3
33
+ :line: 2
34
+ :name_key: double_treble_clef
35
+ :symbols:
36
+ - :unicode: "\U0001D11E\U0001D11E"
37
+ :html_entity: "&#119070;&#119070;"
38
+ - :pitch: F3
39
+ :line: 3
40
+ :name_key: baritone_clef
41
+ :alias_name_keys:
42
+ - baritone_f_clef
43
+ - third_line_f_clef
44
+ :symbols:
45
+ - :unicode: "\U0001D122"
46
+ :html_entity: "&#119074;"
47
+ - :pitch: F3
48
+ :line: 4
49
+ :modern: true
50
+ :name_key: bass_clef
51
+ :alias_name_keys:
52
+ - f_clef
53
+ - fourth_line_f_clef
54
+ :symbols:
55
+ - :unicode: "\U0001D122"
56
+ :html_entity: "&#119074;"
57
+ - :pitch: F3
58
+ :line: 5
59
+ :name_key: sub_bass_clef
60
+ :alias_name_keys:
61
+ - contrabass_clef
62
+ - fifth_line_f_clef
63
+ :symbols:
64
+ - :unicode: "\U0001D122"
65
+ :html_entity: "&#119074;"
66
+ - :pitch: C4
67
+ :line: 1
68
+ :name_key: soprano_clef
69
+ :alias_name_keys:
70
+ - first_line_c_clef
71
+ :symbols:
72
+ - :unicode: "\U0001D121"
73
+ :html_entity: "&#119073;"
74
+ - :pitch: C4
75
+ :line: 2
76
+ :name_key: mezzo_soprano_clef
77
+ :alias_name_keys:
78
+ - second_line_c_clef
79
+ :symbols:
80
+ - :unicode: "\U0001D121"
81
+ :html_entity: "&#119073;"
82
+ - :pitch: C4
83
+ :line: 3
84
+ :modern: true
85
+ :name_key: alto_clef
86
+ :alias_name_keys:
87
+ - c_clef
88
+ - third_line_c_clef
89
+ - viola_clef
90
+ - countertenor_clef
91
+ :symbols:
92
+ - :unicode: "\U0001D121"
93
+ :html_entity: "&#119073;"
94
+ - :pitch: C4
95
+ :line: 4
96
+ :modern: true
97
+ :name_key: tenor_clef
98
+ :alias_name_keys:
99
+ - tenor_c_clef
100
+ - fourth_line_c_clef
101
+ :symbols:
102
+ - :unicode: "\U0001D121"
103
+ :html_entity: "&#119073;"
104
+ - :pitch: C4
105
+ :line: 5
106
+ :name_key: baritone_c_clef
107
+ :alias_name_keys:
108
+ - baritone_clef
109
+ - fifth_line_c_clef
110
+ :symbols:
111
+ - :unicode: "\U0001D121"
112
+ :html_entity: "&#119073;"
113
+ - :pitch:
114
+ :line: 3
115
+ :modern: true
116
+ :name_key: neutral_clef
117
+ :alias_name_keys:
118
+ - percussion_clef
119
+ - indefinite_pitch_clef
120
+ - rhythm_clef
121
+ - drum_clef
122
+ :symbols:
123
+ - :unicode: "\U0001D125"
124
+ :html_entity: "&#119077;"
125
+ - :unicode: "\U0001D126"
126
+ :html_entity: "&#119078;"
@@ -0,0 +1,36 @@
1
+ ---
2
+ - name_key: alto_flute
3
+ - name_key: guitar
4
+ family: string
5
+ classifications:
6
+ - string
7
+ - chordophone
8
+ default_clefs:
9
+ - treble_clef
10
+ - name_key: piano
11
+ family: string
12
+ classifications:
13
+ - string
14
+ - chordophone
15
+ - keyboard
16
+ default_clefs:
17
+ - treble_clef
18
+ - bass_clef
19
+ - name_key: violin
20
+ alias_name_keys:
21
+ - fiddle
22
+ family: string
23
+ classifications:
24
+ - string
25
+ - chordophone
26
+ - bowed
27
+ default_clefs:
28
+ - treble_clef
29
+ - name_key: viola
30
+ family: string
31
+ classifications:
32
+ - string
33
+ - chordophone
34
+ - bowed
35
+ default_clefs:
36
+ - alto_clef
@@ -4,6 +4,7 @@
4
4
  class HeadMusic::DiatonicInterval
5
5
  include Comparable
6
6
 
7
+ # TODO: include Named module
7
8
  NUMBER_NAMES = %w[
8
9
  unison second third fourth fifth sixth seventh octave
9
10
  ninth tenth eleventh twelfth thirteenth fourteenth fifteenth
@@ -6,16 +6,16 @@ class HeadMusic::GrandStaff
6
6
  piano: {
7
7
  instrument: :piano,
8
8
  staves: [
9
- { clef: :treble, instrument: :piano },
10
- { clef: :bass, instrument: :piano },
9
+ { clef: :treble_clef, instrument: :piano },
10
+ { clef: :bass_clef, instrument: :piano },
11
11
  ],
12
12
  },
13
13
  organ: {
14
14
  instrument: :organ,
15
15
  staves: [
16
- { clef: :treble, instrument: :organ },
17
- { clef: :bass, instrument: :organ },
18
- { clef: :bass, instrument: :pedals },
16
+ { clef: :treble_clef, instrument: :organ },
17
+ { clef: :bass_clef, instrument: :organ },
18
+ { clef: :bass_clef, instrument: :pedals },
19
19
  ],
20
20
  },
21
21
  }.freeze
@@ -41,7 +41,12 @@ class HeadMusic::GrandStaff
41
41
 
42
42
  def staves
43
43
  @staves ||=
44
- data[:staves].map { |staff| HeadMusic::Staff.new(staff[:clef], instrument: staff[:instrument] || instrument) }
44
+ data[:staves].map do |staff|
45
+ HeadMusic::Staff.new(
46
+ staff[:clef],
47
+ instrument: staff[:instrument] || instrument
48
+ )
49
+ end
45
50
  end
46
51
 
47
52
  def brace_staves_index_first
@@ -4,44 +4,57 @@
4
4
  class HeadMusic::Instrument
5
5
  include HeadMusic::Named
6
6
 
7
- INSTRUMENTS = {
8
- violin: {
9
- name: 'violin',
10
- family: :string,
11
- default_clef: :treble,
12
- },
13
- piano: {
14
- name: 'piano',
15
- family: :string,
16
- default_system: %i[treble bass],
17
- },
18
- }.freeze
7
+ INSTRUMENTS = YAML.load_file(File.expand_path('data/instruments.yml', __dir__)).freeze
19
8
 
20
9
  def self.get(name)
21
- get_by_name(name)
10
+ return get_by_name(name) if get_by_name(name)
11
+ return get_by_name(key_for_name(name)) if key_for_name(name)
12
+
13
+ new(name)
22
14
  end
23
15
 
24
- def initialize(name)
25
- @name = name.to_s
16
+ attr_reader :name_key, :family, :default_clefs
17
+
18
+ def ==(other)
19
+ to_s == other.to_s
26
20
  end
27
21
 
28
- def data
29
- @data ||= INSTRUMENTS[hash_key]
22
+ private_class_method :new
23
+
24
+ private
25
+
26
+ def initialize(name)
27
+ record = record_for_name(name)
28
+ if record
29
+ initialize_data_from_record(record)
30
+ else
31
+ self.name = name
32
+ end
30
33
  end
31
34
 
32
- def family
33
- data[:family]
35
+ def record_for_name(name)
36
+ key = HeadMusic::Utilities::HashKey.for(name)
37
+ record_for_key(key) || record_for_key(key_for_name(name))
34
38
  end
35
39
 
36
- def default_system
37
- data[:default_system]
40
+ def key_for_name(name)
41
+ INSTRUMENTS.each do |instrument|
42
+ I18n.config.available_locales.each do |locale|
43
+ translation = I18n.t("instruments.#{instrument['name_key']}", locale: locale)
44
+ return instrument['name_key'] if translation.downcase == name.downcase
45
+ end
46
+ end
47
+ nil
38
48
  end
39
49
 
40
- def default_clef
41
- data[:default_clef]
50
+ def record_for_key(key)
51
+ INSTRUMENTS.detect { |instrument| instrument['name_key'] == key }
42
52
  end
43
53
 
44
- def ==(other)
45
- to_s == other.to_s
54
+ def initialize_data_from_record(record)
55
+ @family = record['family']
56
+ @default_clefs = record['default_clefs']
57
+ @name_key = record['name_key'].to_sym
58
+ self.name = I18n.translate(name_key, scope: 'instruments', locale: 'en')
46
59
  end
47
60
  end