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") | |