Spaces:
Sleeping
Sleeping
| import os | |
| import subprocess | |
| import sys | |
| # Liste des dépendances requises avec leurs versions minimales | |
| required_packages = { | |
| "numpy": "1.20.0", | |
| "scipy": "1.6.0", | |
| "gradio": "4.44.1", | |
| "soundfile": "0.10.0", | |
| "pydantic": "1.10.9", | |
| "fastapi": "0.95.0" | |
| } | |
| def install_and_check_dependencies(packages): | |
| """ | |
| Vérifie et installe les dépendances nécessaires. | |
| """ | |
| for package, version in packages.items(): | |
| try: | |
| module = __import__(package) | |
| installed_version = module.__version__ | |
| if tuple(map(int, installed_version.split('.'))) < tuple(map(int, version.split('.'))): | |
| print(f"Mise à jour de {package} vers {version} (actuellement {installed_version})...") | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}>={version}"]) | |
| except ImportError: | |
| print(f"Installation de {package}...") | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", f"{package}>={version}"]) | |
| install_and_check_dependencies(required_packages) | |
| try: | |
| import numpy as np | |
| from scipy.signal import fftconvolve | |
| from scipy.io.wavfile import write | |
| import gradio as gr | |
| import soundfile as sf | |
| from pydantic import BaseModel | |
| except ImportError as e: | |
| print(f"Erreur d'importation: {e}") | |
| sys.exit(1) | |
| # Ajout de la configuration pour les modèles Pydantic | |
| class ConfigurableModel(BaseModel): | |
| class Config: | |
| arbitrary_types_allowed = True | |
| # Gammes musicales enrichies | |
| scales = { | |
| "Occidentale (par défaut)": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88], | |
| "Ionienne (Majeur)": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88], | |
| "Dorienne": [261.63, 293.66, 311.13, 349.23, 392.00, 440.00, 466.16], | |
| "Phrygienne": [261.63, 277.18, 311.13, 349.23, 392.00, 415.30, 466.16], | |
| "Lydienne": [261.63, 293.66, 329.63, 370.00, 392.00, 440.00, 493.88], | |
| "Mixolydienne": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 466.16], | |
| "Aeolienne (Mineur Naturel)": [261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16], | |
| "Locrienne": [261.63, 277.18, 311.13, 349.23, 369.99, 415.30, 466.16], | |
| "Arabe": [261.63, 277.18, 329.63, 369.99, 392.00, 415.30, 466.16], | |
| "Orientale": [261.63, 293.66, 311.13, 369.99, 392.00, 440.00, 466.16], | |
| "Hindoustanie": [261.63, 277.18, 329.63, 369.99, 392.00, 440.00, 466.16], | |
| "Japonaise (In-Sen)": [261.63, 277.18, 329.63, 392.00, 415.30], | |
| "Chinoise (Pentatonique)": [261.63, 293.66, 329.63, 392.00, 440.00], | |
| "Flamenco": [261.63, 277.18, 329.63, 349.23, 369.99, 392.00, 415.30], | |
| "Byzantine": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16], | |
| "Double Harmonique (Arabe)": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16], | |
| "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], | |
| "Blues Mineur": [261.63, 311.13, 329.63, 349.23, 392.00, 466.16], | |
| "Blues Majeur": [261.63, 293.66, 329.63, 370.00, 392.00, 440.00], | |
| "Enigmatique": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16], | |
| "Super-Locrian (Altérée)": [261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99], | |
| "Majeur Napolitain": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 493.88], | |
| "Harmonique Majeure": [261.63, 293.66, 329.63, 349.23, 392.00, 415.30, 466.16], | |
| "Harmonique Mineure": [261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16], | |
| "Melodique Ascendante": [261.63, 293.66, 311.13, 349.23, 392.00, 440.00, 493.88], | |
| "Melodique Descendante (Jazz)": [261.63, 293.66, 311.13, 349.23, 392.00, 415.30, 466.16], | |
| "Bebop Dominant": [261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 466.16, 493.88], | |
| "Bebop Majeur": [261.63, 293.66, 329.63, 349.23, 392.00, 415.30, 440.00, 466.16], | |
| "Gamme Hijaz (Orientale)": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16], | |
| "Gamme Balinaise (Gamelan)": [261.63, 293.66, 329.63, 369.99, 440.00], | |
| "Pentatonique Chinoise": [261.63, 293.66, 329.63, 392.00, 440.00], | |
| "Raga Bhairav (Inde)": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16], | |
| "Raga Yaman": [261.63, 293.66, 329.63, 370.00, 392.00, 440.00, 493.88], | |
| "Lydien Augmenté": [261.63, 293.66, 329.63, 370.00, 415.30, 440.00, 493.88], | |
| "Phrygien Dominant": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16], | |
| "Mixolydien b9 b13": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16], | |
| "Altérée (Super-Locrian)": [261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99], | |
| "Gamme Écossaise": [261.63, 293.66, 349.23, 392.00, 440.00], | |
| "Gamme Hongroise Mineure": [261.63, 293.66, 311.13, 370.00, 392.00, 440.00, 466.16], | |
| "Gamme Klezmer": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16], | |
| "Enigmatique": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 466.16], | |
| "Double Harmonique": [261.63, 277.18, 329.63, 349.23, 392.00, 415.30, 466.16], | |
| "Majeur Napolitain": [261.63, 277.18, 329.63, 349.23, 392.00, 440.00, 493.88], | |
| "Octatonique (Demi-Ton/Ton)": [261.63, 277.18, 311.13, 329.63, 369.99, 392.00, 440.00, 466.16], | |
| "Hexatonique (Augmentée)": [261.63, 293.66, 311.13, 349.23, 370.00, 415.30], | |
| "Gamme Fibonacci": [261.63, 283.63, 329.63, 392.00, 466.16, 440.00, 493.88], | |
| "Gamme Harmoniques Naturels": [261.63, 293.66, 327.03, 349.23, 392.00, 436.04, 490.55], | |
| "Gamme Bohémienne": [261.63, 277.18, 311.13, 349.23, 392.00, 415.30, 493.88], | |
| "Hexatonique Hongroise": [261.63, 277.18, 311.13, 349.23, 392.00, 440.00], | |
| "Gamme Zarlino (Juste)": [261.63, 293.33, 327.03, 349.23, 392.00, 415.30, 490.55], | |
| "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], | |
| "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], | |
| # Autres gammes... | |
| } | |
| def apply_envelope(signal, sample_rate, attack=0.01, release=0.01): | |
| """Applique une enveloppe d'amplitude avec attaque et relâchement.""" | |
| num_samples = len(signal) | |
| attack_samples = int(attack * sample_rate) | |
| release_samples = int(release * sample_rate) | |
| envelope = np.ones(num_samples) | |
| # Création de l'attaque | |
| if attack_samples > 0: | |
| envelope[:attack_samples] = np.linspace(0, 1, attack_samples) | |
| # Création du relâchement | |
| if release_samples > 0: | |
| envelope[-release_samples:] = np.linspace(1, 0, release_samples) | |
| return signal * envelope | |
| def generate_partition(length, pattern_length, pattern_repeats, note_speed_min, note_speed_max, scale): | |
| """ | |
| Génère une partition complète pour la durée totale, en respectant les patterns et répétitions. | |
| """ | |
| partition = [] | |
| current_time = 0 | |
| while current_time < length: | |
| if pattern_length > 0: | |
| # Générer un pattern | |
| pattern = [] | |
| pattern_current_time = 0 | |
| while pattern_current_time < pattern_length: | |
| interval = np.random.uniform(note_speed_min, note_speed_max) | |
| note_duration = np.random.uniform(1.5, 3.0) | |
| num_notes = np.random.choice([2, 3, 4]) | |
| selected_notes = np.random.choice(scale, num_notes, replace=False) | |
| pattern.append({ | |
| "start_time": pattern_current_time, | |
| "duration": note_duration, | |
| "notes": selected_notes.tolist(), | |
| "volume": np.random.uniform(0.7, 1.0) | |
| }) | |
| pattern_current_time += interval | |
| # Ajouter les répétitions du pattern à la partition | |
| for repeat in range(pattern_repeats): | |
| for note in pattern: | |
| start_time_global = current_time + repeat * pattern_length + note["start_time"] | |
| partition.append({ | |
| "start_time": start_time_global, | |
| "duration": note["duration"], | |
| "notes": note["notes"], | |
| "volume": note["volume"] | |
| }) | |
| current_time += pattern_length * pattern_repeats | |
| else: | |
| # Pas de pattern défini, ajouter des notes aléatoires | |
| interval = np.random.uniform(note_speed_min, note_speed_max) | |
| note_duration = np.random.uniform(1.5, 3.0) | |
| num_notes = np.random.choice([2, 3, 4]) | |
| selected_notes = np.random.choice(scale, num_notes, replace=False) | |
| partition.append({ | |
| "start_time": current_time, | |
| "duration": note_duration, | |
| "notes": selected_notes.tolist(), | |
| "volume": np.random.uniform(0.7, 1.0) | |
| }) | |
| current_time += interval | |
| return partition | |
| def synthesize_audio_with_envelope(partition, length=2.0, reverb_length=0.0, stereo_pan=0.0): | |
| """ | |
| Synthèse avec enveloppe d'amplitude pour éviter les craquements. | |
| """ | |
| sample_rate = 44100 | |
| total_length = length + reverb_length | |
| t = np.linspace(0, total_length, int(sample_rate * total_length), endpoint=False) | |
| wave = np.zeros((len(t), 2)) # Stéréo | |
| for note in partition: | |
| start_sample = int(note["start_time"] * sample_rate) | |
| end_sample = start_sample + int(note["duration"] * sample_rate) | |
| if end_sample > len(t): | |
| end_sample = len(t) | |
| chord_wave = np.zeros((end_sample - start_sample, 2)) | |
| for freq in note["notes"]: | |
| left_wave = np.sin(2 * np.pi * freq * t[: end_sample - start_sample]) | |
| right_wave = np.sin(2 * np.pi * (freq + stereo_pan) * t[: end_sample - start_sample]) | |
| chord_wave[:, 0] += left_wave | |
| chord_wave[:, 1] += right_wave | |
| chord_wave *= note["volume"] | |
| # Appliquer l'enveloppe pour éviter des transitions brusques | |
| chord_wave[:, 0] = apply_envelope(chord_wave[:, 0], sample_rate) | |
| chord_wave[:, 1] = apply_envelope(chord_wave[:, 1], sample_rate) | |
| wave[start_sample:end_sample, :] += chord_wave | |
| # Normalisation finale | |
| wave /= np.max(np.abs(wave)) | |
| return wave | |
| def generate_audio(length, reverb_length, stereo_pan, note_speed_min, note_speed_max, pattern_length, pattern_repeats, scale_name, output_format): | |
| """ | |
| Génère le fichier audio simulant un carillon dans le vent, à partir d'une partition. | |
| """ | |
| scale = scales[scale_name] | |
| partition = generate_partition(length, pattern_length, pattern_repeats, note_speed_min, note_speed_max, scale) | |
| wave = synthesize_audio_with_envelope(partition, length, reverb_length, stereo_pan) | |
| # Sauvegarde audio | |
| temp_dir = os.environ.get("TEMP", "/tmp") | |
| base_output_path = os.path.join(temp_dir, f"stereo_wind_chime.{output_format.lower()}") | |
| sf.write(base_output_path, wave, 44100, format=output_format) | |
| # Génération de la partition texte | |
| partition_text = "\n".join( | |
| [f"Start: {note['start_time']:.2f}s, Duration: {note['duration']:.2f}s, Notes: {note['notes']}" for note in partition] | |
| ) | |
| # Retourne les trois éléments requis | |
| return base_output_path, base_output_path, partition_text | |
| # Interface Gradio | |
| iface = gr.Interface( | |
| fn=generate_audio, | |
| inputs=[ | |
| gr.Number(label="Longueur du fichier audio (en secondes)", value=60), | |
| gr.Number(label="Longueur de la réverbération (en secondes)", value=6), | |
| gr.Number(label="Panorama stéréo (différence en Hz)", value=3), | |
| gr.Number(label="Rapidité des notes (min, secondes)", value=0.1), | |
| gr.Number(label="Rapidité des notes (max, secondes)", value=1), | |
| gr.Number(label="Longueur des patterns (en secondes)", value=10), | |
| gr.Number(label="Nombre de répétitions des patterns", value=3), | |
| gr.Radio(list(scales.keys()), label="Choisissez une gamme musicale", value="Occidentale (par défaut)"), | |
| gr.Radio(["FLAC", "WAV"], label="Format de sortie", value="FLAC"), | |
| ], | |
| outputs=[ | |
| gr.Audio(label="Écouter le fichier audio généré"), | |
| gr.File(label="Télécharger le fichier audio généré"), | |
| gr.Textbox(label="Partition générée", lines=20, interactive=False), | |
| ], | |
| title="Générateur de Carillon avec Patterns et Répétitions", | |
| description="Créez un carillon avec des gammes enrichies, des patterns personnalisables, et écoutez votre création.", | |
| ) | |
| iface.launch(share=True) | |
| input("appuyez sur une touche") | |