Skip to content
Snippets Groups Projects
Commit 9eeae168 authored by Axel Marmoret's avatar Axel Marmoret
Browse files

Clean of the content

parent 75e999c6
Branches
Tags
No related merge requests found
Showing
with 1617 additions and 312 deletions
...@@ -26,6 +26,8 @@ persisted_content ...@@ -26,6 +26,8 @@ persisted_content
#Dump #Dump
DoNotPush DoNotPush
*/DoNotPush/* */DoNotPush/*
guichaoua_pc_costs
*/guichaoua_pc_costs/*
#Baseline folders, do not need to be pushed (code duplication) #Baseline folders, do not need to be pushed (code duplication)
Baselines Baselines
......
Copyright (c) 2021 Marmoret Axel Copyright (c) 2021 Marmoret Axel
BSD 3-Clause "New" or "Revised" License
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
......
from . import triad_transformations
from . import chord_movement from . import chord_movement
from . import data_manipulation from . import data_manipulation
#from . import pattern_manipulation #from . import pattern_manipulation
...@@ -11,3 +12,4 @@ from .model import chord ...@@ -11,3 +12,4 @@ from .model import chord
from .model import note from .model import note
from .model import constants from .model import constants
from .model import errors from .model import errors
from .model import triad_manipulation
This diff is collapsed.
...@@ -183,7 +183,7 @@ def apply_triadic_mvt(chord, mvt, chromatic = True): ...@@ -183,7 +183,7 @@ def apply_triadic_mvt(chord, mvt, chromatic = True):
The new chord, after applying the relation. The new chord, after applying the relation.
""" """
circle = dm.get_circle_of_triads(flat = True, chromatic = chromatic) circle = dm.get_circle_of_triads(flat = False, chromatic = chromatic)
idx = get_triadic_position(chord, chromatic = chromatic) idx = get_triadic_position(chord, chromatic = chromatic)
return circle[(idx + mvt)%24] return circle[(idx + mvt)%24]
......
# -*- coding: utf-8 -*-
"""
Created on Thu Aug 5 12:21:07 2021
@author: amarmore
"""
from IPython.display import display, Markdown
# Self-code imports
import polytopes.segmentation_algorithms as algos
import polytopes.data_manipulation as dm
#Generic imports
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import math
database_path = "C:/Users/amarmore/Desktop/Projects/RWC_annotations/final_bimbot_al/"
annotations_folder_path = "C:/Users/amarmore/Desktop/Audio samples/RWC Pop/annotations/final_bimbot/"
def compute_scores_for_different_penalties(database, database_name, segments_size_range, polytope_irr_range, relation_type, max_size = 42):
"""
Compute the scores for different penalty values and print the in readable format.
Parameters
----------
database : TYPE
DESCRIPTION.
segments_size_range : TYPE
DESCRIPTION.
polytope_irr_range : TYPE
DESCRIPTION.
relation_type : TYPE
DESCRIPTION.
max_size : TYPE, optional
DESCRIPTION. The default is 42.
Returns
-------
None.
"""
all_scores = math.inf * np.ones((len(database), len(segments_size_range), len(polytope_irr_range), 2, 3))
for idx_song, song in enumerate(database):
#time_start = time.time()
song_number = song.split(".")[0]
#print(song_number)
bag_of_chords = dm.flowify_song(database_path + song)
annot_name = "{:03d}.manual.seg".format(int(song_number))
annotation_file = open(annotations_folder_path + annot_name,'r')
annotation = annotation_file.read().replace("\n", "").split(" ")
annotation = np.array([int(x) - 1 for x in annotation])
beat_indexed_annotation = np.array(dm.frontiers_to_segments(annotation))
for idx_seg, segment_size_penalty in enumerate(segments_size_range):
for idx_irr, polytope_irregularity_penalty in enumerate(polytope_irr_range):
frontiers, cost = algos.dynamic_minimization_guichaoua_persist_segments(bag_of_chords, database_name,
segment_size_penalty = segment_size_penalty,
min_size = 8, max_size = max_size, target_size = 32,
polytope_irregularity_penalty = polytope_irregularity_penalty,
relation_type = relation_type, song_number = song_number)
#for fst, snd in zip(frontiers[:-1], frontiers[1:]):
# g_distrib_segments.append(snd - fst)
#Scores, computed on the beat annotation
beat_indexed_segments = dm.frontiers_to_segments(frontiers)
all_scores[idx_song, idx_seg, idx_irr, 0] = dm.compute_score_of_segmentation(beat_indexed_annotation, beat_indexed_segments,
window_length = 0.2)
all_scores[idx_song, idx_seg, idx_irr, 1] = dm.compute_score_of_segmentation(beat_indexed_annotation, beat_indexed_segments,
window_length = 3)
"""print_res_for_songs_range(g_all_res_zero_tol, context = "Zero Tolerance")
print_res_for_songs_range(g_all_res_three_tol, context = "Three Tolerance")
plt.figure(figsize=(15,5))
plt.hist(g_distrib_segments, bins = range(max_size))
plt.xlabel("Segment's size (in nb of beats)")
#plt.title("Prec: {}, rap: {}, F measure: {}".format(prec, rap, fmes))
plt.plot()"""
lines = np.array(['Precision', 'Recall', 'F measure'])
all_scores_np = np.array(all_scores)
arr_zero_five = []
arr_three = []
col_seg = []
col_irr = []
for i_seg in range(len(segments_size_range)):
for i_irr in range(len(polytope_irr_range)):
line_zero_five = []
line_three = []
for j in range(3):
line_zero_five.append(round(np.mean(all_scores_np[:,i_seg, i_irr, 0, j]),4))
line_three.append(round(np.mean(all_scores_np[:,i_seg, i_irr, 1, j]),4))
arr_zero_five.append(line_zero_five)
arr_three.append(line_three)
col_seg.append(f"Size penalty: {segments_size_range[i_seg]}")
col_irr.append(f"Polytope penalty: {polytope_irr_range[i_irr]}")
col = [np.array(col_seg),np.array(col_irr)]
dataframe = pd.DataFrame(arr_zero_five, index=col, columns=lines)
display(dataframe.style.bar(subset=["F measure"], color='#5fba7d'))
dataframe = pd.DataFrame(arr_three, index=col, columns=lines)
display(dataframe.style.bar(subset=["F measure"], color='#5fba7d'))
\ No newline at end of file
...@@ -54,7 +54,7 @@ def get_circle_of_triads(flat = True, chromatic = True): ...@@ -54,7 +54,7 @@ def get_circle_of_triads(flat = True, chromatic = True):
# if flat: # if flat:
# return ['C','Fm','Db','Gbm','D','Gm','Eb','Abm','E','Am','F','Bbm','Gb','Bm','G','Cm','Ab','Dbm','A','Dm','Bb','Ebm','B','Em'] # return ['C','Fm','Db','Gbm','D','Gm','Eb','Abm','E','Am','F','Bbm','Gb','Bm','G','Cm','Ab','Dbm','A','Dm','Bb','Ebm','B','Em']
# else: # else:
# return ['C','Fm','C#','F#m','D','Gm','D#','G#m','E','Am','F','A#m','F#','Bm','G','Cm','G#','C#m','A','Cm','A#','D#m','B','Em'] # return ['C','Fm','C#','F#m','D','Gm','D#','G#m','E','Am','F','A#m','F#','Bm','G','Cm','G#','C#m','A','Dm','A#','D#m','B','Em']
# %% Read and treat inputs # %% Read and treat inputs
def get_piano_roll(path, hop_length_seconds, threshold_offset = None): def get_piano_roll(path, hop_length_seconds, threshold_offset = None):
...@@ -477,13 +477,18 @@ def flowify_song(path): ...@@ -477,13 +477,18 @@ def flowify_song(path):
try: try:
bag_of_chords.append(bag_of_chords[-1]) bag_of_chords.append(bag_of_chords[-1])
except IndexError: except IndexError:
if len(np.unique(chords)) == 1: if len(np.unique(chords)) == 1 or (len(np.unique(chords)) == 2 and '' in np.unique(chords)):
raise NotImplementedError("Song is only composed of silence, to check the origianl file at {}".format(path)) raise NotImplementedError("Song is only composed of silence, to check the original file at {}".format(path))
else: else:
for i in chords: for i in chords:
if i != "N": if i != "N":
bag_of_chords.append(Chord(i)) try:
break Chord(i)
bag_of_chords.append(Chord(i))
break
except err.InvalidChordNotesException:
raise NotImplementedError(f"Chord '{i}' have cause failure, on line: {line}")
return bag_of_chords return bag_of_chords
def frontiers_to_chord_sequence(flow, frontiers): def frontiers_to_chord_sequence(flow, frontiers):
......
File moved
File moved
File moved
...@@ -7,6 +7,9 @@ Created on Mon Dec 9 11:06:19 2019 ...@@ -7,6 +7,9 @@ Created on Mon Dec 9 11:06:19 2019
class InvalidArgumentValueException(BaseException): pass class InvalidArgumentValueException(BaseException): pass
class OutdatedBehaviorException(BaseException): pass class OutdatedBehaviorException(BaseException): pass
class TriadException(BaseException): pass
class NotAMajMinTriadException(TriadException): pass
class NoteException(BaseException): pass class NoteException(BaseException): pass
class InvalidNoteException(NoteException): pass class InvalidNoteException(NoteException): pass
class InvalidNoteNumberException(InvalidNoteException): pass class InvalidNoteNumberException(InvalidNoteException): pass
......
File moved
# -*- coding: utf-8 -*-
"""
Created on Tue Nov 26 10:08:18 2019
@author: amarmore
"""
import math
import music21
import polytopes.data_manipulation as dm
import polytopes.model.errors as err
notes_sharp = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']
notes_flat = ['C','Db','D','Eb','E','F','Gb','G','Ab','A','Bb','B']
### Basic chord manipulation
# We only consider minor and major triads
def triad_symbol_from_notes(triad_notes):
notes = reindex_inversed_triad(triad_notes)
triad = notes_sharp[notes[0]]
if (notes[1] - notes[0])%12 == 3:
triad += "m"
return triad
def root_from_notes(notes):
notes = reindex_inversed_triad(notes)
return notes_sharp[notes[0]]
def triad_notes_from_symbol(symbol, triadic_reduction = True):
symbol = little_format_symbol(symbol)
if not is_maj_min_triad_from_symbol(symbol):
if not triadic_reduction:
raise err.NotAMajMinTriadException(f"This symbol ({symbol}) does not correspond to a major or minor triad, which is our only case study.")
else:
symbol = maj_min_triad_reduction_of_symbol(symbol)
notes = []
root_name = root_from_symbol(symbol)
for i in range(12):
if notes_sharp[i] == root_name or notes_flat[i] == root_name:
root = i
notes.append(root)
break
if "m" in symbol:
notes.append((root+3)%12)
else:
notes.append((root+4)%12)
notes.append((root+7)%12)
return notes
def root_from_symbol(symbol):
root_name = symbol[0]
if len(symbol) > 1:
if symbol[1] == '-' or symbol[1] == 'b':
root_name += 'b'
elif symbol[1] == '+' or symbol[1] == '#':
root_name += '#'
return root_name
def reindex_inversed_triad(list_of_notes_numbers):
if len(list_of_notes_numbers) != 3:
raise err.NotAMajMinTriadException(f"Too many notes for this chord to be a triad ({list_of_notes_numbers}).")
for i in range(3):
root = list_of_notes_numbers[i]
first_gap = (list_of_notes_numbers[(i+1)%3] - root)%12
second_gap = (list_of_notes_numbers[(i+2)%3] - root)%12
if first_gap == 7:
if second_gap == 3 or second_gap == 4:
return [root, list_of_notes_numbers[(i+2)%3], list_of_notes_numbers[(i+1)%3]]
elif second_gap == 7:
if first_gap == 3 or first_gap == 4:
return [root, list_of_notes_numbers[(i+1)%3], list_of_notes_numbers[(i+2)%3]]
raise err.NotAMajMinTriadException(f"These notes ({list_of_notes_numbers}) does not correspond to a major or minor triad, which is our only case study.")
def is_maj_min_triad_from_notes(list_of_notes_numbers):
if len(list_of_notes_numbers) != 3:
return False
for i in range(3):
root = list_of_notes_numbers[i]
first_gap = (list_of_notes_numbers[(i+1)%3] - root)%12
second_gap = (list_of_notes_numbers[(i+2)%3] - root)%12
if first_gap == 7:
if second_gap == 3 or second_gap == 4:
return True
elif second_gap == 7:
if first_gap == 3 or first_gap == 4:
return True
return False
def is_maj_min_triad_from_symbol(symbol):
symbol = little_format_symbol(symbol)
root = root_from_symbol(symbol)
idx_post_root = len(root)
return len(symbol) == idx_post_root or (len(symbol) == idx_post_root + 1 and symbol[idx_post_root] == "m")
def maj_min_triad_reduction_of_symbol(symbol, to_sharp = True):
"""
Reducing a chord to its maj or min triad
Returns
-------
string: the triad symbol of the chord.
"""
symbol = little_format_symbol(symbol)
root = root_from_symbol(symbol)
if to_sharp:
if root not in notes_sharp:
root_idx = notes_flat.index(root)
root = notes_sharp[root_idx]
idx_post_root = len(root)
if len(symbol) > idx_post_root and symbol[idx_post_root] in ["m","d"]: # minor or diminished triad
root += "m"
return root
def little_format_symbol(symbol):
if "maj" in symbol:
symbol = symbol.replace("maj","")
if "min" in symbol:
symbol = symbol.replace("min","m")
if "dim" in symbol:
symbol = symbol.replace("dim","d")
return symbol
File moved
File moved
# -*- coding: utf-8 -*-
"""
Created on Tue Mar 23 15:45:22 2021
@author: amarmore
"""
import math
import music21
import itertools
import polytopes.data_manipulation as dm
import polytopes.model.errors as err
import polytopes.model.triad_manipulation as tm
import polytopes.voiceleading_utilities as vl
circle_flat = ['C','Am','F','Dm','Bb','Gm','Eb','Cm','Ab','Fm','Db','Bbm','Gb','Ebm','B','Abm','E','Dbm','A','Gbm','D','Bm','G','Em']
circle_sharp = ['C','Am','F','Dm','A#','Gm','D#','Cm','G#','Fm','C#','A#m','F#','D#m','B','G#m','E','C#m','A','F#m','D','Bm','G','Em']
chromatic_circle_sharp = ['Cm', 'C', 'C#m', 'C#', 'Dm', 'D', 'D#m', 'D#', 'Em', 'E', 'Fm', 'F', 'F#m', 'F#', 'Gm', 'G', 'G#m', 'G#', 'Am', 'A', 'A#m', 'A#', 'Bm', 'B']
three_five_torus_sharp = ['C','Fm','C#','F#m','D','Gm','D#','G#m','E','Am','F','A#m','F#','Bm','G','Cm','G#','C#m','A','Dm','A#','D#m','B','Em']
# %% Voice leading, in optimal transport
# Use Dmitri Tymoczko module instead
def get_voice_leading_distance_symbol(triad_symbol_a, triad_symbol_b, triadic_reduction = True):
return get_voice_leading_distance_notes(tm.triad_notes_from_symbol(triad_symbol_a, triadic_reduction = triadic_reduction), tm.triad_notes_from_symbol(triad_symbol_b, triadic_reduction = triadic_reduction))
def get_voice_leading_distance_notes(triad_a, triad_b):
if not tm.is_maj_min_triad_from_notes(triad_a):
raise err.NotAMajMinTriadException(f"This chord ({triad_a}) does not correspond to a major or minor triad, which is our only case study.")
if not tm.is_maj_min_triad_from_notes(triad_b):
raise err.NotAMajMinTriadException(f"This chord ({triad_b}) does not correspond to a major or minor triad, which is our only case study.")
return vl.nonbijective_vl(triad_a, triad_b)[0]
## Simplistic voice leading
def get_voice_leading_transformation_symbol(triad_symbol_a, triad_symbol_b, triadic_reduction = True):
return get_voice_leading_transformation_notes(tm.triad_notes_from_symbol(triad_symbol_a, triadic_reduction = triadic_reduction), tm.triad_notes_from_symbol(triad_symbol_b, triadic_reduction = triadic_reduction))
def get_voice_leading_transformation_notes(triad_a, triad_b):
if not tm.is_maj_min_triad_from_notes(triad_a):
raise err.NotAMajMinTriadException(f"This chord ({triad_a}) does not correspond to a major or minor triad, which is our only case study.")
if not tm.is_maj_min_triad_from_notes(triad_b):
raise err.NotAMajMinTriadException(f"This chord ({triad_b}) does not correspond to a major or minor triad, which is our only case study.")
return [triad_b[i] - triad_a[i] for i in range(3)]
## Work In Progress
def apply_voice_leading_transformation(triad, transformation):
#TODO: reflechir à cette fonction
if not tm.is_maj_min_triad_from_notes(triad):
raise err.NotAMajMinTriadException(f"This chord ({triad}) does not correspond to a major or minor triad, which is our only case study.")
if len(transformation) != 3:
raise NotImplementedError("Not a valid transformation, better error TODO")
print("Check that it always returns a triad, or act consequently")
return [(triad[i] + transformation[i])%12 for i in range(3)]
# %% Triadic relation
def get_triadic_position_symbol(triad_symbol, triadic_reduction = True):
if not tm.is_maj_min_triad_from_symbol(triad_symbol) and not triadic_reduction:
raise err.NotAMajMinTriadException(f"This chord ({triad_symbol}) does not correspond to a major or minor triad, which is our only case study.")
triad = tm.maj_min_triad_reduction_of_symbol(triad_symbol, to_sharp = True)
return circle_sharp.index(triad)
def get_triadic_position_notes(triad_notes):
if not tm.is_maj_min_triad_from_notes(triad_notes):
raise err.NotAMajMinTriadException(f"This chord ({triad_notes}) does not correspond to a major or minor triad, which is our only case study.")
symb = tm.triad_symbol_from_notes(triad_notes)
return get_triadic_position_symbol(symb)
def triadic_mvt_triads(first_triad_symbol, second_triad_symbol, triadic_reduction = True):
"""
Compute the movement/relation between both chords in the circle of triads.
It depends on the circle, and can be either on circle of fifth (chromatic) or on the 3-5 Torus (!chromatic)
NB: Now, only the circle of fifth is tolerated. Could be temporary or permanent.
Parameters
----------
first_chord_symbol, second_chord_symbol : strings
The symbols of both chords which relation is to compute.
Returns
-------
mvt : integer
The relation between both chords.
"""
mvt = (get_triadic_position_symbol(second_triad_symbol, triadic_reduction = triadic_reduction) - get_triadic_position_symbol(first_triad_symbol, triadic_reduction = triadic_reduction))%24
if mvt > 12:
mvt -= 24
return mvt
def apply_triadic_mvt(triad_symbol, mvt, triadic_reduction = True):
"""
Apply a movement/relation to a chord in the circle of triads.
Parameters
----------
triad_symbol : string
The symbol of the triad, on which to aply the movement.
mvt : integer
The relation to apply.
Returns
-------
string
The new triad symbol, after applying the relation.
"""
if not tm.is_maj_min_triad_from_symbol(triad_symbol) and not triadic_reduction:
raise err.NotAMajMinTriadException(f"This chord ({triad_symbol}) does not correspond to a major or minor triad, which is our only case study.")
triad = tm.maj_min_triad_reduction_of_symbol(triad_symbol, to_sharp = True)
idx = get_triadic_position_symbol(triad)
return circle_sharp[(idx + mvt)%24]
# %% Triadic chromatic relation
def get_chromatic_position_symbol(triad_symbol, triadic_reduction = True):
if not tm.is_maj_min_triad_from_symbol(triad_symbol) and not triadic_reduction:
raise err.NotAMajMinTriadException(f"This chord ({triad_symbol}) does not correspond to a major or minor triad, which is our only case study.")
triad = tm.maj_min_triad_reduction_of_symbol(triad_symbol, to_sharp = True)
return chromatic_circle_sharp.index(triad)
def get_chromatic_position_notes(triad_notes):
if not tm.is_maj_min_triad_from_notes(triad_notes):
raise err.NotAMajMinTriadException(f"This chord ({triad_notes}) does not correspond to a major or minor triad, which is our only case study.")
symb = tm.triad_symbol_from_notes(triad_notes)
return get_chromatic_position_symbol(symb)
def chromatic_mvt_triads(first_triad_symbol, second_triad_symbol, triadic_reduction = True):
mvt = (get_chromatic_position_symbol(second_triad_symbol, triadic_reduction = triadic_reduction) - get_chromatic_position_symbol(first_triad_symbol, triadic_reduction = triadic_reduction))%24
if mvt > 12:
mvt -= 24
return mvt
def apply_chromatic_mvt(triad_symbol, mvt, triadic_reduction = True):
if not tm.is_maj_min_triad_from_symbol(triad_symbol) and not triadic_reduction:
raise err.NotAMajMinTriadException(f"This chord ({triad_symbol}) does not correspond to a major or minor triad, which is our only case study.")
triad = tm.maj_min_triad_reduction_of_symbol(triad_symbol, to_sharp = True)
idx = get_chromatic_position_symbol(triad)
return chromatic_circle_sharp[(idx + mvt)%24]
# %% Triadic 3-5 torus relation
def get_three_five_torus_position_symbol(triad_symbol, triadic_reduction = True):
if not tm.is_maj_min_triad_from_symbol(triad_symbol) and not triadic_reduction:
raise err.NotAMajMinTriadException(f"This chord ({triad_symbol}) does not correspond to a major or minor triad, which is our only case study.")
triad = tm.maj_min_triad_reduction_of_symbol(triad_symbol, to_sharp = True)
return three_five_torus_sharp.index(triad)
def get_three_five_torus_position_notes(triad_notes):
if not tm.is_maj_min_triad_from_notes(triad_notes):
raise err.NotAMajMinTriadException(f"This chord ({triad_notes}) does not correspond to a major or minor triad, which is our only case study.")
symb = tm.triad_symbol_from_notes(triad_notes)
return get_three_five_torus_position_symbol(symb)
def three_five_torus_mvt_triads(first_triad_symbol, second_triad_symbol, triadic_reduction = True):
mvt = (get_three_five_torus_position_symbol(second_triad_symbol, triadic_reduction = triadic_reduction) - get_three_five_torus_position_symbol(first_triad_symbol, triadic_reduction = triadic_reduction))%24
if mvt > 12:
mvt -= 24
return mvt
def apply_three_five_torus_mvt(triad_symbol, mvt, triadic_reduction = True):
if not tm.is_maj_min_triad_from_symbol(triad_symbol) and not triadic_reduction:
raise err.NotAMajMinTriadException(f"This chord ({triad_symbol}) does not correspond to a major or minor triad, which is our only case study.")
triad = tm.maj_min_triad_reduction_of_symbol(triad_symbol, to_sharp = True)
idx = get_three_five_torus_position_symbol(triad)
return three_five_torus_sharp[(idx + mvt)%24]
# %% Tonnetz distances, for triads
def triadic_tonnetz_relation_notes(triad_notes_a, triad_notes_b):
a = music21.chord.Chord(triad_notes_a)
c_1 = music21.analysis.neoRiemannian._simplerEnharmonics(a)
b = music21.chord.Chord(triad_notes_b)
c_2 = music21.analysis.neoRiemannian._simplerEnharmonics(b)
if c_2.orderedPitchClasses == c_1.orderedPitchClasses:
return 0
for i in range(1,9):
for permut in itertools.product('LPR', repeat=i):
try:
c_1_permuted = music21.analysis.neoRiemannian.LRP_combinations(c_1, permut, raiseException=True, simplifyEnharmonics=True)
except music21.analysis.neoRiemannian.LRPException:
# Happens sometimes for now, but should be solved in near future
c_1_permuted = manually_loop_lrp_comb(c_1, permut)
if c_2.orderedPitchClasses == c_1_permuted.orderedPitchClasses:
return permut
raise NotImplementedError("Not found, try increasing the accepted amout of relations.")
def triadic_tonnetz_distance_notes(triad_notes_a, triad_notes_b):
relation = triadic_tonnetz_relation_notes(triad_notes_a, triad_notes_b)
if relation == 0 or relation == None:
return 0
return len(relation)
def triadic_tonnetz_relation_symbol(triad_symbol_a, triad_symbol_b):
return triadic_tonnetz_relation_notes(tm.triad_notes_from_symbol(triad_symbol_a), tm.triad_notes_from_symbol(triad_symbol_b))
def triadic_tonnetz_distance_symbol(triad_symbol_a, triad_symbol_b):
relation = triadic_tonnetz_relation_symbol(triad_symbol_a, triad_symbol_b)
if relation == 0 or relation == None:
return 0
return len(relation)
def manually_loop_lrp_comb(chord, relations):
while len(relations) > 0:
if relations[-1] == "P":
chord = music21.analysis.neoRiemannian.P(chord)
elif relations[-1] == "L":
chord = music21.analysis.neoRiemannian.L(chord)
elif relations[-1] == "R":
chord = music21.analysis.neoRiemannian.R(chord)
else:
raise err.InvalidArgumentValueException(f"Invalid relation type: {relations[-1]}, only L, P and R are accepted.")
chord = music21.analysis.neoRiemannian._simplerEnharmonics(chord)
relations = relations[:-1]
return chord
def apply_triadic_tonnetz_relation_notes(triad_notes_a, LRP_relation):
a = music21.chord.Chord(triad_notes_a)
c_1 = music21.analysis.neoRiemannian._simplerEnharmonics(a)
if LRP_relation == 0 or LRP_relation == None:
return triad_notes_a
try:
c_transformed = music21.analysis.neoRiemannian.LRP_combinations(c_1, LRP_relation, raiseException=True, simplifyEnharmonics=True)
except music21.analysis.neoRiemannian.LRPException:
# Happens sometimes for now, but should be solved in near future
c_transformed = manually_loop_lrp_comb(c_1, LRP_relation)
notes = []
for a_pitch in c_transformed.pitches:
notes.append(int(a_pitch.ps)%12)
return tm.reindex_inversed_triad(notes)
def apply_triadic_tonnetz_relation_symbol(triad_symbol_a, LRP_relation):
if LRP_relation == 0:
return triad_symbol_a
notes = tm.triad_notes_from_symbol(triad_symbol_a)
notes_transformed = apply_triadic_tonnetz_relation_notes(notes, LRP_relation)
return tm.triad_symbol_from_notes(notes_transformed)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment