music_set_theory 0.0.4 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebde4ba3edcbccaba9146b39ee39fbe29757cb5283924768c953a517782e09f8
4
- data.tar.gz: be3cfa795e830ce5f5f0534eb3f62368127dfa70e3abcb5055815dc63cad822c
3
+ metadata.gz: 2b66a2a0c031f2d15f603607c3ce78762a0f3e738f63d968c917fd7872618721
4
+ data.tar.gz: 505349cc9f85f8ecaaa890090522153a35e34c452707fb54f3d4112de5317a06
5
5
  SHA512:
6
- metadata.gz: d03972156c15ecc210b0fcb02df239e743e96e6b22f415d3ec153e4769b9e07d44b5bd721b5c5417ef91e836d1c4fa847e7d5c4abfb28840ab4a87d064d2cc8a
7
- data.tar.gz: 697638e92311f8f085c4d82537169c6a80fe38df64224df8ab597b77f86c22626c36f69cd47c393454ad3260ae575d155c27b61cc00dd019ef08acd9b1add1af
6
+ metadata.gz: 456d90f4f7e2b1c263381554d4587a2052285822d357af26417362dc8b0fc7d4386a18dc15f60b0ed03f883209f0ca20cfbd65adf079007b83a9439b2539cedb
7
+ data.tar.gz: 192520afd4ac5954fc1b933b4560061f7958d8a809036b08443fb44ed73a2f4eabf5a655377af2bb9d24c0e25fc33b56255c854e78e15b5af4976cd7fb4a5b9d
@@ -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}: #{v}"
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}: #{v}"
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}: #{v}"
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 'romannumerals'
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: a name of a scale like "Dorian".
224
- # key: generally a standard music key like "C".
225
- # possiblechords: a sequence of chord types like "Seventh" and "Ninth".
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
- def populate_scale_chords( scale_name, key, possiblechords )
228
- our_scale = WestTemp.get_nseqby_name(scale_name, NSEQ_SCALE);
229
- num_elem = len(our_scale.nseq_posn);
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
- our_slice = CHORDTYPE_DICT[i];
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
- our_chord = WestTemp.get_nseqby_seqpos(our_posn, NSEQ_CHORD)
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
- # thestring += "<th>%s</th>\n" % str(int_to_roman(q+1));
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)))
@@ -250,6 +250,9 @@ module MusicSetTheory
250
250
  # it (using slice, if not none). If and only if raz is True, the
251
251
  # result is rotated-and-zeroed.
252
252
  #
253
+ # ==== See Also
254
+ # - chord_generator.rb:`populate_scale_chords()`
255
+ #
253
256
  def get_posn_for_offset( rotate_by=0, slice=nil, raz: false )
254
257
  if slice
255
258
  modulus = self.nseq_temp.no_nat_keys
@@ -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
- seq_maps: [:seq_dict],
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 None, then the preference depends on...
550
- # sharp_not_flat:: if True, returns the sharpened accidental form
551
- # (e.g., "C#"); if False, returns the flattened accidental form
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. The
686
- # arguments:
687
- # elem: the element to add to the dictionary .
688
- # nseq_type: the type of the elemenet (such as scale or chord).
689
- # name_s: a string, or a sequence of strings. This provides names
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: a string, or a sequence of strings. This provides
745
+ # abbrv_s:: a string, or a sequence of strings. This provides
692
746
  # abbreviations as keys that map onto elem.
693
- # seqpos: a sequence. A tuple form will be used as a key that
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.seq_maps.add_elem(elem, nseq_type, name_s, abbrv_s, seqpos)
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.seq_maps.check_nseqby_subdict(nseq_type)
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.seq_maps.check_nseqby_name(nseq_type, name)
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.seq_maps.check_nseqby_abbrv(nseq_type, abbrv)
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.seq_maps.check_nseqby_seqpos(nseq_type, seqpos)
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.seq_maps.check_nseqby_name(nseq_type, name)
727
- self.seq_maps.get_nseqby_name(name, nseq_type)
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
 
@@ -766,9 +820,9 @@ module MusicSetTheory
766
820
  # ex. D = 2 (i.e., C + #*2), E = 4 (C + #*4), etc.
767
821
  # CHROM_NAT_NOTES:: natural notes.
768
822
  #
823
+ CHROM_SIZE = 12
769
824
  CHROM_NAT_NOTES = ["C", "D", "E", "F", "G", "A", "B"]
770
825
  CHROM_NAT_NOTE_POS = [ 0, 2, 4, 5, 7, 9, 11]
771
- CHROM_SIZE = 12
772
826
  def self.WestTempNew
773
827
  Temperament.new(CHROM_SIZE, CHROM_NAT_NOTES, CHROM_NAT_NOTE_POS)
774
828
  end
@@ -4,7 +4,7 @@
4
4
  #
5
5
 
6
6
  module MusicSetTheory
7
- VERSION = "0.0.4"
7
+ VERSION = "0.0.5"
8
8
  end
9
9
 
10
10
 
@@ -20,5 +20,8 @@ require_relative "music_set_theory/temperament"
20
20
  require_relative "music_set_theory/scales"
21
21
  require_relative "music_set_theory/chords"
22
22
 
23
+ #
24
+ require_relative "music_set_theory/chord_generator"
25
+
23
26
 
24
27
  #### endof filename: music_set_theory.rb.
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
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - YAMAMOTO, Masayuki
@@ -59,12 +59,12 @@ files:
59
59
  - lib/music_set_theory/temperament.rb
60
60
  - lib/music_set_theory/version.rb
61
61
  - sig/music_theory.rbs
62
- homepage: https://voidptrjp.blogspot.com/2025/07/musictheorygem.html
62
+ homepage: https://voidptrjp.blogspot.com/2025/07/musicsettheorygem.html
63
63
  licenses:
64
64
  - MIT
65
65
  metadata:
66
66
  allowed_push_host: https://rubygems.org
67
- homepage_uri: https://voidptrjp.blogspot.com/2025/07/musictheorygem.html
67
+ homepage_uri: https://voidptrjp.blogspot.com/2025/07/musicsettheorygem.html
68
68
  source_code_uri: https://github.com/mephistobooks/music_set_theory
69
69
  changelog_uri: https://github.com/mephistobooks/music_set_theory/CHANGELOG.md
70
70
  rdoc_options: []