music_set_theory 0.0.3 → 0.0.5
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/examples/mst_temperament.rb +3 -3
- data/lib/music_set_theory/chord_generator.rb +87 -38
- data/lib/music_set_theory/musutility.rb +21 -3
- data/lib/music_set_theory/scales.rb +126 -25
- data/lib/music_set_theory/temperament.rb +89 -25
- data/lib/music_set_theory/version.rb +1 -1
- data/lib/music_set_theory.rb +3 -0
- metadata +17 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b66a2a0c031f2d15f603607c3ce78762a0f3e738f63d968c917fd7872618721
|
4
|
+
data.tar.gz: 505349cc9f85f8ecaaa890090522153a35e34c452707fb54f3d4112de5317a06
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 456d90f4f7e2b1c263381554d4587a2052285822d357af26417362dc8b0fc7d4386a18dc15f60b0ed03f883209f0ca20cfbd65adf079007b83a9439b2539cedb
|
7
|
+
data.tar.gz: 192520afd4ac5954fc1b933b4560061f7958d8a809036b08443fb44ed73a2f4eabf5a655377af2bb9d24c0e25fc33b56255c854e78e15b5af4976cd7fb4a5b9d
|
data/examples/mst_temperament.rb
CHANGED
@@ -24,19 +24,19 @@ nseqtype_dict.each {|k, v|
|
|
24
24
|
dict = v
|
25
25
|
puts pad2+"name_dict (#{dict.name_dict.keys.size}):"
|
26
26
|
dict.name_dict.each_with_index {|(k, v), i|
|
27
|
-
puts pad2+pad+"(#{i+1}) #{k}
|
27
|
+
puts pad2+pad+"(#{i+1}) #{k}:#{v}"
|
28
28
|
}
|
29
29
|
puts
|
30
30
|
|
31
31
|
puts pad2+"abbr_dict (#{dict.abbr_dict.keys.size}):"
|
32
32
|
dict.abbr_dict.each_with_index {|(k, v), i|
|
33
|
-
puts pad2+pad+"(#{i+1}) #{k}
|
33
|
+
puts pad2+pad+"(#{i+1}) #{k}:#{v}"
|
34
34
|
}
|
35
35
|
puts
|
36
36
|
|
37
37
|
puts pad2+"seqpos_dict (#{dict.seqpos_dict.keys.size}):"
|
38
38
|
dict.seqpos_dict.each_with_index {|(k, v), i|
|
39
|
-
puts pad2+pad+"(#{i+1}) #{k}
|
39
|
+
puts pad2+pad+"(#{i+1}) #{k}:#{v}"
|
40
40
|
}
|
41
41
|
|
42
42
|
puts '-'*40
|
@@ -47,7 +47,7 @@ require_relative "./temperament"
|
|
47
47
|
require_relative "./scales"
|
48
48
|
require_relative "./chords"
|
49
49
|
|
50
|
-
require '
|
50
|
+
require 'romanumerals'
|
51
51
|
|
52
52
|
|
53
53
|
# import copy, codecs
|
@@ -57,6 +57,10 @@ require 'romannumerals'
|
|
57
57
|
# from .scales import MajorScale, MelMinorScale, HarmMinorScale,\
|
58
58
|
# HarmMajorScale;
|
59
59
|
# from .chords import CHORDTYPE_DICT;
|
60
|
+
require_relative "musutility"
|
61
|
+
require_relative "temperament"
|
62
|
+
require_relative "scales"
|
63
|
+
require_relative "chords"
|
60
64
|
|
61
65
|
|
62
66
|
#
|
@@ -142,8 +146,9 @@ module MusicSetTheory
|
|
142
146
|
# >>> print int_to_roman(1999)
|
143
147
|
# MCMXCIX
|
144
148
|
# """
|
145
|
-
def int_to_roman(input)
|
146
|
-
raise "#{input} must be greater than zero." if input <= 0
|
149
|
+
def int_to_roman( input )
|
150
|
+
raise ArgumentError, "#{input} must be greater than zero." if input <= 0
|
151
|
+
raise TypeError, "Input must be an integer." unless input.is_a? Integer
|
147
152
|
|
148
153
|
input.to_roman
|
149
154
|
end
|
@@ -166,12 +171,28 @@ module MusicSetTheory
|
|
166
171
|
|
167
172
|
attr_accessor :full_name, :key, :full_notes, :table_title, :rows
|
168
173
|
def initialize( full_name, key, full_notes, table_title )
|
169
|
-
self.full_name = full_name
|
170
|
-
self.key = key
|
171
|
-
self.full_notes = full_notes
|
172
|
-
self.table_title = table_title
|
173
|
-
self.rows = []
|
174
|
+
self.full_name = full_name
|
175
|
+
self.key = key
|
176
|
+
self.full_notes = full_notes
|
177
|
+
self.table_title = table_title
|
178
|
+
self.rows = []
|
174
179
|
end
|
180
|
+
|
181
|
+
def to_a
|
182
|
+
tmp = []
|
183
|
+
tmp << [ self.full_name, self.key, self.full_notes, self.table_title, ]
|
184
|
+
|
185
|
+
#
|
186
|
+
self.rows.map{|r|
|
187
|
+
# tmp << r.to_a
|
188
|
+
# tmp += r.to_a
|
189
|
+
tmp << r.to_a
|
190
|
+
}
|
191
|
+
|
192
|
+
ret = tmp
|
193
|
+
ret
|
194
|
+
end
|
195
|
+
|
175
196
|
end
|
176
197
|
|
177
198
|
# Represents different rows (triads, 7ths, 9ths) in tabular
|
@@ -180,8 +201,18 @@ module MusicSetTheory
|
|
180
201
|
class ScaleChordRow
|
181
202
|
attr_accessor :chord_type, :notes
|
182
203
|
def initialize( chord_type )
|
183
|
-
self.chord_type = chord_type
|
184
|
-
self.notes = []
|
204
|
+
self.chord_type = chord_type
|
205
|
+
self.notes = []
|
206
|
+
end
|
207
|
+
|
208
|
+
def to_a
|
209
|
+
# ret = "#{self.chord_type}:#{self.notes}"
|
210
|
+
tmp = []
|
211
|
+
self.notes.each do |nt|
|
212
|
+
tmp << [self.chord_type,] + nt.to_a
|
213
|
+
end
|
214
|
+
ret = tmp
|
215
|
+
ret
|
185
216
|
end
|
186
217
|
end
|
187
218
|
|
@@ -189,9 +220,13 @@ module MusicSetTheory
|
|
189
220
|
class ScaleChordCell
|
190
221
|
attr_accessor :chordname_1, :chordname_2, :notes
|
191
222
|
def initialize( chordname_1, chordname_2, notes )
|
192
|
-
self.chordname_1 = chordname_1
|
193
|
-
self.chordname_2 = chordname_2
|
194
|
-
self.notes = notes
|
223
|
+
self.chordname_1 = chordname_1
|
224
|
+
self.chordname_2 = chordname_2
|
225
|
+
self.notes = notes
|
226
|
+
end
|
227
|
+
|
228
|
+
def to_a
|
229
|
+
[self.chordname_1, self.chordname_2, self.notes]
|
195
230
|
end
|
196
231
|
end
|
197
232
|
|
@@ -201,6 +236,8 @@ end
|
|
201
236
|
module MusicSetTheory
|
202
237
|
|
203
238
|
# Used for converting "C" -> 1, "Db"-> 2b, etc.
|
239
|
+
#
|
240
|
+
#
|
204
241
|
def makebaserep( notex, base = 0 )
|
205
242
|
notexparsed = WestTemp.note_parse(notex)
|
206
243
|
#pos_rep = str(WestTemp.nat_key_lookup_order[notexparsed[0]] + base)
|
@@ -211,7 +248,7 @@ module MusicSetTheory
|
|
211
248
|
elsif notexparsed[1] < 0
|
212
249
|
ret = pos_rep + (M_FLAT * (-1 * notexparsed[1]))
|
213
250
|
else
|
214
|
-
ret = pos_rep
|
251
|
+
ret = pos_rep
|
215
252
|
end
|
216
253
|
|
217
254
|
ret
|
@@ -219,25 +256,33 @@ module MusicSetTheory
|
|
219
256
|
|
220
257
|
|
221
258
|
# Returns an instance of scale_chords using the following inputs:
|
222
|
-
#
|
223
|
-
# scale_name
|
224
|
-
# key
|
225
|
-
# possiblechords
|
259
|
+
# ==== Args
|
260
|
+
# scale_name:: a name of a scale like "Dorian".
|
261
|
+
# key:: generally a standard music key like "C".
|
262
|
+
# possiblechords:: a sequence of chord types like "Seventh" and "Ninth".
|
263
|
+
# west_temp:: Temperament object.
|
226
264
|
#
|
227
|
-
|
228
|
-
|
229
|
-
|
265
|
+
# ==== Returns
|
266
|
+
# ScaleChords object.
|
267
|
+
#
|
268
|
+
#def populate_scale_chords( scale_name, key, possiblechords )
|
269
|
+
def populate_scale_chords( scale_name, key, possiblechords,
|
270
|
+
west_temp = WestTemp )
|
271
|
+
our_scale = west_temp.get_nseqby_name(scale_name, NSEQ_SCALE)
|
272
|
+
# num_elem = len(our_scale.nseq_posn)
|
273
|
+
num_elem = our_scale.nseq_posn.size
|
274
|
+
|
230
275
|
begin
|
231
276
|
int_of_key = key.to_i
|
232
|
-
is_key_an_int = true
|
277
|
+
is_key_an_int = true
|
233
278
|
rescue ValueError
|
234
|
-
is_key_an_int = false
|
235
|
-
int_of_key = nil
|
279
|
+
is_key_an_int = false
|
280
|
+
int_of_key = nil
|
236
281
|
end
|
237
282
|
|
238
283
|
if is_key_an_int
|
239
284
|
#our_scale_notes = [makebaserep(x, int_of_key) for x in
|
240
|
-
#our_scale.get_notes_for_key("C")]
|
285
|
+
#our_scale.get_notes_for_key("C")]
|
241
286
|
our_scale_notes = our_scale.get_notes_for_key("C").map{|x|
|
242
287
|
makebaserep(x, int_of_key) }
|
243
288
|
else
|
@@ -251,19 +296,22 @@ module MusicSetTheory
|
|
251
296
|
ourchordrow = ScaleChordRow.new(i)
|
252
297
|
our_chord_data.rows.append(ourchordrow)
|
253
298
|
|
254
|
-
for j in range(num_elem):
|
255
|
-
|
299
|
+
# for j in range(num_elem):
|
300
|
+
for j in 0...num_elem
|
301
|
+
our_slice = CHORDTYPE_DICT[i]
|
302
|
+
|
256
303
|
if is_key_an_int
|
257
304
|
#our_chord_notes = [makebaserep(x, int_of_key) for x in
|
258
|
-
# our_scale.get_notes_for_key("C", j, our_slice)]
|
305
|
+
# our_scale.get_notes_for_key("C", j, our_slice)]
|
259
306
|
our_chord_notes = our_scale.get_notes_for_key("C", j, our_slice).
|
260
307
|
map{|x| makebaserep(x, int_of_key) }
|
261
308
|
else
|
262
309
|
our_chord_notes = our_scale.get_notes_for_key(key, j, our_slice)
|
263
310
|
end
|
264
311
|
|
265
|
-
our_posn = our_scale.get_posn_for_offset(j, our_slice, true)
|
266
|
-
|
312
|
+
#our_posn = our_scale.get_posn_for_offset(j, our_slice, true)
|
313
|
+
our_posn = our_scale.get_posn_for_offset(j, our_slice, raz: true)
|
314
|
+
our_chord = west_temp.get_nseqby_seqpos(our_posn, NSEQ_CHORD)
|
267
315
|
if our_chord
|
268
316
|
ourchordrow.notes.append(ScaleChordCell.new(our_chord.nseq_name,
|
269
317
|
our_chord.nseq_abbrev, our_chord_notes))
|
@@ -295,16 +343,17 @@ module MusicSetTheory
|
|
295
343
|
thestring += "<caption>%s</caption>\n" %
|
296
344
|
(scale.key + " " + scale.full_name +
|
297
345
|
": " + seqtostr(scale.full_notes))
|
298
|
-
thestring += "<thead>\n" + startrow + "<th>Chord Types</th>\n"
|
299
|
-
for q in range(7)
|
300
|
-
|
346
|
+
thestring += "<thead>\n" + startrow + "<th>Chord Types</th>\n"
|
347
|
+
# for q in range(7)
|
348
|
+
for q in 0...7
|
349
|
+
thestring += "<th>%s</th>\n" % str(int_to_roman(q+1))
|
301
350
|
end
|
302
|
-
thestring += endrow + "</thead>\n<tbody>\n"
|
351
|
+
thestring += endrow + "</thead>\n<tbody>\n"
|
303
352
|
|
304
|
-
for i in scale.rows
|
305
|
-
thestring += startrow
|
306
|
-
thestring += "<td>%s</td>\n" % i.chord_type
|
307
|
-
for j in i.notes
|
353
|
+
for i in scale.rows
|
354
|
+
thestring += startrow
|
355
|
+
thestring += "<td>%s</td>\n" % i.chord_type
|
356
|
+
for j in i.notes
|
308
357
|
if not j.chordname_1
|
309
358
|
thestring += ("<td><p>%s<br />" % (str(j.chordname_1)))
|
310
359
|
thestring += ("<i>%s</i><br />" % (str(j.chordname_2)))
|
@@ -264,17 +264,35 @@ end
|
|
264
264
|
|
265
265
|
require 'active_support'
|
266
266
|
require 'active_support/core_ext/object/deep_dup'
|
267
|
+
require 'ice_nine'
|
267
268
|
module MusicSetTheory
|
268
269
|
module MusUtility
|
269
270
|
|
270
271
|
def deep_freeze( obj )
|
272
|
+
IceNine.deep_freeze(obj)
|
273
|
+
end
|
274
|
+
|
275
|
+
def deep_melt( obj )
|
271
276
|
case obj
|
272
277
|
when Hash
|
273
|
-
obj.
|
278
|
+
obj.each_with_object({}) do |(key, value), new_hash|
|
279
|
+
new_hash[deep_melt(key)] = deep_melt(value)
|
280
|
+
end
|
274
281
|
when Array
|
275
|
-
obj.
|
282
|
+
obj.map { |element| deep_melt(element) }
|
283
|
+
when Object
|
284
|
+
# インスタンス変数の解除
|
285
|
+
obj.instance_variables.each do |var|
|
286
|
+
deep_melt(var)
|
287
|
+
value = obj.instance_variable_get(var)
|
288
|
+
obj.remove_instance_variable(var)
|
289
|
+
obj.instance_variable_set(var, deep_melt(value))
|
290
|
+
end
|
291
|
+
obj.dup rescue obj # 凍結状態を解除
|
292
|
+
else
|
293
|
+
obj.dup rescue obj
|
276
294
|
end
|
277
|
-
obj.
|
295
|
+
obj.dup rescue obj
|
278
296
|
end
|
279
297
|
|
280
298
|
end
|
@@ -121,6 +121,9 @@ module MusicSetTheory
|
|
121
121
|
# Note: it is easier to define scales using the noteseq_scale class (this
|
122
122
|
# file) and chord using the noteseq_chord class (in chords.py)
|
123
123
|
#
|
124
|
+
# ==== Warning
|
125
|
+
# - ! This method updates `nseq_temp`.
|
126
|
+
#
|
124
127
|
attr_accessor :nseq_name, :nseq_type, :nseq_temp, :nseq_posn
|
125
128
|
attr_accessor :nseq_nat_posns, :nseq_abbrev, :nseq_synonyms,
|
126
129
|
:nseq_other_abbrevs
|
@@ -132,7 +135,10 @@ module MusicSetTheory
|
|
132
135
|
nseq_other_abbrevs: [] )
|
133
136
|
self.nseq_name = nseq_name
|
134
137
|
self.nseq_type = nseq_type
|
138
|
+
|
135
139
|
self.nseq_temp = nseq_temp
|
140
|
+
# self.nseq_temp = deep_melt(nseq_temp.deep_dup)
|
141
|
+
|
136
142
|
self.nseq_posn = nseq_posn
|
137
143
|
|
138
144
|
self.nseq_nat_posns = nseq_nat_posns
|
@@ -140,7 +146,8 @@ module MusicSetTheory
|
|
140
146
|
self.nseq_synonyms = nseq_synonyms
|
141
147
|
self.nseq_other_abbrevs = nseq_other_abbrevs
|
142
148
|
|
143
|
-
|
149
|
+
#
|
150
|
+
self.register_with_temp(self.nseq_temp)
|
144
151
|
end
|
145
152
|
|
146
153
|
#
|
@@ -189,8 +196,10 @@ module MusicSetTheory
|
|
189
196
|
|
190
197
|
# Registers this note sequence with the underlying temperament, so
|
191
198
|
# that it can be looked up by name, by abbreviation or by sequence.
|
199
|
+
# ==== Warning
|
200
|
+
# - This methods updates `self.nseq_temp`
|
192
201
|
#
|
193
|
-
def register_with_temp
|
202
|
+
def register_with_temp( nseq_temp = self.nseq_temp )
|
194
203
|
if self.nseq_synonyms
|
195
204
|
#if self.nseq_name not in self.nseq_synonyms
|
196
205
|
if !(self.nseq_synonyms.include? self.nseq_name)
|
@@ -212,7 +221,9 @@ module MusicSetTheory
|
|
212
221
|
else
|
213
222
|
our_abbrevs = [self.nseq_abbrev]
|
214
223
|
end
|
215
|
-
|
224
|
+
|
225
|
+
#
|
226
|
+
nseq_temp.seq_maps.add_elem(
|
216
227
|
self, self.nseq_type, our_names, our_abbrevs, self.nseq_posn)
|
217
228
|
end
|
218
229
|
|
@@ -239,6 +250,9 @@ module MusicSetTheory
|
|
239
250
|
# it (using slice, if not none). If and only if raz is True, the
|
240
251
|
# result is rotated-and-zeroed.
|
241
252
|
#
|
253
|
+
# ==== See Also
|
254
|
+
# - chord_generator.rb:`populate_scale_chords()`
|
255
|
+
#
|
242
256
|
def get_posn_for_offset( rotate_by=0, slice=nil, raz: false )
|
243
257
|
if slice
|
244
258
|
modulus = self.nseq_temp.no_nat_keys
|
@@ -344,55 +358,114 @@ module MusicSetTheory
|
|
344
358
|
DISC_MIN_NOTE_POS = [0, 2, 3, 5, 6, 9, 11]
|
345
359
|
HUNGARIAN_NOTE_POS = [0, 3, 4, 6, 7, 9, 10]
|
346
360
|
|
361
|
+
|
347
362
|
# For ease of comprehension, we have the list of modes as arrays which
|
348
363
|
# can be browsed from outside.
|
349
|
-
|
364
|
+
MAJOR_MODES = [ "Ionian",
|
350
365
|
"Dorian",
|
351
366
|
"Phrygian",
|
352
367
|
"Lydian",
|
353
368
|
"Mixolydian",
|
354
369
|
"Aeolian",
|
355
370
|
"Locrian", ]
|
371
|
+
MAJORMODES = MAJOR_MODES
|
356
372
|
|
357
|
-
|
373
|
+
MEL_MINOR_MODES = [ "Jazz Minor",
|
358
374
|
"Dorian " + M_FLAT + "9",
|
359
375
|
"Lydian Augmented",
|
360
376
|
"Lydian Dominant",
|
361
377
|
"Mixolydian " + M_FLAT + "13",
|
362
378
|
"Semilocrian",
|
363
379
|
"Superlocrian", ]
|
380
|
+
MELMINORMODES = MEL_MINOR_MODES
|
364
381
|
|
365
|
-
|
382
|
+
HARM_MINOR_MODES = [ "Harmonic Minor",
|
366
383
|
"Locrian " + M_SHARP + "6",
|
367
384
|
"Ionian Augmented",
|
368
385
|
"Romanian",
|
369
386
|
"Phrygian Dominant",
|
370
387
|
"Lydian " + M_SHARP + "2",
|
371
388
|
"Ultralocrian", ]
|
389
|
+
HARMINORMODES = HARM_MINOR_MODES # for compatibility... typo?
|
372
390
|
|
373
|
-
|
391
|
+
HARM_MAJOR_MODES = [ "Harmonic Major",
|
374
392
|
"Dorian " + M_FLAT + "6",
|
375
393
|
"Phrygian " + M_FLAT + "4",
|
376
394
|
"Lydian " + M_FLAT + "3",
|
377
395
|
"Mixolydian " + M_FLAT + "9",
|
378
396
|
"Lydian " + M_SHARP + "2 " + M_SHARP + "5",
|
379
397
|
"Locrian " + M_FLAT + M_FLAT + "7", ]
|
398
|
+
HARMMAJORMODES = HARM_MAJOR_MODES
|
380
399
|
|
381
|
-
|
400
|
+
DISCORD_MIN_MODES = [ "Melodic Minor " + M_FLAT + "5",
|
382
401
|
"Dorian " + M_FLAT + "9 " + M_FLAT + "4",
|
383
402
|
"Minor Lydian Augmented",
|
384
403
|
"Lydian Dominant " + M_FLAT + "9",
|
385
404
|
"Lydian Augmented " + M_SHARP + "2 " + M_SHARP + "3",
|
386
405
|
"Semilocrian " + M_FLAT + M_FLAT + "7",
|
387
406
|
"Superlocrian " + M_FLAT + M_FLAT + "6", ]
|
407
|
+
DISCORDMINMODES = DISCORD_MIN_MODES
|
388
408
|
|
389
|
-
|
409
|
+
HUNGARIAN_MODES = [ "Hungarian",
|
390
410
|
"Superlocrian " + M_FLAT + M_FLAT + "6 " + M_FLAT + M_FLAT + "7",
|
391
411
|
"Harmonic Minor " + M_FLAT + "5",
|
392
412
|
"Superlocrian " + M_SHARP + "6",
|
393
413
|
"Melodic Minor " + M_SHARP + "5",
|
394
414
|
"Dorian " + M_FLAT + "9 " + M_SHARP + "11",
|
395
415
|
"Lydian Augmented " + M_SHARP + "3", ]
|
416
|
+
HUNGARIANMODES = HUNGARIAN_MODES
|
417
|
+
|
418
|
+
MODE_ARRAY = [
|
419
|
+
MAJOR_MODES,
|
420
|
+
MEL_MINOR_MODES,
|
421
|
+
HARM_MINOR_MODES,
|
422
|
+
HARM_MAJOR_MODES,
|
423
|
+
DISCORD_MIN_MODES,
|
424
|
+
HUNGARIAN_MODES,
|
425
|
+
]
|
426
|
+
end
|
427
|
+
|
428
|
+
module MusicSetTheory
|
429
|
+
|
430
|
+
#
|
431
|
+
#
|
432
|
+
#
|
433
|
+
Pentatonic_NAT_POSNS = (0...5).to_a # 5音音階
|
434
|
+
Hexatonic_NAT_POSNS = (0...6).to_a # 6音音階
|
435
|
+
Heptatonic_NAT_POSNS = HEPT_NAT_POSNS # 7音音階
|
436
|
+
Octatonic_NAT_POSNS = (0...8).to_a # 8音音階
|
437
|
+
|
438
|
+
# Pentatonic.
|
439
|
+
#
|
440
|
+
#
|
441
|
+
MAJOR_PENTA_NOTE_POS = [ 0, 2, 4, 7, 9, ]
|
442
|
+
MINOR_PENTA_NOTE_POS = [ 0, 3, 5, 7, 10, ]
|
443
|
+
RYUKYU_PENTA_NOTE_POS = [ 0, 4, 5, 7, 11, ]
|
444
|
+
|
445
|
+
BLUES_HEXA_NOTE_POS = [ 0, 3, 5, 6, 7, 10, ]
|
446
|
+
|
447
|
+
def self.def_scale( const_name, name: , tment: ,
|
448
|
+
note_pos: , modes: [],
|
449
|
+
scale_array: :ScaleArray )
|
450
|
+
posns = (0...(note_pos.size)).to_a
|
451
|
+
|
452
|
+
tmp = NoteSeqScale.new(name, tment, note_pos, posns, modes)
|
453
|
+
self.const_set(const_name, tmp)
|
454
|
+
ret = self.const_get(const_name)
|
455
|
+
|
456
|
+
self.const_get(scale_array).const_set(const_name,ret)
|
457
|
+
|
458
|
+
ret
|
459
|
+
end
|
460
|
+
|
461
|
+
def self.undef_scale( const_name, scale_array: :ScaleArray )
|
462
|
+
self.const_get(scale_array).send(:remove_const, const_name)
|
463
|
+
self.send(:remove_const, const_name)
|
464
|
+
end
|
465
|
+
|
466
|
+
def self.scales( scale_array: :ScaleArray )
|
467
|
+
self.const_get(scale_array).constants
|
468
|
+
end
|
396
469
|
|
397
470
|
end
|
398
471
|
|
@@ -401,23 +474,51 @@ end
|
|
401
474
|
#
|
402
475
|
#
|
403
476
|
module MusicSetTheory
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
HarmMajorScale = NoteSeqScale.new("Harmonic Major", WestTemp,
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
477
|
+
#ScaleArray = []
|
478
|
+
module ScaleArray; end
|
479
|
+
|
480
|
+
#MajorScale = NoteSeqScale.new("Major", WestTemp,
|
481
|
+
# CHROM_NAT_NOTE_POS, HEPT_NAT_POSNS, MAJORMODES)
|
482
|
+
#MelMinorScale = NoteSeqScale.new("Melodic Minor", WestTemp,
|
483
|
+
# MEL_MIN_NOTE_POS, HEPT_NAT_POSNS, MELMINORMODES)
|
484
|
+
#HarmMinorScale = NoteSeqScale.new("Harmonic Minor", WestTemp,
|
485
|
+
# HARM_MIN_NOTE_POS, HEPT_NAT_POSNS, HARMINORMODES)
|
486
|
+
#HarmMajorScale = NoteSeqScale.new("Harmonic Major", WestTemp,
|
487
|
+
# HARM_MAJ_NOTE_POS, HEPT_NAT_POSNS, HARMMAJORMODES)
|
488
|
+
#DiscMinorScale = NoteSeqScale.new("Discordant Minor", WestTemp,
|
489
|
+
# DISC_MIN_NOTE_POS, HEPT_NAT_POSNS, DISCORDMINMODES)
|
490
|
+
#HungarianScale = NoteSeqScale.new("Hungarian", WestTemp,
|
491
|
+
# HUNGARIAN_NOTE_POS, HEPT_NAT_POSNS, HUNGARIANMODES)
|
492
|
+
def_scale :MajorScale, name: "Major", tment: WestTemp,
|
493
|
+
note_pos: CHROM_NAT_NOTE_POS, modes: MAJOR_MODES
|
494
|
+
|
495
|
+
def_scale :MelMinorScale, name: "Melodic Minor", tment: WestTemp,
|
496
|
+
note_pos: MEL_MIN_NOTE_POS, modes: MEL_MINOR_MODES
|
497
|
+
|
498
|
+
def_scale :HarmMinorScale, name: "Harmonic Minor", tment: WestTemp,
|
499
|
+
note_pos: HARM_MIN_NOTE_POS, modes: HARM_MINOR_MODES
|
500
|
+
|
501
|
+
def_scale :HarmMajorScale, name: "Harmonic Major", tment: WestTemp,
|
502
|
+
note_pos: HARM_MAJ_NOTE_POS, modes: HARM_MAJOR_MODES
|
503
|
+
|
504
|
+
def_scale :DiscMinorScale, name: "Discordant Minor", tment: WestTemp,
|
505
|
+
note_pos: DISC_MIN_NOTE_POS, modes: DISCORD_MIN_MODES
|
506
|
+
|
507
|
+
def_scale :HungarianScale, name: "Hungarian", tment: WestTemp,
|
508
|
+
note_pos: HUNGARIAN_NOTE_POS, modes: HUNGARIAN_MODES
|
509
|
+
|
510
|
+
# Pentatonic scales.
|
511
|
+
def_scale :MajorPentaScale, name: "Major Pentatonic", tment: WestTemp,
|
512
|
+
note_pos: MAJOR_PENTA_NOTE_POS
|
513
|
+
def_scale :MinorPentaScale, name: "Minor Pentatonic", tment: WestTemp,
|
514
|
+
note_pos: MINOR_PENTA_NOTE_POS
|
515
|
+
def_scale :RyukyuPentaScale, name: "Ryukyu Pentatonic", tment: WestTemp,
|
516
|
+
note_pos: RYUKYU_PENTA_NOTE_POS
|
517
|
+
|
518
|
+
# Hexatonic scales.
|
519
|
+
def_scale :BluesHexaScale, name: "Blues", tment: WestTemp,
|
520
|
+
note_pos: BLUES_HEXA_NOTE_POS
|
418
521
|
|
419
|
-
HungarianScale = NoteSeqScale.new("Hungarian", WestTemp,
|
420
|
-
HUNGARIAN_NOTE_POS, HEPT_NAT_POSNS, HUNGARIANMODES)
|
421
522
|
end
|
422
523
|
|
423
524
|
|
@@ -396,13 +396,23 @@ module MusicSetTheory
|
|
396
396
|
# Initialiser.
|
397
397
|
# ==== Args
|
398
398
|
# no_keys:: the number of keys in the temperament.
|
399
|
+
# !number of semitones in one octave. ex. 12.
|
399
400
|
# nat_keys:: an array consisting of the names of the natural
|
400
401
|
# (unsharped or unflattened) keys in the temperament.
|
402
|
+
# ex.
|
403
|
+
# ["C", "D", "E", "F", "G", "A", "B"]
|
404
|
+
#
|
401
405
|
# nat_key_posn:: the position of the natural keys in the temperament.
|
402
406
|
# These should correspond to the elements in nat_keys.
|
403
407
|
# Positions are calculated base zero.
|
408
|
+
# ex.
|
409
|
+
# [ 0, 2, 4, 5, 7, 9, 11]
|
404
410
|
#
|
405
|
-
def initialize( no_keys, nat_keys, nat_key_posn )
|
411
|
+
# def initialize( no_keys, nat_keys, nat_key_posn )
|
412
|
+
def initialize( no_keys = CHROM_SIZE,
|
413
|
+
nat_keys = CHROM_NAT_NOTES,
|
414
|
+
nat_key_posn = CHROM_NAT_NOTE_POS )
|
415
|
+
#
|
406
416
|
self.no_keys = no_keys
|
407
417
|
self.nat_keys = nat_keys
|
408
418
|
self.nat_key_posn = nat_key_posn
|
@@ -436,13 +446,29 @@ module MusicSetTheory
|
|
436
446
|
|
437
447
|
# Useful for dictionary lookup later.
|
438
448
|
#self.seq_maps = seq_dict([NSEQ_SCALE, NSEQ_CHORD], self);
|
439
|
-
#self.seq_maps = SeqDict.new(NSEQ_SCALE, NSEQ_CHORD);
|
440
449
|
self.seq_maps = SeqDict.new([NSEQ_SCALE, NSEQ_CHORD], self)
|
441
450
|
|
442
451
|
end
|
443
452
|
|
453
|
+
# Alias methods of getters+setters.
|
454
|
+
# {
|
455
|
+
# original_base_name: [ name of aliases ],
|
456
|
+
# original_base_name: [ name of aliases ],
|
457
|
+
# :
|
458
|
+
# :
|
459
|
+
# }
|
460
|
+
#
|
444
461
|
{
|
445
|
-
|
462
|
+
no_keys: [ :num_semitones, :num_keys, ],
|
463
|
+
nat_keys: [ :natural_key_names, ],
|
464
|
+
nat_key_posn: [ :natural_key_positions, ],
|
465
|
+
no_nat_keys: [ :num_natural_keys, ],
|
466
|
+
nat_key_pos_lookup: [
|
467
|
+
:natural_key_position_lookup, :natural_key_position_dict, ],
|
468
|
+
pos_lookup_nat_key: [
|
469
|
+
:position_natural_key_lookup, :position_natural_key_dict, ],
|
470
|
+
|
471
|
+
seq_maps: [:seq_dict],
|
446
472
|
}.each do |k, vary|
|
447
473
|
|
448
474
|
vary.each do |new_name|
|
@@ -525,6 +551,9 @@ module MusicSetTheory
|
|
525
551
|
# position of the key in the unit of halftone. ex. key: "C" =>0,
|
526
552
|
# key: "D#" =>3, key: "Cb" =>-1
|
527
553
|
#
|
554
|
+
# ==== See Also
|
555
|
+
# - `#get_key_of_pos()`
|
556
|
+
#
|
528
557
|
def get_pos_of_key( key, debug_f: false )
|
529
558
|
key_parsed = self.note_parse(key)
|
530
559
|
$stderr.puts "key_parsed: #{key_parsed}" if debug_f
|
@@ -534,6 +563,9 @@ module MusicSetTheory
|
|
534
563
|
ret = self.nat_key_pos_lookup[key_parsed[0]] + key_parsed[1]
|
535
564
|
ret
|
536
565
|
end
|
566
|
+
alias pos_of_key get_pos_of_key
|
567
|
+
alias position_of get_pos_of_key
|
568
|
+
alias pos_of get_pos_of_key
|
537
569
|
|
538
570
|
# Given a position in the temperament, this function attempts to
|
539
571
|
# return the "best" key name corresponding to it. This is not a
|
@@ -546,9 +578,9 @@ module MusicSetTheory
|
|
546
578
|
# pos:: the position inside the temperament.
|
547
579
|
# desired_nat_note:: the preferred natural key to start the key name.
|
548
580
|
# For example "C" makes the function return "C#" instead of "Db".
|
549
|
-
# If
|
550
|
-
# sharp_not_flat:: if
|
551
|
-
# (e.g., "C#"); if
|
581
|
+
# If nil, then the preference depends on...
|
582
|
+
# sharp_not_flat:: if true, returns the sharpened accidental form
|
583
|
+
# (e.g., "C#"); if false, returns the flattened accidental form
|
552
584
|
# (i.e., "Db").
|
553
585
|
# ==== Returns
|
554
586
|
#
|
@@ -602,6 +634,8 @@ module MusicSetTheory
|
|
602
634
|
|
603
635
|
return nil
|
604
636
|
end
|
637
|
+
alias key_of_pos get_key_of_pos
|
638
|
+
alias key_of get_key_of_pos
|
605
639
|
|
606
640
|
|
607
641
|
# This function takes a key and a sequence of positions relative to
|
@@ -669,6 +703,8 @@ module MusicSetTheory
|
|
669
703
|
# ["D", "C#", "E"] =>["D", [0, 11, 2]]
|
670
704
|
#
|
671
705
|
# this is returned by the function.
|
706
|
+
# ==== See Also
|
707
|
+
# - `#get_note_sequence()`
|
672
708
|
#
|
673
709
|
def get_keyseq_notes( note_seq )
|
674
710
|
base_key = note_seq[0]
|
@@ -679,52 +715,70 @@ module MusicSetTheory
|
|
679
715
|
[base_key, pos_seq]
|
680
716
|
end
|
681
717
|
|
718
|
+
end
|
719
|
+
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
|
724
|
+
#
|
725
|
+
#
|
726
|
+
#
|
727
|
+
module MusicSetTheory
|
728
|
+
module Temperament
|
729
|
+
|
730
|
+
class Temperament
|
731
|
+
|
682
732
|
|
683
733
|
### The next few functions are rip-offs of SeqDict functions.
|
734
|
+
#
|
735
|
+
#! actually these are the utility methods for SeqDict instance.
|
736
|
+
#
|
737
|
+
|
684
738
|
|
685
|
-
# This adds an element to the dictionary inside the temperament.
|
686
|
-
#
|
687
|
-
# elem
|
688
|
-
# nseq_type
|
689
|
-
# name_s
|
739
|
+
# This adds an element to the dictionary inside the temperament.
|
740
|
+
# ==== Args
|
741
|
+
# elem:: the element to add to the dictionary .
|
742
|
+
# nseq_type:: the type of the elemenet (such as scale or chord).
|
743
|
+
# name_s:: a string, or a sequence of strings. This provides names
|
690
744
|
# as keys that map onto elem.
|
691
|
-
# abbrv_s
|
745
|
+
# abbrv_s:: a string, or a sequence of strings. This provides
|
692
746
|
# abbreviations as keys that map onto elem.
|
693
|
-
# seqpos
|
747
|
+
# seqpos:: a sequence. A tuple form will be used as a key that
|
694
748
|
# maps onto elem.
|
695
749
|
#
|
696
750
|
# If nseq_type is not associated with any of the sub-dictionaries in
|
697
751
|
# the dictionary in the temperament, then this function exits.
|
698
752
|
#
|
699
753
|
def add_elem( elem, nseq_type, name_s, abbrv_s, seqpos )
|
700
|
-
self.
|
754
|
+
self.seq_dict.add_elem(elem, nseq_type, name_s, abbrv_s, seqpos)
|
701
755
|
end
|
702
756
|
|
703
757
|
|
704
758
|
# Checks if there is a subdictionary associated with nseq_type.
|
705
759
|
def check_nseqby_subdict( nseq_type )
|
706
|
-
self.
|
760
|
+
self.seq_dict.check_nseqby_subdict(nseq_type)
|
707
761
|
end
|
708
762
|
|
709
763
|
# Checks if there is a noteseq with a given name.
|
710
764
|
def check_nseqby_name( nseq_type, name )
|
711
|
-
self.
|
765
|
+
self.seq_dict.check_nseqby_name(nseq_type, name)
|
712
766
|
end
|
713
767
|
|
714
768
|
# Checks if there is a noteseq with a given abbreviation.
|
715
769
|
def check_nseqby_abbrv( nseq_type, abbrv )
|
716
|
-
self.
|
770
|
+
self.seq_dict.check_nseqby_abbrv(nseq_type, abbrv)
|
717
771
|
end
|
718
772
|
|
719
773
|
# Checks if there is a noteseq with a given sequence position.
|
720
774
|
def check_nseqby_seqpos( nseq_type, seqpos )
|
721
|
-
self.
|
775
|
+
self.seq_dict.check_nseqby_seqpos(nseq_type, seqpos)
|
722
776
|
end
|
723
|
-
|
777
|
+
|
724
778
|
# Looks up a noteseq (or anything else) by name.
|
725
779
|
def get_nseqby_name( name, nseq_type )
|
726
|
-
if self.
|
727
|
-
self.
|
780
|
+
if self.seq_dict.check_nseqby_name(nseq_type, name)
|
781
|
+
self.seq_dict.get_nseqby_name(name, nseq_type)
|
728
782
|
else
|
729
783
|
nil
|
730
784
|
end
|
@@ -748,8 +802,8 @@ module MusicSetTheory
|
|
748
802
|
end
|
749
803
|
end
|
750
804
|
|
751
|
-
|
752
805
|
end
|
806
|
+
|
753
807
|
end
|
754
808
|
end
|
755
809
|
|
@@ -760,9 +814,15 @@ end
|
|
760
814
|
module MusicSetTheory
|
761
815
|
module Temperament
|
762
816
|
|
763
|
-
|
764
|
-
|
817
|
+
#
|
818
|
+
# CHROM_SIZE:: number of semitones.
|
819
|
+
# CHROM_NAT_NOTE_POS:: position in number of semitones.
|
820
|
+
# ex. D = 2 (i.e., C + #*2), E = 4 (C + #*4), etc.
|
821
|
+
# CHROM_NAT_NOTES:: natural notes.
|
822
|
+
#
|
765
823
|
CHROM_SIZE = 12
|
824
|
+
CHROM_NAT_NOTES = ["C", "D", "E", "F", "G", "A", "B"]
|
825
|
+
CHROM_NAT_NOTE_POS = [ 0, 2, 4, 5, 7, 9, 11]
|
766
826
|
def self.WestTempNew
|
767
827
|
Temperament.new(CHROM_SIZE, CHROM_NAT_NOTES, CHROM_NAT_NOTE_POS)
|
768
828
|
end
|
@@ -772,9 +832,13 @@ module MusicSetTheory
|
|
772
832
|
|
773
833
|
#WestTemp = deep_freeze(WestTempNew()) # the last failed in chords_test(1).
|
774
834
|
#WestTemp = WestTempNew() # the last failed in chords_test(1).
|
775
|
-
WestTemp = MusicSetTheory.deep_freeze(WestTempNew())
|
776
835
|
#ng. WestTemp.deep_freeze
|
777
836
|
|
837
|
+
### freeze for debug.
|
838
|
+
# WestTemp = MusicSetTheory.deep_freeze(WestTempNew())
|
839
|
+
WestTemp = WestTempNew() # ok. (dependancied removed in test cases).
|
840
|
+
WestTment = WestTemp # alias.
|
841
|
+
|
778
842
|
end
|
779
843
|
end
|
780
844
|
|
data/lib/music_set_theory.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: music_set_theory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- YAMAMOTO, Masayuki
|
@@ -23,6 +23,20 @@ dependencies:
|
|
23
23
|
- - "~>"
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '0.2'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: ice_nine
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.11'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.11'
|
26
40
|
description: A music theory library based on Python's `musictheory` package.
|
27
41
|
email:
|
28
42
|
- mephistobooks@users.noreply.github.com
|
@@ -45,12 +59,12 @@ files:
|
|
45
59
|
- lib/music_set_theory/temperament.rb
|
46
60
|
- lib/music_set_theory/version.rb
|
47
61
|
- sig/music_theory.rbs
|
48
|
-
homepage: https://voidptrjp.blogspot.com/2025/07/
|
62
|
+
homepage: https://voidptrjp.blogspot.com/2025/07/musicsettheorygem.html
|
49
63
|
licenses:
|
50
64
|
- MIT
|
51
65
|
metadata:
|
52
66
|
allowed_push_host: https://rubygems.org
|
53
|
-
homepage_uri: https://voidptrjp.blogspot.com/2025/07/
|
67
|
+
homepage_uri: https://voidptrjp.blogspot.com/2025/07/musicsettheorygem.html
|
54
68
|
source_code_uri: https://github.com/mephistobooks/music_set_theory
|
55
69
|
changelog_uri: https://github.com/mephistobooks/music_set_theory/CHANGELOG.md
|
56
70
|
rdoc_options: []
|