Class: MusicSetTheory::Temperament::Temperament
- Inherits:
-
Object
- Object
- MusicSetTheory::Temperament::Temperament
- Defined in:
- lib/music_set_theory/temperament.rb,
lib/music_set_theory/temperament.rb,
lib/music_set_theory/temperament.rb,
lib/music_set_theory/temperament.rb
Overview
A musical temperament is used to define the possible keys in music, and also the positions of keys relevant to each other. Our primary example is the western or chromatic temperament, with 12 possible keys. A temperament also defines which of the keys can be represented as naturals (like "C", "D" and "E"), and which need to be represented with accidentals (like "C#" and "Db".
Doing anything more at the moment is overkill. For example, we are not intereted in the possible frequencies for notes of a given key.
Instance Attribute Summary collapse
-
#nat_key_lookup_order ⇒ Object
Returns the value of attribute nat_key_lookup_order.
-
#nat_key_pos_lookup ⇒ Object
Returns the value of attribute nat_key_pos_lookup.
-
#nat_key_posn ⇒ Object
Returns the value of attribute nat_key_posn.
-
#nat_keys ⇒ Object
Returns the value of attribute nat_keys.
-
#no_keys ⇒ Object
Returns the value of attribute no_keys.
-
#no_nat_keys ⇒ Object
Returns the value of attribute no_nat_keys.
-
#parsenote ⇒ Object
Returns the value of attribute parsenote.
-
#pos_lookup_nat_key ⇒ Object
Returns the value of attribute pos_lookup_nat_key.
-
#seq_maps ⇒ Object
Returns the value of attribute seq_maps.
Instance Method Summary collapse
-
#add_elem(elem, nseq_type, name_s, abbrv_s, seqpos) ⇒ Object
This adds an element to the dictionary inside the temperament.
-
#check_nseqby_abbrv(nseq_type, abbrv) ⇒ Object
Checks if there is a noteseq with a given abbreviation.
-
#check_nseqby_name(nseq_type, name) ⇒ Object
Checks if there is a noteseq with a given name.
-
#check_nseqby_seqpos(nseq_type, seqpos) ⇒ Object
Checks if there is a noteseq with a given sequence position.
-
#check_nseqby_subdict(nseq_type) ⇒ Object
Checks if there is a subdictionary associated with nseq_type.
-
#get_key_of_pos(pos, desired_nat_note = nil, sharp_not_flat = nil) ⇒ Object
(also: #key_of_pos, #key_of)
Given a position in the temperament, this function attempts to return the "best" key name corresponding to it.
-
#get_keyseq_notes(note_seq) ⇒ Object
The reverse of get_note_sequence().
-
#get_note_sequence(key, pos_seq, nat_pos_seq = nil, sharp_not_flat = nil) ⇒ Object
This function takes a key and a sequence of positions relative to it.
-
#get_nseqby_abbrv(abbrv, nseq_type) ⇒ Object
Looks up a noteseq (or anything else) by abbreviation.
-
#get_nseqby_name(name, nseq_type) ⇒ Object
Looks up a noteseq (or anything else) by name.
-
#get_nseqby_seqpos(seqpos, nseq_type) ⇒ Object
Looks up a noteseq (or anything else) by sequence position.
-
#get_pos_of_key(key, debug_f: false) ⇒ Object
(also: #pos_of_key, #position_of, #pos_of)
Returns the position of the key in the temperament.
-
#initialize(no_keys = CHROM_SIZE, nat_keys = CHROM_NAT_NOTES, nat_key_posn = CHROM_NAT_NOTE_POS) ⇒ Temperament
constructor
Initialiser.
-
#note_parse(key, debug_f: false) ⇒ Object
Parses the name of a key into the tuple (natural key name, sharp_or_flat count).
Constructor Details
#initialize(no_keys = CHROM_SIZE, nat_keys = CHROM_NAT_NOTES, nat_key_posn = CHROM_NAT_NOTE_POS) ⇒ Temperament
Initialiser.
Args
- no_keys
the number of keys in the temperament. !number of semitones in one octave. ex. 12.
- nat_keys
an array consisting of the names of the natural (unsharped or unflattened) keys in the temperament. ex. ["C", "D", "E", "F", "G", "A", "B"]
- nat_key_posn
the position of the natural keys in the temperament. These should correspond to the elements in nat_keys. Positions are calculated base zero. ex. [ 0, 2, 4, 5, 7, 9, 11]
def initialize( no_keys, nat_keys, nat_key_posn )
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/music_set_theory/temperament.rb', line 412 def initialize( no_keys = CHROM_SIZE, nat_keys = CHROM_NAT_NOTES, nat_key_posn = CHROM_NAT_NOTE_POS ) # self.no_keys = no_keys self.nat_keys = nat_keys self.nat_key_posn = nat_key_posn # Extra variable: no_nat_keys: number of natural keys. #self.no_nat_keys = min(len(nat_keys), len(nat_key_posn)) self.no_nat_keys = [nat_keys.size, nat_key_posn.size].min # Extra variable: nat_key_pos_lookup: # key -> position (e.g. A -> 9, C->0). self.nat_key_pos_lookup = {} # Extra variable: post_lookup_nat_key: position # -> key/None (e.g., 9->A, 0->C). self.pos_lookup_nat_key = {} # Extra variable: nat_key_lookup_order: looks up order of # nat_keys (e.g, C->0, D->1... B->6). Reverse translation # available by self.nat_keys[order]. self.nat_key_lookup_order = {} # for i in 0...self.no_nat_keys self.nat_key_pos_lookup[nat_keys[i]] = nat_key_posn[i] self.pos_lookup_nat_key[nat_key_posn[i]] = nat_keys[i] self.nat_key_lookup_order[nat_keys[i]] = i end # self.parsenote = re.compile(RE_NOTEPARSE); self.parsenote = Regexp.compile(RE_NOTEPARSE) # Useful for dictionary lookup later. #self.seq_maps = seq_dict([NSEQ_SCALE, NSEQ_CHORD], self); self.seq_maps = SeqDict.new([NSEQ_SCALE, NSEQ_CHORD], self) end |
Instance Attribute Details
#nat_key_lookup_order ⇒ Object
Returns the value of attribute nat_key_lookup_order.
392 393 394 |
# File 'lib/music_set_theory/temperament.rb', line 392 def nat_key_lookup_order @nat_key_lookup_order end |
#nat_key_pos_lookup ⇒ Object
Returns the value of attribute nat_key_pos_lookup.
391 392 393 |
# File 'lib/music_set_theory/temperament.rb', line 391 def nat_key_pos_lookup @nat_key_pos_lookup end |
#nat_key_posn ⇒ Object
Returns the value of attribute nat_key_posn.
390 391 392 |
# File 'lib/music_set_theory/temperament.rb', line 390 def nat_key_posn @nat_key_posn end |
#nat_keys ⇒ Object
Returns the value of attribute nat_keys.
390 391 392 |
# File 'lib/music_set_theory/temperament.rb', line 390 def nat_keys @nat_keys end |
#no_keys ⇒ Object
Returns the value of attribute no_keys.
390 391 392 |
# File 'lib/music_set_theory/temperament.rb', line 390 def no_keys @no_keys end |
#no_nat_keys ⇒ Object
Returns the value of attribute no_nat_keys.
391 392 393 |
# File 'lib/music_set_theory/temperament.rb', line 391 def no_nat_keys @no_nat_keys end |
#parsenote ⇒ Object
Returns the value of attribute parsenote.
393 394 395 |
# File 'lib/music_set_theory/temperament.rb', line 393 def parsenote @parsenote end |
#pos_lookup_nat_key ⇒ Object
Returns the value of attribute pos_lookup_nat_key.
392 393 394 |
# File 'lib/music_set_theory/temperament.rb', line 392 def pos_lookup_nat_key @pos_lookup_nat_key end |
#seq_maps ⇒ Object
Returns the value of attribute seq_maps.
394 395 396 |
# File 'lib/music_set_theory/temperament.rb', line 394 def seq_maps @seq_maps end |
Instance Method Details
#add_elem(elem, nseq_type, name_s, abbrv_s, seqpos) ⇒ Object
This adds an element to the dictionary inside the temperament.
Args
- elem
the element to add to the dictionary .
- nseq_type
the type of the elemenet (such as scale or chord).
- name_s
a string, or a sequence of strings. This provides names as keys that map onto elem.
- abbrv_s
a string, or a sequence of strings. This provides abbreviations as keys that map onto elem.
- seqpos
a sequence. A tuple form will be used as a key that maps onto elem.
If nseq_type is not associated with any of the sub-dictionaries in the dictionary in the temperament, then this function exits.
753 754 755 |
# File 'lib/music_set_theory/temperament.rb', line 753 def add_elem( elem, nseq_type, name_s, abbrv_s, seqpos ) self.seq_dict.add_elem(elem, nseq_type, name_s, abbrv_s, seqpos) end |
#check_nseqby_abbrv(nseq_type, abbrv) ⇒ Object
Checks if there is a noteseq with a given abbreviation.
769 770 771 |
# File 'lib/music_set_theory/temperament.rb', line 769 def check_nseqby_abbrv( nseq_type, abbrv ) self.seq_dict.check_nseqby_abbrv(nseq_type, abbrv) end |
#check_nseqby_name(nseq_type, name) ⇒ Object
Checks if there is a noteseq with a given name.
764 765 766 |
# File 'lib/music_set_theory/temperament.rb', line 764 def check_nseqby_name( nseq_type, name ) self.seq_dict.check_nseqby_name(nseq_type, name) end |
#check_nseqby_seqpos(nseq_type, seqpos) ⇒ Object
Checks if there is a noteseq with a given sequence position.
774 775 776 |
# File 'lib/music_set_theory/temperament.rb', line 774 def check_nseqby_seqpos( nseq_type, seqpos ) self.seq_dict.check_nseqby_seqpos(nseq_type, seqpos) end |
#check_nseqby_subdict(nseq_type) ⇒ Object
Checks if there is a subdictionary associated with nseq_type.
759 760 761 |
# File 'lib/music_set_theory/temperament.rb', line 759 def check_nseqby_subdict( nseq_type ) self.seq_dict.check_nseqby_subdict(nseq_type) end |
#get_key_of_pos(pos, desired_nat_note = nil, sharp_not_flat = nil) ⇒ Object Also known as: key_of_pos, key_of
Given a position in the temperament, this function attempts to return the "best" key name corresponding to it. This is not a straight-forward reverse of get_pos_of_key, as there may be two different keynames for the same position, with one preferred. For example, "C# and "Db" are the same key, but "C# is preferred in an A major scale. Fortunately, arguments are provided to indicate the programmer's preference.
Args
- pos
the position inside the temperament.
- desired_nat_note
the preferred natural key to start the key name. For example "C" makes the function return "C#" instead of "Db". If nil, then the preference depends on...
- sharp_not_flat
if true, returns the sharpened accidental form
(e.g., "C#"); if false, returns the flattened accidental form (i.e., "Db").
Returns
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
# File 'lib/music_set_theory/temperament.rb', line 587 def get_key_of_pos( pos, desired_nat_note = nil, sharp_not_flat = nil ) # accdtls: number of sharps or flats to add to the output string. if desired_nat_note accdtls = pos - self.nat_key_pos_lookup[desired_nat_note] accdtls = (accdtls % self.no_keys) if accdtls > (self.no_keys / 2) accdtls = accdtls - self.no_keys end if accdtls > 0 return desired_nat_note + (M_SHARP * accdtls) elsif accdtls < 0 return desired_nat_note + (M_FLAT * (-1 * accdtls)) else return desired_nat_note end else accdtls = 0 # if (pos % self.no_keys) in self.pos_lookup_nat_key if self.pos_lookup_nat_key.key?( pos % self.no_keys ) return self.pos_lookup_nat_key[pos % self.no_keys] elsif sharp_not_flat while accdtls < self.no_keys accdtls = accdtls - 1 #if ((pos + accdtls) % self.no_keys) in self.pos_lookup_nat_key if self.pos_lookup_nat_key.key?((pos + accdtls) % self.no_keys) return self.pos_lookup_nat_key[ (pos + accdtls) % self.no_keys] + (M_SHARP * (-1 * accdtls)) end end else while accdtls < self.no_keys accdtls = accdtls + 1 #if ((pos + accdtls) % self.no_keys) in self.pos_lookup_nat_key: if self.pos_lookup_nat_key.key?((pos + accdtls) % self.no_keys) return self.pos_lookup_nat_key[ (pos + accdtls) % self.no_keys] + (M_FLAT * accdtls) end end end end return nil end |
#get_keyseq_notes(note_seq) ⇒ Object
The reverse of get_note_sequence().
Args
- note_seq
a sequence of notes (note_seq)
Returns
[base key, position sequence]
Examples
["C", "D"] =>["C", [0, 2]] ["D", "C#", "E"] =>["D", [0, 11, 2]]
this is returned by the function.
See Also
#get_note_sequence()
709 710 711 712 713 714 715 716 |
# File 'lib/music_set_theory/temperament.rb', line 709 def get_keyseq_notes( note_seq ) base_key = note_seq[0] base_pos = self.get_pos_of_key(base_key) pos_seq = note_seq.map{|i| (self.get_pos_of_key(i) - base_pos) % self.no_keys } [base_key, pos_seq] end |
#get_note_sequence(key, pos_seq, nat_pos_seq = nil, sharp_not_flat = nil) ⇒ Object
This function takes a key and a sequence of positions relative to it. It returns a sequence of notes.
Args
- key
the starting key; examples are "C", "C#" and "Db".
- pos_seq
a list of positions relative to it in the temperament base 0). For example, in the Western/Chromatic temperament, a key of "C" and a sequence of [0, 1] returns ["C", "C#].
- nat_pos_seq
a list of numbers. These are used to calculate the desired natural notes produced from the corresponding positions in pos_seq. A number of 0 means to use the same natural note as in key, a number of 1 means using the next natural note, and so on. Examples for the Western/Chromatic scale: get_note_sequence("C", [0, 1], [0, 0]) =>["C", "C#"] get_note_sequence("C", [0, 1], [0, 1]) =>["C", "Dbb"] get_note_sequence("C", [0, 1], [0, 6]) =>["C", "B##"]
- sharp_not_flat
if nat_pos_seq is None, indicates whether notes with accidentals are preferred to have sharps (True) or flats (False)
Returns
See Also
#get_keyseq_notes()
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 |
# File 'lib/music_set_theory/temperament.rb', line 664 def get_note_sequence( key, pos_seq, nat_pos_seq = nil, sharp_not_flat = nil ) ret = nil base_key_pos = self.get_pos_of_key(key) #result_pos_seq = [((base_key_pos + i) % self.no_keys) for i in pos_seq] result_pos_seq = pos_seq.map{|i| (base_key_pos + i) % self.no_keys } if nat_pos_seq result_seq = [] desired_nat_key = self.note_parse(key)[0] desired_nat_key_posn = self.nat_keys.index(desired_nat_key) #for i in range(min(len(nat_pos_seq), len(result_pos_seq))): for i in 0...([nat_pos_seq.size, result_pos_seq.size].min) des_nat_note = self.nat_keys[ (desired_nat_key_posn + nat_pos_seq[i]) % self.no_nat_keys] des_key = self.get_key_of_pos(result_pos_seq[i], des_nat_note) result_seq.append(des_key) end ret = result_seq else # return [self.get_key_of_pos(i, None, sharp_not_flat) for i in # result_pos_seq]; ret = result_pos_seq.map{|i| self.get_key_of_pos(i, nil, sharp_not_flat) } end ret end |
#get_nseqby_abbrv(abbrv, nseq_type) ⇒ Object
Looks up a noteseq (or anything else) by abbreviation.
788 789 790 791 792 793 794 |
# File 'lib/music_set_theory/temperament.rb', line 788 def get_nseqby_abbrv( abbrv, nseq_type ) if self.seq_maps.check_nseqby_abbrv(nseq_type, abbrv) self.seq_maps.get_nseqby_abbrv(abbrv, nseq_type) else nil end end |
#get_nseqby_name(name, nseq_type) ⇒ Object
Looks up a noteseq (or anything else) by name.
779 780 781 782 783 784 785 |
# File 'lib/music_set_theory/temperament.rb', line 779 def get_nseqby_name( name, nseq_type ) if self.seq_dict.check_nseqby_name(nseq_type, name) self.seq_dict.get_nseqby_name(name, nseq_type) else nil end end |
#get_nseqby_seqpos(seqpos, nseq_type) ⇒ Object
Looks up a noteseq (or anything else) by sequence position.
797 798 799 800 801 802 803 |
# File 'lib/music_set_theory/temperament.rb', line 797 def get_nseqby_seqpos( seqpos, nseq_type ) if self.seq_maps.check_nseqby_seqpos(nseq_type, seqpos) self.seq_maps.get_nseqby_seqpos(seqpos, nseq_type) else nil end end |
#get_pos_of_key(key, debug_f: false) ⇒ Object Also known as: pos_of_key, position_of, pos_of
Returns the position of the key in the temperament.
Args
- key
key note. ex. "C", "D#", "Cb"
Returns
position of the key in the unit of halftone. ex. key: "C" =>0, key: "D#" =>3, key: "Cb" =>-1
See Also
#get_key_of_pos()
557 558 559 560 561 562 563 564 565 |
# File 'lib/music_set_theory/temperament.rb', line 557 def get_pos_of_key( key, debug_f: false ) key_parsed = self.note_parse(key) $stderr.puts "key_parsed: #{key_parsed}" if debug_f $stderr.puts "nat_key_pos_lookup: #{self.nat_key_pos_lookup}" if \ debug_f ret = self.nat_key_pos_lookup[key_parsed[0]] + key_parsed[1] ret end |
#note_parse(key, debug_f: false) ⇒ Object
Parses the name of a key into the tuple (natural key name, sharp_or_flat count).
Args
- key
note.
Returns
an array: [
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
# File 'lib/music_set_theory/temperament.rb', line 515 def note_parse( key, debug_f: false ) noteMatch = self.parsenote.match(key) $stderr.puts "{#{__method__}} key (#{key.length}): #{key}" if debug_f $stderr.puts " =>#{noteMatch[:basenotename].chars}" if debug_f return nil if !(noteMatch) # # $stderr.puts "note_match: #{noteMatch}:#{noteMatch.class}" # noteGroup = noteMatch.group('basenotename') noteGroup = noteMatch[:basenotename].chars baseNote = noteGroup[0]; $stderr.puts "key: #{key}::" + " note_group: #{noteGroup.inspect}:#{noteGroup.class}," + " chars(#{noteGroup.size}): #{noteGroup};" + " base_note: #{baseNote}" if debug_f # flatSharpAdj = 0 for chr in noteGroup[1..] $stderr.puts "{#{__method__}} adj: #{chr}" if debug_f if /#{chr}/ =~ (M_FLAT + MNU_FLAT) flatSharpAdj += -1 end if /#{chr}/ =~ (M_SHARP + MNU_SHARP) flatSharpAdj += +1 end end [ baseNote, flatSharpAdj,] end |