def test_is_chordal(self): logging.debug('Start test_is_chordal') upper_context_note = Note(DiatonicPitch.parse('F:5'), Duration(1, 4)) lower_policy_context = TestChordalToneConstraint.policy_creator(ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8') lower_context_note = ContextualNote(lower_policy_context) parameter_map = dict([(upper_context_note, lower_context_note)]) policy = ChordalPitchConstraint(upper_context_note) v_result = policy.values(parameter_map, upper_context_note) results = {DiatonicTone('D'), DiatonicTone('F#'), DiatonicTone('A')} for note in v_result: print('test_is_chordal; note = {0}'.format(note)) tone = note.diatonic_pitch.diatonic_tone octave = note.diatonic_pitch.octave assert tone in results assert octave in range(2, 9) assert len(v_result) == 6 * 3 for note in v_result: parameter_map[upper_context_note].note = note assert policy.verify(parameter_map) is True logging.debug('End test_is_chordal')
def test_pentatonic_tonal_function(self): t_domain = Tonality.create(ModalityType.MajorPentatonic, DiatonicTone('C')) interval = Interval(3, IntervalType.Major) f = CrossTonalityShiftTonalFunction.create_shift(t_domain, interval) # C, D, E, G. A ==> E, F#, G#, B, C# assert 'E' == f['C'].diatonic_symbol assert 'F#' == f['D'].diatonic_symbol assert 'G#' == f['E'].diatonic_symbol assert 'B' == f['G'].diatonic_symbol assert 'C#' == f['A'].diatonic_symbol assert 'A' == f['F'].diatonic_symbol assert 'D#' == f['B'].diatonic_symbol assert 'A#' == f['F#'].diatonic_symbol assert 'D##' == f['B#'].diatonic_symbol assert 'Ab' == f['Fb'].diatonic_symbol assert 'D' == f['Bb'].diatonic_symbol t_range = Tonality.create(ModalityType.MajorPentatonic, interval.get_end_tone(DiatonicTone('C'))) f = CrossTonalityShiftTonalFunction(t_domain, t_range.annotation[2], 2) # C, D, E, G. A ==> G#, B, C#, E, F# assert 'G#' == f['C'].diatonic_symbol assert 'B' == f['D'].diatonic_symbol assert 'C#' == f['E'].diatonic_symbol assert 'E' == f['G'].diatonic_symbol assert 'F#' == f['A'].diatonic_symbol TestCrossTonalityShiftTonalFunction.print_function(f)
def __init__(self, tonality, increment, aux_tonality=None): self.__tonality = tonality self.__increment = increment self.__aux_tonality = aux_tonality if tonality.cardinality != 7: raise Exception('Expect tonalities with cardinality 7 only.') self.__tone_map = dict() if aux_tonality is None: annotation = self.tonality.annotation for index in range(0, self.tonality.cardinality): dt = annotation[index] mapped_tone = self.tonality.annotation[(index + self.increment) % self.tonality.cardinality] if dt.augmentation_offset != 0: mapped_tone = DiatonicTone.alter_tone_by_augmentation(mapped_tone, -dt.augmentation_offset) self.tone_map[dt.diatonic_letter] = mapped_tone else: annotation = self.tonality.annotation # target scale aux_annotation = self.aux_tonality.annotation # source scale for index in range(0, self.aux_tonality.cardinality): dt = aux_annotation[index] mapped_tone = annotation[index] if dt.augmentation_offset != 0: mapped_tone = DiatonicTone.alter_tone_by_augmentation(mapped_tone, -dt.augmentation_offset) self.tone_map[dt.diatonic_letter] = mapped_tone
def test_across_tonalities(self): logging.debug('Start test_across_tonalities.') lower_policy_context_1 = TestRelativeDiatonicConstraint.policy_creator( ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8') lower_policy_context_2 = TestRelativeDiatonicConstraint.policy_creator( ModalityType.Major, DiatonicTone('Ab'), 'tI', 'C:2', 'C:8') upper_note_1 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8)) upper_note_2 = Note(DiatonicPitch.parse('D:5'), Duration(1, 8)) lower_note_1 = ContextualNote( lower_policy_context_1, Note(DiatonicPitch.parse('F#:5'), Duration(1, 8))) lower_note_2 = ContextualNote(lower_policy_context_2) p_map = dict([(upper_note_1, lower_note_1), (upper_note_2, lower_note_2)]) policy = RelativeDiatonicConstraint(upper_note_1, upper_note_2, Interval(3, IntervalType.Minor), Interval(3, IntervalType.Major)) v_result = policy.values(p_map, upper_note_2) for note in v_result: logging.debug(note) pitches = [note.diatonic_pitch for note in v_result] assert {str(p) for p in pitches} == {'Eb:5', 'F:5', 'G:5', 'Ab:5'} logging.debug('End test_across_tonalities.')
def get_end_pitch(self, pitch): """ Given a pitch and this interval, Assuming pitch is the starting pitch of the interval, compute the end pitch. Args: pitch: DiatonicPitch Returns: DiatonicPitch of end tone """ diatonic_dist = pitch.diatonic_distance() + self.diatonic_distance tone_index = diatonic_dist % 7 end_pitch_string = DiatonicTone.get_diatonic_letter(tone_index) end_pitch_octave = diatonic_dist // 7 chromatic_dist = pitch.chromatic_distance + self.chromatic_distance normal_pitch = DiatonicPitch( end_pitch_octave, DiatonicFoundation.get_tone(end_pitch_string)) alteration = chromatic_dist - normal_pitch.chromatic_distance end_pitch_string += DiatonicTone.augmentation(alteration) return DiatonicPitch.parse(end_pitch_string + ':' + str(end_pitch_octave))
def test_setting_values(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('C')) t_range = Tonality.create(ModalityType.MajorPentatonic, DiatonicTone('A')) # A, B, C#, E, F# # default map between 2 tonalities of unequal cardinality - empty p = { 'C': 'A', 'D': 'A', 'E': 'B', 'F': 'C#', 'G': 'C#', 'A': 'E', 'B': 'F#' } f = TonalFunction(t_domain, t_range, p) assert DiatonicToneCache.get_tone('A') == f['C'] assert DiatonicToneCache.get_tone('A') == f['D'] assert DiatonicToneCache.get_tone('B') == f['E'] assert DiatonicToneCache.get_tone('C#') == f['F'] assert DiatonicToneCache.get_tone('C#') == f['G'] assert DiatonicToneCache.get_tone('E') == f['A'] assert DiatonicToneCache.get_tone('F#') == f['B'] assert [0, 0, 1, 2, 2, 3, 4] == f.extract_template().tonal_order f['C'] = 'B' f['D'] = 'A' f['E'] = 'C#' f['F'] = 'C#' f['G'] = 'F#' f['A'] = 'A' f['B'] = 'E' # del f['D'] # assert f['D'] is None assert [1, 0, 2, 2, 4, 0, 3] == f.extract_template().tonal_order f['C#'] = 'B#' f['Db'] = 'Ab' f['A#'] = 'Eb' assert DiatonicToneCache.get_tone('B#') == f['C#'] assert DiatonicToneCache.get_tone('Ab') == f['Db'] assert DiatonicToneCache.get_tone('Eb') == f['A#'] del f['Db']
def test_centered_even_function(self): t_domain = Tonality.create(ModalityType.HWOctatonic, DiatonicTone('C')) r = PitchRange.create('E:3', 'E:7') f = DiatonicPitchReflectionFunction( t_domain, DiatonicPitch(4, DiatonicToneCache.get_tone('A')), r, FlipType.CenterTone) TestFlipOnTonality.print_map('test_centered_even_function', f) t = f.tonal_function assert 'C' == t['Gb'].diatonic_symbol assert 'Db' == t['Fb'].diatonic_symbol assert 'Eb' == t['Eb'].diatonic_symbol assert 'Fb' == t['Db'].diatonic_symbol assert 'Gb' == t['C'].diatonic_symbol assert 'G' == t['Bb'].diatonic_symbol assert 'A' == t['A'].diatonic_symbol assert 'Bb' == t['G'].diatonic_symbol assert 'C:4' == str(f['Gb:5']) assert 'Db:4' == str(f['Fb:5']) assert 'Eb:4' == str( f['Eb:5']) # The other stable tone outside of the centered! assert 'Fb:4' == str(f['Db:5']) assert 'Gb:4' == str(f['C:5']) assert 'G:4' == str(f['Bb:4']) assert 'A:4' == str(f['A:4']) assert 'Bb:4' == str(f['G:4']) assert 'C:5' == str(f['Gb:4']) assert 'Db:5' == str(f['Fb:4'])
def test_is_not_equal(self): logging.debug('Start test_is_not_equal') note1 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8)) lower_policy_context = TestNotEqualPitchConstraint.policy_creator( ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8') lower_context_note_a = ContextualNote( lower_policy_context, Note(DiatonicPitch.parse('F#:6'), Duration(1, 8))) p_map = PMap() p_map[note1] = lower_context_note_a other_source = [] for tone in ['B:5', 'D:5', 'E:5']: n = Note(DiatonicPitch.parse(tone), Duration(1, 8)) lower_context_note = ContextualNote(lower_policy_context) p_map[n] = lower_context_note other_source.append(n) params = list([note1]) params.extend(other_source) policy = NotEqualPitchConstraint(params) for c_note in p_map.unassigned_actors(policy): print(c_note) v_result = policy.values(p_map, c_note) assert v_result is not None assert DiatonicPitch.parse('F#:6') not in { n.diatonic_pitch for n in v_result } p_map[other_source[1]].note = Note(DiatonicPitch.parse('E:6'), Duration(1, 8)) for c_note in p_map.unassigned_actors(policy): print(c_note) v_result = policy.values(p_map, c_note) assert v_result is not None ret_pitches = {n.diatonic_pitch for n in v_result} assert len( ret_pitches.intersection( {DiatonicPitch.parse('F#:6'), DiatonicPitch.parse('E:6')})) == 0 assert policy.verify(p_map) is False p_map[other_source[2]].note = Note(DiatonicPitch.parse('F#:6'), Duration(1, 8)) assert policy.verify(p_map) is False p_map[other_source[0]].note = Note(DiatonicPitch.parse('D:6'), Duration(1, 8)) assert policy.verify(p_map) is False p_map[other_source[2]].note = Note(DiatonicPitch.parse('A:6'), Duration(1, 8)) assert policy.verify(p_map) is True logging.debug('End test_is_not_equal') return
def print_book_map(tag, f): print(tag) domain_tonality = f.domain_tonality range_tonality = f.range_tonality print('CrossTonalityShiftPitchFunction {0}--> {1}'.format( domain_tonality, range_tonality)) domain_annotation = domain_tonality.annotation[:-1] presentation_reg = 4 domain_presentation_pitches = list() last_pitch = None for t in domain_annotation: domain_pitch = DiatonicPitch(presentation_reg, t) # augment register if crossing past C, e.g. B:4 > C:4 if last_pitch is not None and last_pitch.chromatic_distance > domain_pitch.chromatic_distance: presentation_reg = presentation_reg + 1 domain_pitch = DiatonicPitch(presentation_reg, t) for a in [-1, 0, 1]: a_tone = DiatonicTone.alter_tone_by_augmentation(t, a) domain_pitch_mod = DiatonicPitch(presentation_reg, a_tone) domain_presentation_pitches.append(domain_pitch_mod) last_pitch = domain_pitch for domain_pitch in domain_presentation_pitches: range_pitch = f[domain_pitch] print('{0} --> {1}'.format(domain_pitch, range_pitch))
def test_book_examples(self): diatonic_tonality = Tonality.create(ModalityType.Major, DiatonicTone("A")) template = SecondaryChordTemplate.parse('V/V') if template: print('succeeded') chord = template.create_chord(diatonic_tonality) print(chord) else: print("failed") template = SecondaryChordTemplate.parse('III/II') chord = template.create_chord(diatonic_tonality) print(chord) template = SecondaryChordTemplate.parse('CMaj7/II') chord = template.create_chord(diatonic_tonality) print(chord) template = SecondaryChordTemplate.parse('V/V[NaturalMinor]') chord = template.create_chord(diatonic_tonality) print(chord) template = SecondaryChordTemplate.parse('V/V[Phrygian]') chord = template.create_chord(diatonic_tonality) print(chord) template = SecondaryChordTemplate.parse('QVIPPAP@2/V') chord = template.create_chord(diatonic_tonality) print(chord)
def test_diatonic_modal_indexed_function(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('C')) i = Interval(2, IntervalType.Major) r = PitchRange.create('E:3', 'E:7') f = CrossTonalityShiftPitchFunction(t_domain, r, i, modal_index=4) print('f={0}'.format(f)) TestCrossTonalityShiftPitchFunction.print_map('test_diatonic_function', f) t = f.tonal_function print(t) # test diatonic maps assert 'D:4' == str(f['C:4']) assert 'E:4' == str(f['D:4']) assert 'F#:4' == str(f['E:4']) assert 'G:4' == str(f['F:4']) assert 'A:4' == str(f['G:4']) assert 'B:4' == str(f['A:4']) assert 'C:5' == str(f['B:4']) assert 'D:5' == str(f['C:5']) assert 'M:2' == str(f.root_shift_interval)
def test_simple_fixed_pitch(self): logging.debug('Start test_simple_fixed_pitch') note = Note(DiatonicPitch.parse("C:5"), Duration(1, 4)) select_notes = {'A:5', 'C:4', 'Eb:2', 'F#:6'} constraint = FixedPitchSelectSetConstraint( note, {DiatonicPitch.parse(p) for p in select_notes}) policy_context = TestFixedPitchSelectSetConstraint.policy_creator( ModalityType.Major, DiatonicTone('C'), 'tIV', 'C:2', 'C:8') lower_contextual = ContextualNote(policy_context) p_map = PMap() p_map[note] = lower_contextual v_results = constraint.values(p_map, note) assert v_results is not None assert len(v_results) == len(select_notes) for result in v_results: print(str(result)) assert select_notes == {str(n.diatonic_pitch) for n in v_results} assert not constraint.verify(p_map) lower_contextual.note = Note(DiatonicPitch.parse('Eb:2'), Duration(1, 4)) assert constraint.verify(p_map)
def test_is_equal(self): logging.debug('Start test_is_equal') note1 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8)) note2 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8)) lower_policy_context = TestEqualPitchConstraint.policy_creator( ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8') lower_context_note_a = ContextualNote( lower_policy_context, Note(DiatonicPitch.parse('F#:6'), Duration(1, 8))) lower_context_note_b = ContextualNote(lower_policy_context) parameter_map = dict([(note1, lower_context_note_a), (note2, lower_context_note_b)]) parameter_map = PMap(parameter_map) policy = EqualPitchConstraint([note1, note2]) result = policy.values(parameter_map, note2) actual_note = next(iter(result)) print('test_is_equal; note = {0}'.format(actual_note)) assert actual_note.diatonic_pitch == DiatonicPitch.parse("F#:6") assert actual_note.base_duration == Duration(1, 8) parameter_map[note2].note = actual_note assert policy.verify(parameter_map) is True logging.debug('End test_is_equal')
def test_non_centered_odd_function(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('E')) r = PitchRange.create('E:3', 'E:7') f = DiatonicPitchReflectionFunction( t_domain, DiatonicPitch(4, DiatonicToneCache.get_tone('F#')), r, FlipType.LowerNeighborOfPair) TestFlipOnTonality.print_map('test_non_centered_odd_function', f) t = f.tonal_function assert 'E' == t['A'].diatonic_symbol assert 'F#' == t['G#'].diatonic_symbol assert 'G#' == t['F#'].diatonic_symbol assert 'A' == t['E'].diatonic_symbol assert 'B' == t['D#'].diatonic_symbol assert 'C#' == t['C#'].diatonic_symbol assert 'D#' == t['B'].diatonic_symbol assert 'C#:4' == str(f['C#:5']) assert 'D#:4' == str(f['B:4']) assert 'E:4' == str(f['A:4']) assert 'F#:4' == str(f['G#:4']) assert 'G#:4' == str(f['F#:4']) assert 'A:4' == str(f['E:4']) assert 'B:4' == str(f['D#:4']) assert 'C#:5' == str(f['C#:4']) assert 'D#:5' == str(f['B:3'])
def test_simple_tonality_permutation_function(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('A')) cycles = [['C#', 'D'], ['E', 'G#']] p = TonalityPermutationFunction.create(t_domain, cycles) assert DiatonicToneCache.get_tone('C#') in p.domain assert DiatonicToneCache.get_tone('D') in p.domain assert DiatonicToneCache.get_tone('E') in p.domain assert DiatonicToneCache.get_tone('G#') in p.domain assert DiatonicToneCache.get_tone('D') == p['C#'] assert DiatonicToneCache.get_tone('C#') == p['D'] assert DiatonicToneCache.get_tone('E') == p['G#'] assert DiatonicToneCache.get_tone('G#') == p['E'] f = TonalityPermutationFunction(TonalityPermutation(t_domain, cycles)) assert DiatonicToneCache.get_tone('C#') in f.domain assert DiatonicToneCache.get_tone('D') in f.domain assert DiatonicToneCache.get_tone('E') in f.domain assert DiatonicToneCache.get_tone('G#') in f.domain assert DiatonicToneCache.get_tone('D') == f['C#'] assert DiatonicToneCache.get_tone('C#') == f['D'] assert DiatonicToneCache.get_tone('E') == f['G#'] assert DiatonicToneCache.get_tone('G#') == f['E']
def test_centered_odd_function(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('E')) r = PitchRange.create('E:3', 'E:7') f = DiatonicPitchReflectionFunction( t_domain, DiatonicPitch(4, DiatonicToneCache.get_tone('A')), r, FlipType.CenterTone) print('f={0}'.format(f)) TestFlipOnTonality.print_map('test_simple_pitch_function', f) t = f.tonal_function assert 'E' == t['D#'].diatonic_symbol assert 'F#' == t['C#'].diatonic_symbol assert 'G#' == t['B'].diatonic_symbol assert 'A' == t['A'].diatonic_symbol assert 'B' == t['G#'].diatonic_symbol assert 'C#' == t['F#'].diatonic_symbol assert 'D#' == t['E'].diatonic_symbol assert 'D#:5' == str(f['E:4']) assert 'C#:5' == str(f['F#:4']) assert 'A:4' == str(f['A:4']) assert 'G#:4' == str(f['B:4']) assert 'F#:4' == str(f['C#:5']) assert 'E:4' == str(f['D#:5'])
def test_non_centered_even_function(self): t_domain = Tonality.create(ModalityType.WholeTone, DiatonicTone('C')) r = PitchRange.create('E:3', 'E:7') f = DiatonicPitchReflectionFunction( t_domain, DiatonicPitch(4, DiatonicToneCache.get_tone('G#')), r, FlipType.UpperNeighborOfPair) TestFlipOnTonality.print_map('test_non_centered_even_function', f) t = f.tonal_function assert 'C' == t['D'].diatonic_symbol assert 'D' == t['C'].diatonic_symbol assert 'E' == t['A#'].diatonic_symbol assert 'F#' == t['G#'].diatonic_symbol assert 'G#' == t['F#'].diatonic_symbol assert 'A#' == t['E'].diatonic_symbol assert 'C:4' == str(f['D:5']) assert 'D:4' == str(f['C:5']) assert 'E:4' == str(f['A#:4']) assert 'F#:4' == str(f['G#:4']) assert 'G#:4' == str(f['F#:4']) assert 'A#:4' == str(f['E:4']) assert 'C:5' == str(f['D:4'])
def test_book_examples(self): template = QuartalChordTemplate.parse('GPerAug') if template: print('succeeded') chord = template.create_chord() print(chord) else: print("failed") diatonic_tonality = Tonality.create(ModalityType.Major, DiatonicTone("Bb")) template = QuartalChordTemplate.parse('IIPerPer@2') chord = template.create_chord(diatonic_tonality) print(chord) template = QuartalChordTemplate.parse('GPPAAPP') chord = template.create_chord() print(chord) template = QuartalChordTemplate.parse('IIIPAPAA@3') chord = template.create_chord(diatonic_tonality) print(chord) template = QuartalChordTemplate.parse('C') chord = template.create_chord() print(chord) template = QuartalChordTemplate.parse('IV') chord = template.create_chord(diatonic_tonality) print(chord)
def test_additional_octaves(self): t_domain = Tonality.create(ModalityType.MinorPentatonic, DiatonicTone('C')) i = Interval(12, IntervalType.Perfect) r = PitchRange.create('E:3', 'E:7') f = CrossTonalityShiftPitchFunction(t_domain, r, i) print('f={0}'.format(f)) assert 'P:12' == str(f.root_shift_interval) assert 'G:5' == str(f['C:4']) assert 'A:5' == str(f['D:4']) assert 'B:5' == str(f['E:4']) assert 'C:6' == str(f['F:4']) i = Interval(11, IntervalType.Perfect).negation() f = CrossTonalityShiftPitchFunction(t_domain, r, i) print('f={0}'.format(f)) assert '-P:11' == str(f.root_shift_interval) assert 'G:2' == str(f['C:4']) assert 'A:2' == str(f['D:4']) assert 'B:2' == str(f['E:4']) assert 'C:3' == str(f['F:4'])
def test_reversal_on_policy(self): logging.debug('Start test_reversal_on_policy') lower_policy_context = TestRelativeScalarStepConstraint.policy_creator( ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8') note1 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8)) note2 = Note(DiatonicPitch.parse('D:5'), Duration(1, 8)) lower_note_1 = ContextualNote(lower_policy_context) lower_note_2 = ContextualNote( lower_policy_context, Note(DiatonicPitch.parse('C:5'), Duration(1, 8))) p_map = dict([(note1, lower_note_1), (note2, lower_note_2)]) # F#:5 --> G Major two below and 3 above policy = RelativeScalarStepConstraint(note1, note2, -2, 3) result = policy.values(p_map, note1) pitches = [n.diatonic_pitch for n in result] for pitch in pitches: logging.debug(pitch) # Check that each returned verifies for n in result: lower_note_1.note = n assert policy.verify(p_map) logging.debug('End test_reversal_on_policy')
def test_non_scale_note(self): logging.debug('Start test_non_scale_note') note = Note(DiatonicPitch.parse("Bb:4"), Duration(1, 4)) policy = FixedPitchConstraint(note, DiatonicPitch.parse("Ab:5")) policy_context = TestFixedPitchConstraint.policy_creator( ModalityType.Major, DiatonicTone('C'), 'tIV', 'C:2', 'C:8') contextual_note = ContextualNote(policy_context) p_map = PMap() p_map[note] = contextual_note v_result = policy.values(p_map, note) result = next(iter(v_result)) print('test_non_scale_note note= {0}'.format(result)) assert result.diatonic_pitch == DiatonicPitch.parse("Ab:5") assert result.base_duration == Duration(1, 4) contextual_note.note = result result = policy.verify(p_map) assert result is True logging.debug('end test_non_scale_note')
def test_basic_policy(self): logging.debug('Start test_basic_policy') lower_policy_context = TestRelativeScalarStepConstraint.policy_creator( ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8') note1 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8)) note2 = Note(DiatonicPitch.parse('D:5'), Duration(1, 8)) lower_note_1 = ContextualNote( lower_policy_context, Note(DiatonicPitch.parse('F#:5'), Duration(1, 8))) lower_note_2 = ContextualNote(lower_policy_context) p_map = dict([(note1, lower_note_1), (note2, lower_note_2)]) # F#:5 --> G Major two below and 3 above policy = RelativeScalarStepConstraint(note1, note2, -2, 3) v_result = policy.values(p_map, note2) pitches = [n.diatonic_pitch for n in v_result] assert len(pitches) == 6 for s in ['D:5', 'E:5', 'F#:5', 'G:5', 'A:5', 'B:5']: assert DiatonicPitch.parse(s) in pitches for note in v_result: logging.debug(note) # Check verify for each answer for n in v_result: lower_note_2.note = n assert policy.verify(p_map) logging.debug('End test_basic_policy')
def test_forward_with_extension(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('E')) cycles = [['E', 'G#', 'A', 'C#'], ('F#', 'D#')] extension = dict() extension['D'] = 'F' extension['F'] = 'G' permutation_function = TonalityPermutationFunction.create( t_domain, cycles, extension) r = PitchRange.create('E:3', 'E:7') f = TonalityPitchFunction(permutation_function, ('E:4', 'E:5'), r, False) TestTonalityPitchFunction.print_map('test_forward_with_extension', f) assert 'G#:5' == str(f['E:4']) assert 'G:5' == str(f['F:4']) assert 'D#:6' == str(f['F#:4']) assert 'A:5' == str(f['G#:4']) assert 'C#:6' == str(f['A:4']) assert 'B:5' == str(f['B:4']) assert 'E:5' == str(f['C#:5']) assert 'E:5' == str(f['B##:4']) assert 'F:5' == str(f['D:5']) assert 'F:5' == str(f['C##:5']) assert 'F#:5' == str(f['D#:5'])
def _map_non_tonality_tone(self, ttone, pure_tonal_map): # Get the closest tone in terms of chromatic distance. closest_tone, closest_distance = self._find_closest_tone(ttone) dd = closest_tone.diatonic_index - ttone.diatonic_index # Look for octave crossing. if dd in {-5, -6] closest pitch is one octave up. if dd in [-5, -6]: # [1, 2] are alright. pitch < closest_pitch dd = (7 + dd) % 7 elif dd in [5, 6]: # [-1, -2] are alright. pitch > closest_pitch dd = dd - 7 closest_tone_target = pure_tonal_map[closest_tone] index = (closest_tone_target.diatonic_index + dd) % 7 tone = DiatonicToneCache.get_tone('CDEFGAB'[index]) # Octave 4 is chosen arbitrarily, and using dd we determine if ttone and closest need different # octave assignments. We do this in order to get accurate chromatic distances below. if dd < 0: octave = 3 if DiatonicPitch.crosses_c(tone, closest_tone_target, True) else 4 elif dd > 0: octave = 5 if DiatonicPitch.crosses_c(tone, closest_tone_target, False) else 4 else: octave = 4 target_pitch = DiatonicPitch(octave, tone) actual_distance = target_pitch.chromatic_distance - DiatonicPitch(4, closest_tone_target).chromatic_distance # recall: closest_distance is chrom. dist to get from closest pitch to pitch: we are on 'other side', so # we use the negative of it. correction = closest_distance - actual_distance if correction == 0: return tone result_tone = DiatonicTone.alter_tone_by_augmentation(tone, correction) return result_tone
def test_simple_reverse_pitch_function(self): t_domain = Tonality.create(ModalityType.Major, DiatonicTone('E')) cycles = [['E', 'G#', 'A', 'C#'], ('F#', 'D#')] permutation_function = TonalityPermutationFunction.create( t_domain, cycles) r = PitchRange.create('E:3', 'E:7') f = TonalityPitchFunction(permutation_function, ('E:4', 'E:5'), r, True) TestTonalityPitchFunction.print_map( 'test_simple_reverse_pitch_fnction', f) assert 'G#:5' == str(f['E:4']) assert 'D#:6' == str(f['F#:4']) assert 'A:5' == str(f['G#:4']) assert 'C#:6' == str(f['A:4']) assert 'B:5' == str(f['B:4']) assert 'E:5' == str(f['C#:5']) assert 'F#:5' == str(f['D#:5']) assert 'G#:4' == str(f['E:5']) assert 'D#:5' == str(f['F#:5']) assert 'A:4' == str(f['G#:5']) assert 'C#:5' == str(f['A:5']) assert 'B:4' == str(f['B:5']) assert 'E:4' == str(f['C#:6']) assert 'F#:4' == str(f['D#:6'])
def __create_chord_on_diatonic_tonality(self, diatonic_tone, diatonic_tonality): if not diatonic_tonality: raise Exception( "Cannot base secundal chord on tone {0} without tonality.". format(diatonic_tone.diatonic_symbol)) # The tonality must include this tone. tone_scale = diatonic_tonality.annotation found_index = -1 for i in range(0, len(tone_scale)): if diatonic_tone == tone_scale[i]: found_index = i break if found_index == -1: raise Exception( "For secundal chord based on tone {0}, tone must be in given tonality {1}" .format(diatonic_tone.diatonic_symbol, diatonic_tonality)) self.chord_basis = [] basis_tone = tone_scale[found_index] for i in range(0, 3): tone = tone_scale[(found_index + i) % (len(tone_scale) - 1)] pitch_a = DiatonicPitch(1, basis_tone.diatonic_symbol) b_octave = 2 if basis_tone.diatonic_index > tone.diatonic_index else 1 pitch_b = DiatonicPitch(b_octave, tone.diatonic_symbol) interval = Interval.create_interval(pitch_a, pitch_b) # If for any reason, the interval is not perfect or augmented (we know it is a 4th), just adjust tone upward # It is unknown if this can happen in a diatonic scale in practice. if interval.interval_type.value == IntervalType.Diminished: tone = DiatonicTone.alter_tone_by_augmentation(tone, 1) pitch_b = DiatonicPitch(b_octave, tone.diatonic_symbol) interval = Interval.create_interval(pitch_a, pitch_b) self.chord_basis.append(interval) self.__tones.append((tone, interval)) basis_tone = tone
def test_pentatonic_tonal_function(self): t_domain = Tonality.create(ModalityType.MinorPentatonic, DiatonicTone('C')) i = Interval(5, IntervalType.Perfect) r = PitchRange.create('E:3', 'E:7') f = CrossTonalityShiftPitchFunction(t_domain, r, i) print('f={0}'.format(f)) TestCrossTonalityShiftPitchFunction.print_map( 'test_pentatonic_tonal_function', f) t = f.tonal_function print(t) assert 'G' == t['C'].diatonic_symbol assert 'Bb' == t['Eb'].diatonic_symbol assert 'C' == t['F'].diatonic_symbol assert 'D' == t['G'].diatonic_symbol assert 'F' == t['Bb'].diatonic_symbol assert 'G:5' == str(f['C:5']) assert 'F:5' == str(f['Bb:4']) assert 'D:5' == str(f['G:4']) assert 'C:5' == str(f['F:4']) assert 'Bb:4' == str(f['Eb:4']) assert 'G:4' == str(f['C:4']) # test range d = f.domain_pitch_range print(d) assert d.start_index == DiatonicPitch.parse('E:3').chromatic_distance assert d.end_index == DiatonicPitch.parse('E:7').chromatic_distance r = f.range_pitch_range print(r) assert r.start_index == DiatonicPitch.parse('B:3').chromatic_distance assert r.end_index == DiatonicPitch.parse('B:7').chromatic_distance # test chromatics assert 'G#:5' == str(f['C#:5']) assert 'F#:5' == str(f['B:4']) assert 'E#:5' == str(f['A#:4']) assert 'E:5' == str(f['A:4']) assert 'D#:5' == str(f['G#:4']) assert 'C#:5' == str(f['F#:4']) assert 'B:4' == str(f['E:4']) assert 'A:4' == str(f['D:4']) assert 'A#:4' == str(f['D#:4']) assert 'G#:4' == str(f['C#:4']) assert 'Gb:5' == str(f['Cb:5']) assert 'Fb:5' == str(f['Bbb:4']) assert 'Eb:5' == str(f['Ab:4']) assert 'Db:5' == str(f['Gb:4']) assert 'Cb:5' == str(f['Fb:4']) assert 'Bbb:4' == str(f['Ebb:4']) assert 'Ab:4' == str(f['Db:4']) assert 'Gb:4' == str(f['Cb:4'])
def __build_diatonics(self): """ Builds all diatonic tones for the cache. """ for ltr in DiatonicTone.DIATONIC_LETTERS: for aug in DiatonicTone.AUGMENTATIONS: self.diatonic_map[(ltr + aug).lower()] = DiatonicTone(ltr + aug)
def end_tone_from_pure_distance(tone, dd, cc, up_down=True): """ Given a tone and diatonic/chromatic distances compute the end tone above or below it. :param tone: :param dd: :param cc: :param up_down: :return: """ new_dd = (tone.diatonic_index + dd) % 7 if up_down else (tone.diatonic_index - dd) % 7 end_tone = DiatonicToneCache.get_tone( DiatonicTone.get_diatonic_letter(new_dd)) aug = (cc - (end_tone.placement - tone.placement) % 12) if up_down else \ (cc - (tone.placement - end_tone.placement) % 12) return DiatonicTone.alter_tone_by_augmentation(end_tone, aug)
def test_secondary_chord(self): print('----- test_secondary_tonality -----') diatonic_tonality = Tonality.create(ModalityType.Major, DiatonicTone("C")) chort_t_i = TertianChordTemplate.parse('tI') chord_i = chort_t_i.create_chord(diatonic_tonality) chord_v_ii = SecondaryChordTemplate.parse('V/ii').create_chord( diatonic_tonality) chord_vi_v = SecondaryChordTemplate.parse('vi/V').create_chord( diatonic_tonality) chord_t_ii = TertianChordTemplate.parse('tii') chord_ii = chord_t_ii.create_chord(diatonic_tonality) hc_track = HarmonicContextTrack() hc_track.append( HarmonicContext(diatonic_tonality, chord_i, Duration(1))) hc_track.append( HarmonicContext(diatonic_tonality, chord_v_ii, Duration(1))) hc_track.append( HarmonicContext(diatonic_tonality, chord_vi_v, Duration(1))) hc_track.append( HarmonicContext(diatonic_tonality, chord_ii, Duration(1))) TestTFlip.print_hct(hc_track) tune = [('C:5', (1, 1)), ('E:5', (1, 1)), ('E:5', (1, 1)), ('G:5', (1, 1))] line = TestTFlip.build_line(tune) cue = DiatonicPitch(5, 'd') tflip = TDiatonicReflection(line, hc_track, cue) temporal_extent = Interval(Fraction(0), Fraction(4)) score_line, score_hct = tflip.apply(temporal_extent, cue) TestTFlip.print_notes(score_line) TestTFlip.print_hct(score_hct) notes = score_line.get_all_notes() assert len(notes) == 4 assert str(notes[0].diatonic_pitch) == 'E:5' assert str(notes[1].diatonic_pitch) == 'C#:5' assert str(notes[2].diatonic_pitch) == 'C:5' assert str(notes[3].diatonic_pitch) == 'A:4' hc_list = score_hct.hc_list() assert len(hc_list) == 4 assert hc_list[1].chord.primary_chord.chord_template.scale_degree == 7 assert {t[0].diatonic_symbol for t in hc_list[1].chord.tones} == {'C#', 'E', 'G'} assert hc_list[1].chord.primary_chord.chord_template.inversion == 3 assert hc_list[2].chord.primary_chord.chord_template.scale_degree == 7 assert {t[0].diatonic_symbol for t in hc_list[2].chord.tones} == {'C', 'F#', 'A'} assert hc_list[2].chord.primary_chord.chord_template.inversion == 3