head_music 0.25.0 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 59d262194fb0642a00c0f6a491f0225ac8e637fc1b81fb210e202bb71f38d37d
4
- data.tar.gz: b28f1d54a1f51a8500e2448fd5e64a8fcd2a901ef085ca2a378add0277a65c9b
3
+ metadata.gz: 4274cb71a70fcab6904e99afc282bb1db756e2a1cce0c1e1e50c601e0e848edf
4
+ data.tar.gz: 47566a6db5b0836686fd789cf2e0b052416a68f8f2ed67b2f6582266a141ea1a
5
5
  SHA512:
6
- metadata.gz: 963a048a8b771cc8c3dd5d1a5735744bbed9ea1ba90028d1e152f8649aa4f08d37c4e9c91d87b07872fc9ea65557a4d7d124b2c1a1c62747279188b7ca64db13
7
- data.tar.gz: 6b27b0330b5b8d9dbdb18934baa8b2e5e1b2992b580f8e930d9979f2da08be19ba1274c2eba1d251e57c9c026cd871d05cc9d6f5e58e0b52ce57ba49f7809d61
6
+ metadata.gz: b85d9dbd114b70bfea1fa9ce8cb964f6780ad9f64325a75baba02df48e416475f33e26a9dab22309740ff6e7f033fca756906a4fd5935af1f22e6fd325a775d4
7
+ data.tar.gz: 60a75acc692a1e1014f6042f9b167ecd7a0f6ec8eb98ca5dccb4c16a9bbcc79deff0b56481a713941caad779b7cc17b8487c89f4b4839bb31e3b453cff7558b5
data/lib/head_music.rb CHANGED
@@ -17,7 +17,7 @@ require 'i18n/backend/fallbacks'
17
17
 
18
18
  I18n::Backend::Simple.include I18n::Backend::Fallbacks
19
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]
20
+ I18n.config.available_locales = %i[en fr de it ru es en_US en_GB]
21
21
  I18n.default_locale = :en
22
22
  I18n.fallbacks[:de] = %i[de en_GB en]
23
23
  I18n.fallbacks[:en_US] = %i[en_US en en_GB]
@@ -25,6 +25,7 @@ I18n.fallbacks[:en_GB] = %i[en_GB en en_US]
25
25
  I18n.fallbacks[:es] = %i[es en]
26
26
  I18n.fallbacks[:fr] = %i[fr en_GB en]
27
27
  I18n.fallbacks[:it] = %i[it en_GB en]
28
+ I18n.fallbacks[:ru] = %i[ru en_GB en]
28
29
 
29
30
  # utilities
30
31
  require 'head_music/utilities/hash_key'
@@ -48,7 +49,6 @@ require 'head_music/melodic_interval'
48
49
  require 'head_music/meter'
49
50
  require 'head_music/motion'
50
51
  require 'head_music/musical_symbol'
51
- require 'head_music/octave'
52
52
  require 'head_music/pitch'
53
53
  require 'head_music/pitch/enharmonic_equivalence'
54
54
  require 'head_music/pitch/octave_equivalence'
@@ -57,6 +57,7 @@ require 'head_music/pitch_class_set'
57
57
  require 'head_music/pitch_set'
58
58
  require 'head_music/quality'
59
59
  require 'head_music/reference_pitch'
60
+ require 'head_music/register'
60
61
  require 'head_music/rhythm'
61
62
  require 'head_music/rhythmic_unit'
62
63
  require 'head_music/scale'
@@ -1,83 +1,145 @@
1
1
  ---
2
- guitar:
3
- family: string
4
- classifications:
5
- - string
6
- - chordophone
7
- default_clefs:
8
- - treble_clef
9
- voice: tenor
10
- piano:
11
- family: string
2
+ bassoon:
12
3
  classifications:
13
- - string
14
- - chordophone
15
- - keyboard
16
- default_clefs:
17
- - treble_clef
4
+ - aerophone
5
+ - reed
6
+ - double_reed
7
+ - wind
8
+ - woodwind
9
+ family: woodwind
10
+ standard_staves:
18
11
  - bass_clef
19
- violin:
20
- family: string
12
+ cello:
21
13
  alias_name_keys:
22
- - fiddle
14
+ - violoncello
23
15
  classifications:
24
- - string
25
- - chordophone
26
16
  - bowed
27
- default_clefs:
28
- - treble_clef
29
- voice: soprano
30
- viola:
31
- family: string
32
- classifications:
33
- - string
34
17
  - chordophone
35
- - bowed
36
- default_clefs:
37
- - alto_clef
38
- voice: alto
39
- cello:
40
- family: string
41
- classifications:
42
18
  - string
43
- - chordophone
44
- - bowed
45
- default_clefs:
19
+ family: string
20
+ standard_staves:
46
21
  - bass_clef
47
- voice: tenor
22
+ clarinet:
23
+ classifications:
24
+ - aerophone
25
+ - reed
26
+ - single_reed
27
+ - wind
28
+ - woodwind
29
+ family: woodwind
30
+ standard_staves:
31
+ - treble_clef
48
32
  double_bass:
49
- family: string
50
33
  classifications:
51
34
  - string
52
35
  - chordophone
53
36
  - bowed
54
- default_clefs:
37
+ family: string
38
+ standard_staves:
55
39
  - bass_clef
56
- voice: bass
40
+ flute:
41
+ classifications:
42
+ - aerophone
43
+ - edge-blown aerophone
44
+ - wind
45
+ - woodwind
46
+ family: woodwind
47
+ standard_staves:
48
+ - treble_clef
49
+ french_horn:
50
+ alias_name_keys:
51
+ - horn
52
+ classifications:
53
+ - aerophone
54
+ - brass
55
+ - wind
56
+ family: brass
57
+ standard_staves:
58
+ - treble_clef
59
+ guitar:
60
+ classifications:
61
+ - chordophone
62
+ - plucked
63
+ - string
64
+ family: string
65
+ standard_staves:
66
+ - treble_clef
57
67
  harp:
58
68
  family: string
59
69
  classifications:
60
- - string
61
70
  - chordophone
62
71
  - plucked
63
- default_clefs:
72
+ - string
73
+ standard_staves:
64
74
  - treble_clef
65
75
  - bass_clef
66
- flute:
67
- family: woodwind
76
+ oboe:
68
77
  classifications:
69
- - woodwind
70
- - wind
71
78
  - aerophone
72
- - edge-blown aerophone
73
- default_clefs:
79
+ - reed
80
+ - double_reed
81
+ - wind
82
+ - woodwind
83
+ family: woodwind
84
+ standard_staves:
74
85
  - treble_clef
75
- voice: soprano
76
86
  organ:
77
87
  classifications:
78
88
  - aerophone
79
89
  - keyboard
80
- default_clefs:
90
+ standard_staves:
91
+ - treble_clef
92
+ - bass_clef
93
+ - bass_clef
94
+ piano:
95
+ classifications:
96
+ - chordophone
97
+ - keyboard
98
+ - percussion
99
+ - string
100
+ family: percussion
101
+ standard_staves:
81
102
  - treble_clef
82
103
  - bass_clef
104
+ trombone:
105
+ classifications:
106
+ - aerophone
107
+ - brass
108
+ - wind
109
+ family: brass
110
+ standard_staves:
111
+ - bass_clef
112
+ trumpet:
113
+ classifications:
114
+ - aerophone
115
+ - brass
116
+ - wind
117
+ family: brass
118
+ standard_staves:
119
+ - treble_clef
120
+ tuba:
121
+ classifications:
122
+ - aerophone
123
+ - brass
124
+ - wind
125
+ family: brass
126
+ standard_staves:
83
127
  - bass_clef
128
+ viola:
129
+ family: string
130
+ classifications:
131
+ - bowed
132
+ - chordophone
133
+ - string
134
+ standard_staves:
135
+ - alto_clef
136
+ violin:
137
+ alias_name_keys:
138
+ - fiddle
139
+ classifications:
140
+ - bowed
141
+ - chordophone
142
+ - string
143
+ family: string
144
+ standard_staves:
145
+ - treble_clef
@@ -304,7 +304,7 @@ class HeadMusic::DiatonicInterval
304
304
  def inversion
305
305
  inverted_low_pitch = lower_pitch
306
306
  while inverted_low_pitch < higher_pitch
307
- inverted_low_pitch = HeadMusic::Pitch.fetch_or_create(lower_pitch.spelling, inverted_low_pitch.octave + 1)
307
+ inverted_low_pitch = HeadMusic::Pitch.fetch_or_create(lower_pitch.spelling, inverted_low_pitch.register + 1)
308
308
  end
309
309
  HeadMusic::DiatonicInterval.new(higher_pitch, inverted_low_pitch)
310
310
  end
@@ -17,7 +17,7 @@ class HeadMusic::Instrument
17
17
  INSTRUMENTS.map { |key, _data| get(key) }.sort_by(&:name)
18
18
  end
19
19
 
20
- attr_reader :name_key, :family, :default_clefs, :classifications, :voice
20
+ attr_reader :name_key, :family, :standard_staves, :classifications
21
21
 
22
22
  def ==(other)
23
23
  to_s == other.to_s
@@ -67,9 +67,8 @@ class HeadMusic::Instrument
67
67
  def initialize_data_from_record(record)
68
68
  @name_key = record['name_key'].to_sym
69
69
  @family = record['family']
70
- @default_clefs = record['default_clefs'] || []
70
+ @standard_staves = record['standard_staves'] || []
71
71
  @classifications = record['classifications'] || []
72
- @voice = record['voice'].to_s
73
72
  self.name = I18n.translate(name_key, scope: 'instruments', locale: 'en', default: inferred_name)
74
73
  end
75
74
 
@@ -86,6 +86,7 @@ de:
86
86
  hammered_dulcimer: Hackbrett
87
87
  harp: Harfe
88
88
  harpsichord: Cembalo
89
+ horn: Horn
89
90
  lute: Laute
90
91
  mandolin: Mandoline
91
92
  marimba: Marimbaphon
@@ -124,6 +124,7 @@ en:
124
124
  hammered_dulcimer: hammered dulcimer
125
125
  harp: harp
126
126
  harpsichord: harpsichord
127
+ horn: horn
127
128
  lute: lute
128
129
  mandolin: mandolin
129
130
  maracas: maracas
@@ -80,6 +80,7 @@ es:
80
80
  hammered_dulcimer: dulcémele
81
81
  harp: arpa
82
82
  harpsichord: clavecémbalo
83
+ horn: corno
83
84
  lute: laúd
84
85
  mandolin: mandolina
85
86
  marimba: marimba
@@ -92,6 +92,7 @@ fr:
92
92
  hammered_dulcimer: tympanon
93
93
  harp: harpe
94
94
  harpsichord: clavecin
95
+ horn: cor
95
96
  lute: luth
96
97
  mandolin: mandoline
97
98
  marimba: marimba
@@ -84,6 +84,7 @@ it:
84
84
  hammered_dulcimer: salterio
85
85
  harp: arpa
86
86
  harpsichord: clavicembalo
87
+ horn: corno
87
88
  lute: liuto
88
89
  mandolin: mandolino
89
90
  marimba: marimba
@@ -40,6 +40,7 @@ ru:
40
40
  hammered_dulcimer: tsimbaly
41
41
  harp: arfa
42
42
  harpsichord: klavesin
43
+ horn: gorn
43
44
  lute: liutnia
44
45
  mandolin: mandolina
45
46
  marimba: marimba
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # A pitch is a named frequency represented by a spelling and an octive.
3
+ # A pitch is a named frequency represented by a spelling and a register.
4
4
  class HeadMusic::Pitch
5
5
  include Comparable
6
6
 
7
- attr_reader :spelling
8
- attr_reader :octave
7
+ attr_reader :spelling, :register
9
8
 
10
9
  delegate :letter_name, to: :spelling
11
10
  delegate :series_ascending, :series_descending, to: :letter_name, prefix: true
@@ -51,7 +50,7 @@ class HeadMusic::Pitch
51
50
  def self.from_name(name)
52
51
  return nil unless name == name.to_s
53
52
 
54
- fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Octave.get(name).to_i)
53
+ fetch_or_create(HeadMusic::Spelling.get(name), HeadMusic::Register.get(name).to_i)
55
54
  end
56
55
 
57
56
  def self.from_number(number)
@@ -66,7 +65,7 @@ class HeadMusic::Pitch
66
65
  sign_interval = natural_letter_pitch.smallest_interval_to(HeadMusic::PitchClass.get(number))
67
66
  sign = HeadMusic::Sign.by(:semitones, sign_interval) if sign_interval != 0
68
67
  spelling = HeadMusic::Spelling.fetch_or_create(letter_name, sign)
69
- fetch_or_create(spelling, natural_letter_pitch.octave)
68
+ fetch_or_create(spelling, natural_letter_pitch.register)
70
69
  end
71
70
 
72
71
  def self.natural_letter_pitch(number, letter_name)
@@ -76,26 +75,26 @@ class HeadMusic::Pitch
76
75
  get(natural_letter_pitch)
77
76
  end
78
77
 
79
- def self.fetch_or_create(spelling, octave = nil)
80
- octave ||= HeadMusic::Octave::DEFAULT
81
- return unless spelling && (-1..9).cover?(octave)
78
+ def self.fetch_or_create(spelling, register = nil)
79
+ register ||= HeadMusic::Register::DEFAULT
80
+ return unless spelling && (-1..9).cover?(register)
82
81
 
83
82
  @pitches ||= {}
84
- hash_key = [spelling, octave].join
85
- @pitches[hash_key] ||= new(spelling, octave)
83
+ hash_key = [spelling, register].join
84
+ @pitches[hash_key] ||= new(spelling, register)
86
85
  end
87
86
 
88
- def initialize(spelling, octave)
87
+ def initialize(spelling, register)
89
88
  @spelling = HeadMusic::Spelling.get(spelling.to_s)
90
- @octave = octave.to_i
89
+ @register = register.to_i
91
90
  end
92
91
 
93
92
  def name
94
- [spelling, octave].join
93
+ [spelling, register].join
95
94
  end
96
95
 
97
96
  def midi_note_number
98
- (octave + 1) * 12 + letter_name.pitch_class.to_i + sign_semitones.to_i
97
+ (register + 1) * 12 + letter_name.pitch_class.to_i + sign_semitones.to_i
99
98
  end
100
99
 
101
100
  alias midi midi_note_number
@@ -109,6 +108,10 @@ class HeadMusic::Pitch
109
108
  midi_note_number
110
109
  end
111
110
 
111
+ def helmholtz_notation
112
+ helmholtz_letter_name + helmholtz_marks
113
+ end
114
+
112
115
  def natural
113
116
  HeadMusic::Pitch.get(to_s.gsub(HeadMusic::Sign.matcher, ''))
114
117
  end
@@ -150,7 +153,7 @@ class HeadMusic::Pitch
150
153
  end
151
154
 
152
155
  def natural_steps(num_steps)
153
- HeadMusic::Pitch.get([target_letter_name(num_steps), octave + octaves_delta(num_steps)].join)
156
+ HeadMusic::Pitch.get([target_letter_name(num_steps), register + octaves_delta(num_steps)].join)
154
157
  end
155
158
 
156
159
  def frequency
@@ -167,7 +170,7 @@ class HeadMusic::Pitch
167
170
  private
168
171
 
169
172
  def octave_changes_to(other)
170
- other.octave - octave - octave_adjustment_to(other)
173
+ other.register - register - octave_adjustment_to(other)
171
174
  end
172
175
 
173
176
  def octave_adjustment_to(other)
@@ -212,4 +215,14 @@ class HeadMusic::Pitch
212
215
  @target_letter_name ||= {}
213
216
  @target_letter_name[num_steps] ||= letter_name.steps_up(num_steps)
214
217
  end
218
+
219
+ def helmholtz_letter_name
220
+ return spelling.to_s.downcase if HeadMusic::Register.get(register).helmholtz_case == :lower
221
+
222
+ spelling.to_s
223
+ end
224
+
225
+ def helmholtz_marks
226
+ HeadMusic::Register.get(register).helmholtz_marks
227
+ end
215
228
  end
@@ -15,7 +15,7 @@ class HeadMusic::Pitch::OctaveEquivalence
15
15
 
16
16
  def octave_equivalent?(other)
17
17
  other = HeadMusic::Pitch.get(other)
18
- pitch.spelling == other.spelling && pitch.octave != other.octave
18
+ pitch.spelling == other.spelling && pitch.register != other.register
19
19
  end
20
20
 
21
21
  alias equivalent? octave_equivalent?
@@ -187,7 +187,7 @@ class HeadMusic::PitchSet
187
187
 
188
188
  def reduction_pitches
189
189
  pitches.map do |pitch|
190
- pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.octave - 1) while pitch > bass_pitch + 12
190
+ pitch = HeadMusic::Pitch.fetch_or_create(pitch.spelling, pitch.register - 1) while pitch > bass_pitch + 12
191
191
  pitch
192
192
  end.sort
193
193
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The register is a numeric octave identifier used in scientific pitch notation.
4
+ #
5
+ # A pitch is a spelling plus a register. For example, C4 is middle C and C5 is the C one octave higher.
6
+ # The number changes between the letter names B and C regardless of sharps and flats,
7
+ # so as an extreme example, Cb5 is actually a semitone below B#4.
8
+ class HeadMusic::Register
9
+ include Comparable
10
+
11
+ DEFAULT = 4
12
+
13
+ def self.get(identifier)
14
+ from_number(identifier) || from_name(identifier) || default
15
+ end
16
+
17
+ def self.from_number(identifier)
18
+ return nil unless identifier.to_s == identifier.to_i.to_s
19
+ return nil unless (-2..12).cover?(identifier.to_i)
20
+
21
+ @registers ||= {}
22
+ @registers[identifier.to_i] ||= new(identifier.to_i)
23
+ end
24
+
25
+ def self.from_name(string)
26
+ return unless string.to_s.match?(HeadMusic::Spelling::MATCHER)
27
+
28
+ _letter, _sign, register_string = string.to_s.match(HeadMusic::Spelling::MATCHER).captures
29
+ @registers ||= {}
30
+ @registers[register_string.to_i] ||= new(register_string.to_i) if register_string
31
+ end
32
+
33
+ def self.default
34
+ @registers[DEFAULT] ||= new(DEFAULT)
35
+ end
36
+
37
+ attr_reader :number
38
+
39
+ delegate :to_i, :to_s, to: :number
40
+
41
+ def initialize(number)
42
+ @number = number
43
+ end
44
+
45
+ def <=>(other)
46
+ to_i <=> other.to_i
47
+ end
48
+
49
+ def +(other)
50
+ self.class.get(to_i + other.to_i)
51
+ end
52
+
53
+ def -(other)
54
+ self.class.get(to_i - other.to_i)
55
+ end
56
+
57
+ def helmholtz_case
58
+ return :upper if number < 3
59
+
60
+ :lower
61
+ end
62
+
63
+ def helmholtz_marks
64
+ return ',' * (2 - number) if number < 2
65
+ return "'" * (number - 3) if number > 3
66
+
67
+ ''
68
+ end
69
+
70
+ private_class_method :new
71
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HeadMusic
4
- VERSION = '0.25.0'
4
+ VERSION = '0.27.0'
5
5
  end
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: 0.25.0
4
+ version: 0.27.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: 2020-08-14 00:00:00.000000000 Z
11
+ date: 2021-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -166,7 +166,6 @@ files:
166
166
  - lib/head_music/motion.rb
167
167
  - lib/head_music/musical_symbol.rb
168
168
  - lib/head_music/named.rb
169
- - lib/head_music/octave.rb
170
169
  - lib/head_music/pitch.rb
171
170
  - lib/head_music/pitch/enharmonic_equivalence.rb
172
171
  - lib/head_music/pitch/octave_equivalence.rb
@@ -175,6 +174,7 @@ files:
175
174
  - lib/head_music/pitch_set.rb
176
175
  - lib/head_music/quality.rb
177
176
  - lib/head_music/reference_pitch.rb
177
+ - lib/head_music/register.rb
178
178
  - lib/head_music/rhythm.rb
179
179
  - lib/head_music/rhythmic_unit.rb
180
180
  - lib/head_music/scale.rb
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # The Octave identifier is a number used in scientific pitch notation.
4
- class HeadMusic::Octave
5
- include Comparable
6
-
7
- DEFAULT = 4
8
-
9
- def self.get(identifier)
10
- from_number(identifier) || from_name(identifier) || default
11
- end
12
-
13
- def self.from_number(identifier)
14
- return nil unless identifier.to_s == identifier.to_i.to_s
15
- return nil unless (-2..12).cover?(identifier.to_i)
16
-
17
- @octaves ||= {}
18
- @octaves[identifier.to_i] ||= new(identifier.to_i)
19
- end
20
-
21
- def self.from_name(string)
22
- return unless string.to_s.match?(HeadMusic::Spelling::MATCHER)
23
-
24
- _letter, _sign, octave_string = string.to_s.match(HeadMusic::Spelling::MATCHER).captures
25
- @octaves ||= {}
26
- @octaves[octave_string.to_i] ||= new(octave_string.to_i) if octave_string
27
- end
28
-
29
- def self.default
30
- @octaves[DEFAULT] ||= new(DEFAULT)
31
- end
32
-
33
- attr_reader :number
34
-
35
- delegate :to_i, :to_s, to: :number
36
-
37
- def initialize(number)
38
- @number = number
39
- end
40
-
41
- def <=>(other)
42
- to_i <=> other.to_i
43
- end
44
-
45
- def +(other)
46
- self.class.get(to_i + other.to_i)
47
- end
48
-
49
- def -(other)
50
- self.class.get(to_i - other.to_i)
51
- end
52
-
53
- private_class_method :new
54
- end