Ribot commited on
Commit
483bd60
·
verified ·
1 Parent(s): f531bdd

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +256 -0
app.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import subprocess
3
+ import sys
4
+
5
+ # Liste des dépendances requises avec leurs versions minimales
6
+ required_packages = {
7
+ "numpy": "1.20.0",
8
+ "scipy": "1.6.0",
9
+ "gradio": "4.44.1",
10
+ "soundfile": "0.10.0",
11
+ "pydantic": "1.10.9",
12
+ "fastapi": "0.95.0"
13
+ }
14
+
15
+ def install_and_check_dependencies(packages):
16
+ """
17
+ Vérifie et installe les dépendances nécessaires.
18
+ """
19
+ for package, version in packages.items():
20
+ try:
21
+ module = __import__(package)
22
+ installed_version = module.__version__
23
+ if tuple(map(int, installed_version.split('.'))) < tuple(map(int, version.split('.'))):
24
+ print(f"Mise à jour de {package} vers {version} (actuellement {installed_version})...")
25
+ subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}>={version}"])
26
+ except ImportError:
27
+ print(f"Installation de {package}...")
28
+ subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}>={version}"])
29
+
30
+ install_and_check_dependencies(required_packages)
31
+
32
+ try:
33
+ import numpy as np
34
+ from scipy.signal import fftconvolve
35
+ from scipy.io.wavfile import write
36
+ import gradio as gr
37
+ import soundfile as sf
38
+ from pydantic import BaseModel
39
+ except ImportError as e:
40
+ print(f"Erreur d'importation: {e}")
41
+ sys.exit(1)
42
+
43
+ # Ajout de la configuration pour les modèles Pydantic
44
+ class ConfigurableModel(BaseModel):
45
+ class Config:
46
+ arbitrary_types_allowed = True
47
+
48
+ # Gammes musicales enrichies
49
+ scales = {
50
+ "Occidentale (par défaut)": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88],
51
+ "Ionienne (Majeur)": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88],
52
+ "Dorienne": [261.63, 293.66, 311.13, 349.23, 392.00, 440.00, 466.16],
53
+ "Phrygienne": [261.63, 277.18, 311.13, 349.23, 392.00, 415.30, 466.16],
54
+ "Lydienne": [261.63, 293.66, 329.63, 370.00, 392.00, 440.00, 493.88],
55
+ "Mixolydienne": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 466.16],
56
+ "Aeolienne (Mineur Naturel)": [261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16],
57
+ "Locrienne": [261.63, 277.18, 311.13, 349.23, 369.99, 415.30, 466.16],
58
+ "Arabe": [261.63, 277.18, 329.63, 369.99, 392.00, 415.30, 466.16],
59
+ "Orientale": [261.63, 293.66, 311.13, 369.99, 392.00, 440.00, 466.16],
60
+ "Hindoustanie": [261.63, 277.18, 329.63, 369.99, 392.00, 440.00, 466.16],
61
+ "Japonaise (In-Sen)": [261.63, 277.18, 329.63, 392.00, 415.30],
62
+ "Chinoise (Pentatonique)": [261.63, 293.66, 329.63, 392.00, 440.00],
63
+ "Flamenco": [261.63, 277.18, 329.63, 349.23, 369.99, 392.00, 415.30],
64
+ "Byzantine": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16],
65
+ "Double Harmonique (Arabe)": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16],
66
+ "Chromatique": [261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88],
67
+ "Blues Mineur": [261.63, 311.13, 329.63, 349.23, 392.00, 466.16],
68
+ "Blues Majeur": [261.63, 293.66, 329.63, 370.00, 392.00, 440.00],
69
+ "Enigmatique": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16],
70
+ "Super-Locrian (Altérée)": [261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99],
71
+ "Majeur Napolitain": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 493.88],
72
+ "Harmonique Majeure": [261.63, 293.66, 329.63, 349.23, 392.00, 415.30, 466.16],
73
+ "Harmonique Mineure": [261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16],
74
+ "Melodique Ascendante": [261.63, 293.66, 311.13, 349.23, 392.00, 440.00, 493.88],
75
+ "Melodique Descendante (Jazz)": [261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16],
76
+ "Bebop Dominant": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 466.16, 493.88],
77
+ "Bebop Majeur": [261.63, 293.66, 329.63, 349.23, 392.00, 415.30, 440.00, 466.16],
78
+ "Gamme Hijaz (Orientale)": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16],
79
+ "Gamme Balinaise (Gamelan)": [261.63, 293.66, 329.63, 369.99, 440.00],
80
+ "Pentatonique Chinoise": [261.63, 293.66, 329.63, 392.00, 440.00],
81
+ "Raga Bhairav (Inde)": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16],
82
+ "Raga Yaman": [261.63, 293.66, 329.63, 370.00, 392.00, 440.00, 493.88],
83
+ "Lydien Augmenté": [261.63, 293.66, 329.63, 370.00, 415.30, 440.00, 493.88],
84
+ "Phrygien Dominant": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16],
85
+ "Mixolydien b9 b13": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16],
86
+ "Altérée (Super-Locrian)": [261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99],
87
+ "Gamme Écossaise": [261.63, 293.66, 349.23, 392.00, 440.00],
88
+ "Gamme Hongroise Mineure": [261.63, 293.66, 311.13, 370.00, 392.00, 440.00, 466.16],
89
+ "Gamme Klezmer": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16],
90
+ "Enigmatique": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16],
91
+ "Double Harmonique": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16],
92
+ "Majeur Napolitain": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 493.88],
93
+ "Octatonique (Demi-Ton/Ton)": [261.63, 277.18, 311.13, 329.63, 369.99, 392.00, 440.00, 466.16],
94
+ "Hexatonique (Augmentée)": [261.63, 293.66, 311.13, 349.23, 370.00, 415.30],
95
+ "Gamme Fibonacci": [261.63, 283.63, 329.63, 392.00, 466.16, 440.00, 493.88],
96
+ "Gamme Harmoniques Naturels": [261.63, 293.66, 327.03, 349.23, 392.00, 436.04, 490.55],
97
+ "Gamme Bohémienne": [261.63, 277.18, 311.13, 349.23, 392.00, 415.30, 493.88],
98
+ "Hexatonique Hongroise": [261.63, 277.18, 311.13, 349.23, 392.00, 440.00],
99
+ "Gamme Zarlino (Juste)": [261.63, 293.33, 327.03, 349.23, 392.00, 415.30, 490.55],
100
+ "31-TET": [261.63, 270.00, 278.80, 287.94, 297.44, 307.31, 317.56, 328.20, 339.25, 350.71, 362.60, 374.94, 387.74, 401.02, 414.79, 429.07, 443.87, 459.21, 475.10, 491.57, 508.63, 526.30, 544.60, 563.56, 583.18, 603.49, 624.50, 646.25, 668.74, 692.01, 716.08],
101
+ "53-TET": [261.63, 265.00, 268.42, 271.89, 275.40, 278.97, 282.59, 286.26, 290.00, 293.78, 297.63, 301.53, 305.50, 309.52, 313.61, 317.75, 321.96, 326.23, 330.56, 334.95, 339.41, 343.94, 348.53, 353.19, 357.91, 362.71, 367.57, 372.50, 377.50, 382.58, 387.73, 392.95, 398.24, 403.61, 409.06, 414.58, 420.17, 425.85, 431.60, 437.43, 443.34, 449.32, 455.39, 461.54, 467.77, 474.08, 480.48, 486.95, 493.51, 500.15, 506.88, 513.68],
102
+ # Autres gammes...
103
+ }
104
+
105
+ def apply_envelope(signal, sample_rate, attack=0.01, release=0.01):
106
+ """Applique une enveloppe d'amplitude avec attaque et relâchement."""
107
+ num_samples = len(signal)
108
+ attack_samples = int(attack * sample_rate)
109
+ release_samples = int(release * sample_rate)
110
+
111
+ envelope = np.ones(num_samples)
112
+ # Création de l'attaque
113
+ if attack_samples > 0:
114
+ envelope[:attack_samples] = np.linspace(0, 1, attack_samples)
115
+ # Création du relâchement
116
+ if release_samples > 0:
117
+ envelope[-release_samples:] = np.linspace(1, 0, release_samples)
118
+
119
+ return signal * envelope
120
+
121
+ def generate_partition(length, pattern_length, pattern_repeats, note_speed_min, note_speed_max, scale):
122
+ """
123
+ Génère une partition complète pour la durée totale, en respectant les patterns et répétitions.
124
+ """
125
+ partition = []
126
+ current_time = 0
127
+
128
+ while current_time < length:
129
+ if pattern_length > 0:
130
+ # Générer un pattern
131
+ pattern = []
132
+ pattern_current_time = 0
133
+ while pattern_current_time < pattern_length:
134
+ interval = np.random.uniform(note_speed_min, note_speed_max)
135
+ note_duration = np.random.uniform(1.5, 3.0)
136
+ num_notes = np.random.choice([2, 3, 4])
137
+ selected_notes = np.random.choice(scale, num_notes, replace=False)
138
+
139
+ pattern.append({
140
+ "start_time": pattern_current_time,
141
+ "duration": note_duration,
142
+ "notes": selected_notes.tolist(),
143
+ "volume": np.random.uniform(0.7, 1.0)
144
+ })
145
+
146
+ pattern_current_time += interval
147
+
148
+ # Ajouter les répétitions du pattern à la partition
149
+ for repeat in range(pattern_repeats):
150
+ for note in pattern:
151
+ start_time_global = current_time + repeat * pattern_length + note["start_time"]
152
+ partition.append({
153
+ "start_time": start_time_global,
154
+ "duration": note["duration"],
155
+ "notes": note["notes"],
156
+ "volume": note["volume"]
157
+ })
158
+
159
+ current_time += pattern_length * pattern_repeats
160
+ else:
161
+ # Pas de pattern défini, ajouter des notes aléatoires
162
+ interval = np.random.uniform(note_speed_min, note_speed_max)
163
+ note_duration = np.random.uniform(1.5, 3.0)
164
+ num_notes = np.random.choice([2, 3, 4])
165
+ selected_notes = np.random.choice(scale, num_notes, replace=False)
166
+
167
+ partition.append({
168
+ "start_time": current_time,
169
+ "duration": note_duration,
170
+ "notes": selected_notes.tolist(),
171
+ "volume": np.random.uniform(0.7, 1.0)
172
+ })
173
+
174
+ current_time += interval
175
+
176
+ return partition
177
+
178
+ def synthesize_audio_with_envelope(partition, length=2.0, reverb_length=0.0, stereo_pan=0.0):
179
+ """
180
+ Synthèse avec enveloppe d'amplitude pour éviter les craquements.
181
+ """
182
+ sample_rate = 44100
183
+ total_length = length + reverb_length
184
+ t = np.linspace(0, total_length, int(sample_rate * total_length), endpoint=False)
185
+ wave = np.zeros((len(t), 2)) # Stéréo
186
+
187
+ for note in partition:
188
+ start_sample = int(note["start_time"] * sample_rate)
189
+ end_sample = start_sample + int(note["duration"] * sample_rate)
190
+ if end_sample > len(t):
191
+ end_sample = len(t)
192
+
193
+ chord_wave = np.zeros((end_sample - start_sample, 2))
194
+ for freq in note["notes"]:
195
+ left_wave = np.sin(2 * np.pi * freq * t[: end_sample - start_sample])
196
+ right_wave = np.sin(2 * np.pi * (freq + stereo_pan) * t[: end_sample - start_sample])
197
+ chord_wave[:, 0] += left_wave
198
+ chord_wave[:, 1] += right_wave
199
+
200
+ chord_wave *= note["volume"]
201
+ # Appliquer l'enveloppe pour éviter des transitions brusques
202
+ chord_wave[:, 0] = apply_envelope(chord_wave[:, 0], sample_rate)
203
+ chord_wave[:, 1] = apply_envelope(chord_wave[:, 1], sample_rate)
204
+ wave[start_sample:end_sample, :] += chord_wave
205
+
206
+ # Normalisation finale
207
+ wave /= np.max(np.abs(wave))
208
+ return wave
209
+
210
+ def generate_audio(length, reverb_length, stereo_pan, note_speed_min, note_speed_max, pattern_length, pattern_repeats, scale_name, output_format):
211
+ """
212
+ Génère le fichier audio simulant un carillon dans le vent, à partir d'une partition.
213
+ """
214
+ scale = scales[scale_name]
215
+ partition = generate_partition(length, pattern_length, pattern_repeats, note_speed_min, note_speed_max, scale)
216
+ wave = synthesize_audio_with_envelope(partition, length, reverb_length, stereo_pan)
217
+
218
+ # Sauvegarde audio
219
+ temp_dir = os.environ.get("TEMP", "/tmp")
220
+ base_output_path = os.path.join(temp_dir, f"stereo_wind_chime.{output_format.lower()}")
221
+ sf.write(base_output_path, wave, 44100, format=output_format)
222
+
223
+ # Génération de la partition texte
224
+ partition_text = "\n".join(
225
+ [f"Start: {note['start_time']:.2f}s, Duration: {note['duration']:.2f}s, Notes: {note['notes']}" for note in partition]
226
+ )
227
+
228
+ # Retourne les trois éléments requis
229
+ return base_output_path, base_output_path, partition_text
230
+
231
+ # Interface Gradio
232
+ iface = gr.Interface(
233
+ fn=generate_audio,
234
+ inputs=[
235
+ gr.Number(label="Longueur du fichier audio (en secondes)", value=60),
236
+ gr.Number(label="Longueur de la réverbération (en secondes)", value=6),
237
+ gr.Number(label="Panorama stéréo (différence en Hz)", value=3),
238
+ gr.Number(label="Rapidité des notes (min, secondes)", value=0.1),
239
+ gr.Number(label="Rapidité des notes (max, secondes)", value=1),
240
+ gr.Number(label="Longueur des patterns (en secondes)", value=10),
241
+ gr.Number(label="Nombre de répétitions des patterns", value=3),
242
+ gr.Radio(list(scales.keys()), label="Choisissez une gamme musicale", value="Occidentale (par défaut)"),
243
+ gr.Radio(["FLAC", "WAV"], label="Format de sortie", value="FLAC"),
244
+ ],
245
+ outputs=[
246
+ gr.Audio(label="Écouter le fichier audio généré"),
247
+ gr.File(label="Télécharger le fichier audio généré"),
248
+ gr.Textbox(label="Partition générée", lines=20, interactive=False),
249
+ ],
250
+ title="Générateur de Carillon avec Patterns et Répétitions",
251
+ description="Créez un carillon avec des gammes enrichies, des patterns personnalisables, et écoutez votre création.",
252
+ )
253
+
254
+ iface.launch(share=True)
255
+
256
+ input("appuyez sur une touche")