dissonance.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. # algorithme de calcul de dissonance
  2. # prenant en argument une liste de fréquences
  3. # ou de notes, et déterminant si l'accord obtenu
  4. # est consonnant ou dissonnant.
  5. # utiliser http://www.mandolintab.net/abcconverter.php pour créer des partitions avec la syntaxe abc
  6. import random
  7. import math
  8. notes = {'la': 440., 'si': 493.883, 'do': 261.63, 're': 293.665, 'mi': 329.628, 'fa': 349.228, 'sol': 391.995}
  9. liste_note = ['do', 're', 'mi', 'fa', 'sol', 'la', 'si']
  10. dic_note_vers_abc = {'do': 'c', 're': 'd', 'mi': 'e', 'fa': 'f', 'sol': 'g', 'la': 'a', 'si': 'b'}
  11. # Fonction qui ramène une fréquence dans un intervalle [261.63 ; 493.883]
  12. def same_freq(frequence):
  13. while frequence < 261.63:
  14. frequence *= 2.
  15. while frequence > 493.883:
  16. frequence /= 2.
  17. return float(frequence)
  18. # Fonction qui convertit une note (ex. la3) en fréquence (ex. 440)
  19. def conversion(note): # ex la3
  20. hauteur = note[-1:] # découpe la string note en ne gardant que le dernier caractère (la hauteur d'octave)
  21. nom = note[:-1] # découpe en n'enlevant que le dernier caractère
  22. if hauteur == "1":
  23. puissance = 1. / 3 # f(do1)=1/4 f(do3)
  24. elif hauteur == "2":
  25. puissance = 1. / 2 # f(do2)=1/2 f(do3)
  26. else:
  27. puissance = int(hauteur) - 2 # f(do<n>) = 2^n (f(do3))
  28. frequence = notes[nom] * (2 ** int(puissance))
  29. return frequence
  30. # Fonction qui parcourt un tableau de notes/fréquences, le change en tableau de fréquences
  31. # dans un intervalle précis
  32. def frequencificateur(tableau):
  33. tab = []
  34. for i in enumerate(tableau): # on convertit les notes en fréquences et on les alignes dans la plage [261.63 ; 493.883]
  35. if str(i[1])[:-1] in liste_note: # si i est une note (ex. fa4), on la convertit en fréquence
  36. tab.append(conversion(i[1])) # sinon on aligne juste dans l'intervalle
  37. else:
  38. tab.append(i[1])
  39. tab[int(i[0])] = same_freq(tab[i[0]])
  40. return tab
  41. # Fonction qui vérifie que toutes les notes d'un accord sont consonnantes entre elles
  42. # et retourne True le cas échéant
  43. def consonnant(accord):
  44. tab = frequencificateur(accord)
  45. # tableau avec les rapports d'octave, quinte J, quarte J, tierce M et m et sixte
  46. # Comme les calculs utilisent des floats on aura besoin d'approximer les rapports
  47. rapports = [1., 2., 2 ** (7. / 12), 2 ** (5. / 12), 2 ** (4. / 12), 2 ** (3. / 12), 2 ** (9. / 12), 2 ** (10. / 12)]
  48. final = 0 # compteur pour vérifier avec accord de plus de deux notes
  49. if len(tab) == 2: # trick pour les accords de deux notes
  50. final += 1
  51. for i in range(0,len(tab)): # on compare les fréquences une à une
  52. for j in range(0,len(tab)): # pour valider si leur rapport est consonnant
  53. if i != j:
  54. rapport = float(tab[i]) / float(tab[j])
  55. for rap in rapports:
  56. if math.isclose(rap, rapport, abs_tol=1e-3) is True: # la fonction math.isclose() permet d'admettre
  57. final += 1 # une marge d'erreur sur les calculs de floats
  58. if final == len(tab): # Si pour chaque note, elle est consonnante par rapport à toutes les autres, retourne True
  59. return True
  60. else:
  61. return False
  62. # Fonction qui crée une note aléatoire (de do à si)
  63. # à une hauteur donnée
  64. def generer_note(hauteur):
  65. nom = liste_note[random.randint(0, 6)] # une note de do à si
  66. note = str(nom) + str(hauteur) # par exemple fa2
  67. return str(note)
  68. # Fonction pour générer la partie de basse de notre musique
  69. # utilisant la fonction generer_note()
  70. def generer_basse():
  71. return generer_note(2) # 2ème octave pour la ligne de basse
  72. # Fonction pour générer la partie mélodique en fonction de la partie de basse
  73. # pour des raisons de place on génère la mélodie à la troisième octave (hauteur
  74. # "moyenne" mélodique, en utilisant la notation abc. Dans les faits, la note
  75. # est générée à hauteur 2 puis réhaussée d'une octave
  76. def generer_melodie(basse):
  77. melodie = [generer_note(random.randint(2, 2)), generer_note(random.randint(2, 2))]
  78. while consonnant(melodie + [basse]) is False:
  79. melodie = [generer_note(random.randint(2, 2)), generer_note(random.randint(2, 2))]
  80. return melodie
  81. # Fonction qui génère une liste de trois listes de notes afin de créer une musique
  82. # composée d'accords consonnants
  83. def generer_musique():
  84. basse = []
  85. melodie1 = []
  86. melodie2 = []
  87. for i in range(16):
  88. if i % 4 == 0: # On génère une note de la partie de basse tous les quatre temps
  89. basse.append(generer_basse()) # afin d'alléger la structure musicale
  90. else:
  91. basse.append(basse[i - 1])
  92. melodie_double = generer_melodie(basse[i]) # on génère la mélodie selon la
  93. melodie1.append(melodie_double[0]) # partie de basse
  94. melodie2.append(melodie_double[1])
  95. return [basse, melodie1, melodie2]
  96. # Fonction qui génère une ligne de texte de partition au format abc en convertissant un tableau de notes
  97. def ligne_abc_basse(tableau):
  98. sortie = " "
  99. for i in range(16):
  100. if i % 4 == 0:
  101. # attention à la syntaxe : On prend la clef du dictionnaire qui correspond à la note sans sa hauteur
  102. # ex. : dic_note_vers_abc['fa'] au lieu de fa5, pour le convertir simplement en "f"
  103. sortie += " "
  104. sortie += dic_note_vers_abc[str(tableau[i])[:-1]] # la ligne de la complication syntaxique d'un homme aux abois
  105. sortie += "4"
  106. sortie += " |"
  107. return str(sortie)
  108. # exemple output :
  109. # >>> f4 | c4 | e4 | d4 |
  110. # Fonction qui génère une ligne de texte de partition mélodique au format abc en convertissant un tableau de notes
  111. def ligne_abc(tableau):
  112. sortie = ""
  113. for i in range(16):
  114. if i % 2 == 0:
  115. sortie += " "
  116. # attention à la syntaxe : On prend la clef du dictionnaire qui correspond à la note sans sa hauteur
  117. # ex. : dic_note_vers_abc['fa'] au lieu de fa5, pour le convertir simplement en "f"
  118. if random.randint(0, 10) == 5:
  119. sortie += "z"
  120. else:
  121. sortie += dic_note_vers_abc[str(tableau[i])[:-1]] # la ligne de la complication syntaxique d'un homme aux abois
  122. num = int(tableau[i][-1:]) - 2
  123. for octave in range(num):
  124. sortie += "'" # aligne la hauteur avec les apostrophes en abc ( " ' " )
  125. if i % 2 == 0:
  126. sortie += " "
  127. if i % 4 == 3:
  128. sortie += "|"
  129. return sortie
  130. # exemple output :
  131. # >>> f'c cd | d'e f'z | f'c zd | d'e f'g |
  132. # Fonction appelant ligne_abc() et ligne_abc_basse() pour écrire du abc dans un fichier texte
  133. def generer_fichier_abc(triple_table):
  134. file = open('./maPartition.txt', 'w')
  135. file.write("X:1\nT:Douce Nuit de Printemps sur la Colline\nM:2/4\nC:Francois Gwillou\nQ:1/4=92\nL:1/4\n")
  136. file.write("V:T1 clef=treble-8 name=\"Tenore I\" snm=\"T.I\"\n")
  137. file.write("V:T2 clef=treble-8 name=\"Tenore II\" snm=\"T.II\"\n")
  138. file.write("V:B1 middle=d clef=bass name=\"Basso I\" snm=\"B.I\" transpose=-24\n")
  139. file.write("K:C\n")
  140. file.write("%On commence la partition\n")
  141. file.write("[V:T1] " + ligne_abc(triple_table[1]) + "|\n")
  142. file.write("[V:T2] " + ligne_abc(triple_table[2]) + "|\n")
  143. file.write("[V:B1] " + ligne_abc_basse(triple_table[0]) + "|\n")
  144. # Début du programme
  145. # accord_de_base contient les notes à tester
  146. accord_de_base = ['mi4','sol4']
  147. # on teste la consonnance de notre accord
  148. if consonnant(accord_de_base) is True:
  149. print("Ca passe trois fois !")
  150. else:
  151. print("Je l'tenterais pas")
  152. print(349.228 / 261.63)
  153. print("Maintenant, on génère une partition !")
  154. # voir ligne du dessus
  155. mes_trois_voix = generer_musique()
  156. generer_fichier_abc(mes_trois_voix)