# Parametres Papermill - JAMAIS modifier ce commentaire
# Configuration notebook
notebook_mode = "interactive" # "interactive" ou "batch"
skip_widgets = False # True pour mode batch MCP
debug_level = "INFO"
# Parametres midi-model
model_id = "skytnt/midi-model-tv2o-medium" # HuggingFace model ID
config_name = "tv2o-medium" # Config name
max_len = 512 # Nombre max d'evenements MIDI
device = "cuda" # "cuda" ou "cpu"
# Parametres de sampling
temperature = 1.0 # Temperature (0.5-1.2)
top_k = 20 # Top-K sampling
top_p = 0.98 # Nucleus sampling
# Configuration
generate_midi = True # Generer les fichiers MIDI
save_results = True # Sauvegarder les fichiers
synthesize_audio = True # Synthetiser MIDI en audio
test_prompts = True # Tester les prompts musicauxGeneration MIDI avec midi-model (SkyTNT)
Module : 02-Audio-Advanced
Niveau : Intermediaire
Technologies : midi-model (SkyTNT), FluidSynth, ~2-4 GB VRAM
Duree estimee : 45 minutes
Objectifs d’Apprentissage
Prerequis
- GPU NVIDIA avec au moins 2 GB VRAM (ou CPU, plus lent)
- FluidSynth >= 2.0 installe au niveau système
pip install torch safetensors peft transformers pyfluidsynth tqdm huggingface_hub- Connaissances de base en théorie musicale (gammes, accords) utiles mais pas indispensables
Navigation : << 02-5 | Index | 03-1 >>
# Parameters
BATCH_MODE = "true"Les paramètres Papermill definissent le modèle midi-model (skytnt/midi-model-tv2o-medium), les paramètres de sampling (temperature, top_k, top_p) et les options de generation. La cellule suivante initialise l’environnement Python et verifie la disponibilite du GPU.
# Setup environnement et imports
import os
import sys
import time
import gc
import tempfile
import subprocess
from pathlib import Path
from datetime import datetime
from typing import Dict, List, Any, Optional
import logging
import numpy as np
from IPython.display import Audio, display, HTML
# Import helpers GenAI
GENAI_ROOT = Path.cwd()
while GENAI_ROOT.name != 'GenAI' and len(GENAI_ROOT.parts) > 1:
GENAI_ROOT = GENAI_ROOT.parent
HELPERS_PATH = GENAI_ROOT / 'shared' / 'helpers'
if HELPERS_PATH.exists():
sys.path.insert(0, str(HELPERS_PATH.parent))
try:
from helpers.audio_helpers import (
play_audio, save_audio, display_audio_array
)
print("Helpers audio importes")
except ImportError:
print("Helpers audio non disponibles - mode autonome")
# Repertoires
OUTPUT_DIR = GENAI_ROOT / 'outputs' / 'audio' / 'midi'
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
# Repertoire pour le code source midi-model
MIDI_MODEL_DIR = GENAI_ROOT / 'models' / 'midi-model'
MIDI_MODEL_DIR.mkdir(parents=True, exist_ok=True)
# Configuration logging
logging.basicConfig(level=getattr(logging, debug_level))
logger = logging.getLogger('midi-generation')
# Verification GPU
gpu_available = False
try:
import torch
gpu_available = torch.cuda.is_available()
if gpu_available:
gpu_name = torch.cuda.get_device_name(0)
gpu_vram = torch.cuda.get_device_properties(0).total_memory / (1024**3)
print(f"GPU : {gpu_name} ({gpu_vram:.1f} GB VRAM)")
else:
print("GPU non disponible - generation plus lente sur CPU")
if device == "cuda":
device = "cpu"
print("Fallback vers CPU")
except ImportError:
print("torch non installe")
device = "cpu"
print(f"\nGeneration MIDI avec midi-model")
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Mode : {notebook_mode}, Device : {device}")
print(f"Modele : {model_id}")
print(f"Max events : {max_len}, Temperature : {temperature}")
print(f"Sortie : {OUTPUT_DIR.relative_to(GENAI_ROOT)}")Helpers audio importes
GPU : NVIDIA GeForce RTX 3090 (24.0 GB VRAM)
Generation MIDI avec midi-model
Date : 2026-08-20 17:33:06
Mode : interactive, Device : cuda
Modele : skytnt/midi-model-tv2o-medium
Max events : 512, Temperature : 1.0
Sortie : outputs\audio\midi
Interpretation : Configuration de l’environnement
L’environnement est correctement configure avec les éléments suivants :
| Composant | Statut | Rôle |
|---|---|---|
| GPU RTX 3090 | 24 GB VRAM | runtime machine-dep : generation rapide (5-30s vs 1-5min CPU) |
| Repertoires | Crees | Stockage des fichiers MIDI générés |
| Helpers audio | Importes | Fonctions de lecture audio |
| Device | cuda | Utilisation du GPU pour l’inference |
Verification GPU : La presence d’un GPU avec suffisamment de VRAM est critique pour la generation MIDI. Le modèle medium (~100M paramètres) necessite environ 0.5-2 GB de VRAM en bfloat16, bien moins que MusicGen (10-20 GB).
L’environnement Python est initialise et les repertoires de sortie sont crees. La cellule suivante charge le fichier .env pour recuperer le token HuggingFace, utile pour le telechargement des poids de skytnt/midi-model.
# Chargement robuste de la configuration .env
from dotenv import load_dotenv
import os
current_path = Path.cwd()
env_loaded = False
for _ in range(10):
env_path = current_path / ".env"
if env_path.exists():
load_dotenv(env_path)
print(f".env charge depuis: {env_path.name}")
env_loaded = True
break
if current_path.name == "GenAI" or len(current_path.parts) <= 1:
break
current_path = current_path.parent
if not env_loaded:
print("WARNING: .env non trouve, utilisation variables environnement")
# HuggingFace token (optionnel, modele public)
hf_token = os.environ.get("HUGGINGFACE_TOKEN") or os.environ.get("HF_TOKEN")
if hf_token:
print("Token HuggingFace disponible")
else:
print("Token HuggingFace non disponible (telechargement public uniquement)").env charge depuis: .env
Token HuggingFace disponible
Interpretation : Chargement .env
Le fichier .env n’a pas ete trouve dans ce repertoire. Les variables d’environnement sont utilisees en fallback. Pour le telechargement depuis HuggingFace :
| Scénario | Token requis | Notes |
|---|---|---|
| modèle public midi-model | Non | Le modèle est accessible sans authentification |
| modèles prives | Oui | Necessite HUGGINGFACE_TOKEN ou HF_TOKEN |
| Rate limits | Oui | Token recommande pour eviter les limitations |
Prochaine étape : Telechargement des fichiers sources du modèle depuis le depot HuggingFace skytnt/midi-model-tv2o-medium.
Dependances GPU Optionnelles
Ce notebook utilise un modèle Transformer leger (~200 MB). runtime machine-dep : GPU recommande pour la vitesse, mais le CPU fonctionne. FluidSynth est requis au niveau système pour la synthese audio des fichiers MIDI.
Section 1 : Generation symbolique vs generation audio
Il existe deux approches fondamentalement différentes pour générer de la musique par IA :
| Aspect | Generation symbolique (MIDI) | Generation audio (MusicGen) |
|---|---|---|
| Sortie | Notes, durees, instruments | Signal audio brut |
| Editabilite | Totale (notes individuelles) | Aucune (forme d’onde) |
| Taille | Quelques KB (.mid) | Plusieurs MB (.wav) |
| Multi-instruments | Natif (16 canaux MIDI) | Melange dans le signal |
| Qualite sonore | Depend du synthetiseur | Directement realiste |
| VRAM | ~2-4 GB | ~10-20 GB |
Architecture de midi-model
midi-model utilise une architecture dual-transformer basee sur LLaMA :
| Composant | Rôle | Taille (medium) |
|---|---|---|
| net (Main) | Traite la sequence d’événements MIDI | 12 couches, 16 tetes, 1024 hidden |
| net_token (Token) | généré les tokens individuels par événement | 3 couches, 4 tetes, 1024 hidden |
| Tokenizer V2 | Encode/decode les événements MIDI | Vocabulaire : 3406 tokens |
Le tokenizer V2 represente chaque événement MIDI par jusqu’a 8 tokens : type, temps, piste, canal, pitch, velocity, duree.
# Installation et telechargement du modele
print("INSTALLATION ET CHARGEMENT")
print("=" * 45)
# Verifier les dependances
missing_deps = []
for pkg_name, import_name in [("safetensors", "safetensors"), ("peft", "peft"),
("transformers", "transformers"), ("tqdm", "tqdm"),
("huggingface_hub", "huggingface_hub")]:
try:
__import__(import_name)
except ImportError:
missing_deps.append(pkg_name)
if missing_deps:
print(f"Dependances manquantes : {', '.join(missing_deps)}")
print(f"Installation : pip install {' '.join(missing_deps)}")
else:
print("Toutes les dependances Python sont installees")
# Verifier FluidSynth
fluidsynth_available = False
try:
import fluidsynth
fluidsynth_available = True
print("pyfluidsynth disponible")
except ImportError:
print("pyfluidsynth non installe (pip install pyfluidsynth)")
print("FluidSynth systeme requis egalement")
# Telecharger les fichiers source du modele.
# NB : les sources Python (midi_model.py, midi_tokenizer.py, ...) NE SONT PAS dans le
# repo HuggingFace du modele (qui ne contient que les weights model.safetensors).
# Elles vivent dans le depot source GitHub SkyTNT/midi-model.
from huggingface_hub import hf_hub_download, snapshot_download
import urllib.request
source_files = ["midi_model.py", "midi_tokenizer.py", "midi_synthesizer.py", "MIDI.py"]
GITHUB_SOURCES = "https://raw.githubusercontent.com/SkyTNT/midi-model/main/"
model_files_ready = True
print(f"\nTelechargement des sources midi-model...")
for fname in source_files:
target = MIDI_MODEL_DIR / fname
if not target.exists():
try:
urllib.request.urlretrieve(GITHUB_SOURCES + fname, str(target))
print(f" Telecharge : {fname}")
except Exception as e:
print(f" Erreur {fname} : {str(e)[:80]}")
model_files_ready = False
else:
print(f" Deja present : {fname}")
# Telecharger le soundfont pour la synthese
soundfont_path = None
if synthesize_audio:
sf_target = MIDI_MODEL_DIR / "soundfont.sf2"
if not sf_target.exists():
try:
soundfont_path = hf_hub_download(
repo_id="skytnt/midi-model",
filename="soundfont.sf2",
local_dir=str(MIDI_MODEL_DIR),
token=hf_token
)
print(f" Telecharge : soundfont.sf2")
except Exception as e:
print(f" Erreur soundfont : {str(e)[:80]}")
else:
soundfont_path = str(sf_target)
print(f" Deja present : soundfont.sf2")
if model_files_ready:
print(f"\nFichiers source prets dans : {MIDI_MODEL_DIR.relative_to(GENAI_ROOT)}")INSTALLATION ET CHARGEMENT
=============================================
Toutes les dependances Python sont installees
pyfluidsynth disponible
Telechargement des sources midi-model...
Telecharge : midi_model.py
Telecharge : midi_tokenizer.py
Telecharge : midi_synthesizer.py
Telecharge : MIDI.py
Telecharge : soundfont.sf2
Fichiers source prets dans : models\midi-model
Cette section prepare l’environnement pour midi-model en verifiant les dependances et en telechargeant les fichiers sources necessaires. Le modèle midi-model est distribue sous forme de fichiers Python source plutot que de package pip, necessitant un telechargement manuel depuis HuggingFace.
Dependances critiques : - safetensors : Chargement securise des poids du modèle - peft : Parameter-Efficient Fine-Tuning (optionnel pour LoRA) - pyfluidsynth : Synthese audio MIDI (optionnel) - transformers : Infrastructure Transformer (optionnel) - FluidSynth : Synthetiseur système (requis pour audio)
Les dependances Python (peft, pyfluidsynth) et les fichiers sources du depot midi-model sont telecharges. La cellule suivante charge le modèle en memoire et initialise le tokenizer V2 (vocabulaire de 3406 tokens).
# Chargement du modele midi-model
print("CHARGEMENT DU MODELE")
print("=" * 45)
midi_model_loaded = False
if model_files_ready:
# Ajouter le repertoire au path
if str(MIDI_MODEL_DIR) not in sys.path:
sys.path.insert(0, str(MIDI_MODEL_DIR))
try:
from midi_model import MIDIModel, MIDIModelConfig
from midi_tokenizer import MIDITokenizerV2
import MIDI as MIDI_module
# Charger la configuration
print(f"Configuration : {config_name}")
config = MIDIModelConfig.from_name(config_name)
# Creer le modele
model = MIDIModel(config=config)
tokenizer = model.tokenizer
# Telecharger et charger les poids
print(f"Telechargement des poids depuis {model_id}...")
from safetensors.torch import load_file
weights_path = MIDI_MODEL_DIR / "model.safetensors"
if not weights_path.exists():
weights_path = hf_hub_download(
repo_id=model_id,
filename="model.safetensors",
local_dir=str(MIDI_MODEL_DIR),
token=hf_token
)
state_dict = load_file(str(weights_path))
model.load_state_dict(state_dict, strict=False)
# Choisir dtype et device
dtype = torch.bfloat16 if device == "cuda" and gpu_available else torch.float32
model = model.to(device, dtype=dtype).eval()
midi_model_loaded = True
param_count = sum(p.numel() for p in model.parameters()) / 1e6
print(f"\nModele charge : {param_count:.0f}M parametres")
print(f"Device : {device}, Dtype : {dtype}")
print(f"Vocabulaire tokenizer : {tokenizer.vocab_size} tokens")
print(f"Max token seq par evenement : {tokenizer.max_token_seq}")
if gpu_available:
vram_used = torch.cuda.memory_allocated(0) / (1024**3)
print(f"VRAM utilisee : {vram_used:.2f} GB")
except ImportError as e:
print(f"Import manquant : {e}")
print("Verifier que les fichiers source sont telecharges")
except Exception as e:
print(f"Erreur lors du chargement : {type(e).__name__} - {str(e)[:200]}")
else:
print("Fichiers source non disponibles")CHARGEMENT DU MODELE
=============================================
Configuration : tv2o-medium
Telechargement des poids depuis skytnt/midi-model-tv2o-medium...
Modele charge : 234M parametres
Device : cuda, Dtype : torch.bfloat16
Vocabulaire tokenizer : 3406 tokens
Max token seq par evenement : 8
VRAM utilisee : 0.44 GB
Interpretation : Chargement du modèle
| Aspect | Valeur typique | Signification |
|---|---|---|
| paramètres | ~100M (medium) | Beaucoup plus leger que MusicGen (300M-3.3B) |
| VRAM | ~0.5-2 GB | Accessible sur la plupart des GPU |
| Vocabulaire | 3406 tokens | Couvre tous les événements MIDI standard |
Le modèle medium offre runtime machine-dep : equilibre entre qualite et rapidite. La variante “o” (optimised) reorganise les pistes et canaux MIDI pour de meilleurs résultats.
Section 2 : Generation inconditionnelle
La generation inconditionnelle laisse le modèle composer librement sans contrainte. Le modèle choisit le tempo, les instruments, la tonalite et le style.
| paramètre | Valeur | Rôle |
|---|---|---|
max_len |
512 | Nombre max d’événements MIDI a générer |
temp |
1.0 | Diversite du sampling |
top_k |
20 | Filtre les k tokens les plus probables |
top_p |
0.98 | Nucleus sampling |
# Fonctions utilitaires pour la generation et la synthese
def generate_and_save(model, tokenizer, prompt=None, max_len=512,
temp=1.0, top_k=20, top_p=0.98,
filename="output", output_dir=OUTPUT_DIR):
"""Genere du MIDI, sauvegarde le fichier .mid et retourne le score."""
start_time = time.time()
midi_seq = model.generate(
prompt=prompt,
batch_size=1,
max_len=max_len,
temp=temp,
top_p=top_p,
top_k=top_k
)
gen_time = time.time() - start_time
num_events = midi_seq.shape[1]
# Detokeniser en score MIDI
mid_score = tokenizer.detokenize(midi_seq[0])
# Sauvegarder en fichier .mid
midi_bytes = MIDI_module.score2midi(mid_score)
mid_path = output_dir / f"{filename}.mid"
with open(str(mid_path), "wb") as f:
f.write(midi_bytes)
# Extraire les informations
ticks_per_beat = mid_score[0] if mid_score else 480
num_tracks = len(mid_score) - 1 if len(mid_score) > 1 else 0
result = {
"path": mid_path,
"score": mid_score,
"num_events": num_events,
"num_tracks": num_tracks,
"gen_time": gen_time,
"ticks_per_beat": ticks_per_beat,
"file_size": mid_path.stat().st_size
}
print(f" Evenements : {num_events} | Pistes : {num_tracks}")
print(f" Temps : {gen_time:.2f}s | Fichier : {mid_path.name} ({result['file_size']} octets)")
return result
def synthesize_midi(mid_score, soundfont_path, sample_rate=44100):
"""Synthetise un score MIDI en audio via FluidSynth."""
if not fluidsynth_available or soundfont_path is None:
print(" Synthese non disponible (FluidSynth ou soundfont manquant)")
return None
try:
from midi_synthesizer import MidiSynthesizer
synth = MidiSynthesizer(soundfont_path, sample_rate=sample_rate)
mid_opus = MIDI_module.score2opus(mid_score)
audio_data = synth.synthesis(mid_opus)
return audio_data # numpy int16 array, shape (samples, 2)
except Exception as e:
print(f" Erreur synthese : {type(e).__name__} - {str(e)[:100]}")
return None
def play_midi(mid_score, soundfont_path, sample_rate=44100, preview_seconds=3):
"""Synthetise et joue le MIDI dans le notebook (apercu ~3s, audio complet sur disque)."""
audio_data = synthesize_midi(mid_score, soundfont_path, sample_rate)
if audio_data is not None:
# Apercu court embarque (~3s) pour limiter la taille du notebook ; l'audio
# complet reste sauvegarde sur disque par l'appelant. audio_data est shape
# (samples, channels) -> on tronque l'axe 0 (temps), PAS les canaux.
preview = audio_data[:sample_rate * preview_seconds]
audio_float = preview.astype(np.float32) / 32768.0
display_audio_array(audio_float.T, sample_rate)
return audio_data
return None
print("Fonctions utilitaires definies")Fonctions utilitaires definies
Cette section définit les fonctions utilitaires pour la generation MIDI et la synthese audio. Trois fonctions principales sont implementees :
| Fonction | Rôle | paramètres cles |
|---|---|---|
generate_and_save() |
généré une sequence MIDI et sauvegarde le fichier | prompt, max_len, temp, top_k, top_p |
synthesize_midi() |
Convertit un score MIDI en audio via FluidSynth | mid_score, soundfont_path |
play_midi() |
Synthetise et affiche un lecteur audio dans le notebook | mid_score, soundfont_path |
Le processus complet est : 1. Generation : Le modèle créé des tokens MIDI 2. Detokenization : Conversion des tokens en score MIDI 3. Serialisation : Export en fichier .mid 4. Synthese : Conversion audio via FluidSynth (optionnel)
Les fonctions de generation MIDI et de synthese audio FluidSynth sont définies. La cellule suivante lance la generation inconditionnelle : le modèle compose 3 morceaux sans contrainte de style ou d’instrument.
# Generation inconditionnelle
print("GENERATION INCONDITIONNELLE")
print("=" * 45)
uncond_results = {}
if midi_model_loaded and generate_midi:
for i in range(3):
print(f"\n--- Morceau {i+1} ---")
result = generate_and_save(
model, tokenizer,
prompt=None,
max_len=max_len,
temp=temperature,
top_k=top_k,
top_p=top_p,
filename=f"uncond_{i+1}"
)
uncond_results[f"morceau_{i+1}"] = result
# Synthese audio
if synthesize_audio:
audio = play_midi(result["score"], soundfont_path)
if audio is not None and save_results:
import scipy.io.wavfile
wav_path = OUTPUT_DIR / f"uncond_{i+1}.wav"
scipy.io.wavfile.write(str(wav_path), 44100, audio)
print(f" Audio sauvegarde : {wav_path.name}")
# Recapitulatif
print(f"\nRecapitulatif :")
print(f"{'Morceau':<12} {'Events':<10} {'Pistes':<10} {'Temps (s)':<12} {'Taille (B)':<12}")
print("-" * 56)
for name, data in uncond_results.items():
print(f"{name:<12} {data['num_events']:<10} {data['num_tracks']:<10} "
f"{data['gen_time']:<12.2f} {data['file_size']:<12}")
else:
print("Modele non charge ou generation desactivee")GENERATION INCONDITIONNELLE
=============================================
--- Morceau 1 ---
Evenements : 512 | Pistes : 9
Temps : 24.01s | Fichier : uncond_1.mid (3755 octets)
Audio sauvegarde : uncond_1.wav
--- Morceau 2 ---
Evenements : 421 | Pistes : 6
Temps : 20.69s | Fichier : uncond_2.mid (3728 octets)
Audio sauvegarde : uncond_2.wav
--- Morceau 3 ---
Evenements : 512 | Pistes : 14
Temps : 23.42s | Fichier : uncond_3.mid (4274 octets)
Audio sauvegarde : uncond_3.wav
Recapitulatif :
Morceau Events Pistes Temps (s) Taille (B)
--------------------------------------------------------
morceau_1 512 9 24.01 3755
morceau_2 421 6 20.69 3728
morceau_3 512 14 23.42 4274
Interpretation : Generation inconditionnelle
| Aspect | Valeur typique | Signification |
|---|---|---|
| Événements | 200-512 | Nombre de notes/changements générés |
| Pistes | 2-8 | Le modèle attribue automatiquement les instruments |
| Temps | runtime machine-dep : 5-30s (GPU) | Beaucoup plus rapide que MusicGen |
| Taille .mid | 1-10 KB | Extremement compact vs audio |
Points cles : 1. Chaque generation est unique et non-déterministe 2. Le modèle choisit librement les instruments, le tempo et la structure 3. Les fichiers MIDI sont editables dans n’importe quel DAW (FL Studio, Ableton, MuseScore)
Exercice 1 : Construction d’un prompt musical personnalise
Objectif : Créer un prompt MIDI qui contraint le modèle a générer dans un style musical précis, en utilisant les fonctions create_music_prompt() et le dictionnaire INSTRUMENTS.
Le dictionnaire INSTRUMENTS contient les mappings nom -> numéro de patch MIDI (ex: "Violin": 40, "Trumpet": 56). La fonction create_music_prompt() accepte les paramètres tempo, time_sig, key, et instruments.
Indices : - Le format instruments est une liste de tuples (canal, patch_number) - Les canaux MIDI vont de 0 a 15 (maximum 16 instruments simultanes) - La tonalite est un tuple (note, mode) avec mode = "major" ou "minor" - # étape 1 : Choisir un style musical (ex: bossa nova, metal, baroque) - # étape 2 : Sélectionner 2-4 instruments coherents avec ce style - # étape 3 : Construire le prompt avec tempo et tonalite adaptes - # étape 4 : générer et analyser si le résultat correspond au style vise - # Indice : La bossa nova utilise typiquement guitare nylon (24) + flute (73), tempo ~130
# Exercice 3 : Construction d'un prompt musical personnalise
# TODO etudiant : Creer un prompt musical pour un style de votre choix
def create_custom_style_prompt(tokenizer, style_name):
"""
Cree un prompt MIDI adapte a un style musical donne.
Args:
tokenizer: Le tokenizer MIDI
style_name: Nom du style (ex: "bossa_nova", "baroque", "jazz_fusion")
Returns:
numpy array: Le prompt MIDI pret pour la generation
"""
# TODO etudiant : Definir les parametres musicaux selon le style
tempo = None # TODO etudiant : choisir un tempo adapte
time_sig = None # TODO etudiant : choisir la signature (ex: (4, 4) ou (3, 4))
key = None # TODO etudiant : choisir la tonalite (ex: ("G", "major"))
instruments = None # TODO etudiant : choisir 2-4 instruments (canal, patch)
# Indice : Utilisez le dictionnaire INSTRUMENTS pour les numeros de patch
# Exemple : INSTRUMENTS["Violin"] = 40
# TODO etudiant : Appeler create_music_prompt() avec vos parametres
prompt = None # TODO etudiant
return prompt
# Test (decommenter quand le modele est charge)
# prompt = create_custom_style_prompt(tokenizer, "bossa_nova")
# if prompt is not None and midi_model_loaded:
# result = generate_and_save(model, tokenizer, prompt=prompt, filename="custom_style")
print("Exercice a completer")Exercice a completer
Exercice 2 : Exploration des paramètres de generation MIDI
Objectif : Analyser l’impact du paramètre max_len (nombre d’événements MIDI) sur la duree, la complexite et la coherence musicale d’une generation.
Le paramètre max_len contrôle directement le nombre d’événements MIDI générés. Un nombre faible (128) produit un court fragment, tandis qu’un nombre élevé (1024) généré un morceau complet mais potentiellement moins coherent. L’objectif est de trouver le point d’equilibre entre duree suffisante et coherence musicale.
Indices : - Chaque événement MIDI correspond environ a 0.05-0.2 seconde selon le tempo - Avec max_len=128 : on obtient généralement 5-15 secondes de musique - Avec max_len=512 : on obtient généralement 20-60 secondes - Avec max_len=1024 : le morceau peut durer 1-3 minutes mais des repetitions apparaissent - # étape 1 : Fixer un prompt musical simple (piano solo, 120 BPM) - # étape 2 : générer avec max_len = 128, 256, 512 et 1024 - # étape 3 : Pour chaque generation, noter le nombre de pistes et la taille du fichier - # étape 4 : Identifier le max_len optimal pour un morceau coherent de 30-60s - # Indice : generate_and_save() retourne un dict avec num_events, num_tracks, gen_time
# Exercice 4 : Exploration des parametres de generation MIDI
# TODO etudiant : Analyser l'impact de max_len sur la generation
def analyze_max_len_impact(model, tokenizer, prompt=None, lengths=None):
"""
Genere des morceaux MIDI avec differentes valeurs de max_len
et analyse l'impact sur la qualite.
Args:
model: Modele midi-model charge
tokenizer: Tokenizer MIDI
prompt: Prompt musical (si None, generation inconditionnelle)
lengths: Liste de valeurs max_len a tester
Returns:
dict: Resultats comparatifs par valeur de max_len
"""
if lengths is None:
lengths = [] # TODO etudiant : [128, 256, 512, 1024]
results = {}
# TODO etudiant : Pour chaque valeur de max_len :
# 1. Generer avec generate_and_save()
# 2. Collecter : nombre d'evenements, pistes, temps, taille fichier
# 3. Estimer la duree musicale approximative
# TODO etudiant : Identifier le max_len optimal
optimal_length = None # TODO etudiant
return results
# Test (decommenter quand le modele est charge)
# results = analyze_max_len_impact(model, tokenizer)
print("Exercice a completer")Exercice a completer
Section 3 : Prompts musicaux
Contrairement a MusicGen qui utilise des descriptions textuelles, midi-model accepte des prompts symboliques : des sequences d’événements MIDI qui definissent le contexte musical.
On peut specifier :
| Élément | méthode | Exemple |
|---|---|---|
| Tempo | set_tempo |
120 BPM |
| Signature | time_signature |
4/4, 3/4, 6/8 |
| Tonalite | key_signature |
Do majeur, La mineur |
| Instruments | patch_change |
Piano (0), Guitare (25), Violon (40) |
# Prompts musicaux - Configuration d'instruments et de tempo
print("GENERATION AVEC PROMPTS MUSICAUX")
print("=" * 45)
# Instruments MIDI General (numeros de patch)
INSTRUMENTS = {
"Acoustic Grand Piano": 0,
"Electric Piano": 4,
"Nylon Guitar": 24,
"Electric Guitar": 27,
"Acoustic Bass": 32,
"Electric Bass": 33,
"Violin": 40,
"Cello": 42,
"String Ensemble": 48,
"Trumpet": 56,
"Alto Sax": 65,
"Flute": 73,
"Synth Lead": 80,
"Synth Pad": 88,
}
def create_music_prompt(tokenizer, tempo=120, time_sig=(4, 4),
key=("C", "major"), instruments=None):
"""Cree un prompt MIDI avec tempo, signature et instruments."""
mid = [[tokenizer.bos_id] + [tokenizer.pad_id] * (tokenizer.max_token_seq - 1)]
# Tempo
mid.append(tokenizer.event2tokens(["set_tempo", 0, 0, 0, tempo]))
# Signature temporelle : nn = numerator-1, dd = denominator_power-1
nn = time_sig[0] - 1
dd = {1: 0, 2: 0, 4: 1, 8: 2, 16: 3}.get(time_sig[1], 1)
mid.append(tokenizer.event2tokens(["time_signature", 0, 0, 0, nn, dd]))
# Tonalite : sf+7 (sf va de -7 a +7), mi (0=majeur, 1=mineur)
key_map = {"C": 0, "G": 1, "D": 2, "A": 3, "E": 4, "B": 5,
"F#": 6, "Gb": -6, "F": -1, "Bb": -2, "Eb": -3,
"Ab": -4, "Db": -5, "Cb": -7}
sf = key_map.get(key[0], 0) + 7
mi = 0 if key[1] == "major" else 1
mid.append(tokenizer.event2tokens(["key_signature", 0, 0, 0, sf, mi]))
# Instruments
if instruments:
for track_idx, (channel, patch) in enumerate(instruments, start=1):
mid.append(tokenizer.event2tokens(
["patch_change", 0, 0, track_idx, channel, patch]
))
return np.array([mid], dtype=np.int64)
# Definir des presets musicaux
presets = {
"Piano classique": {
"tempo": 100,
"time_sig": (4, 4),
"key": ("C", "major"),
"instruments": [(0, 0)] # Piano sur canal 0
},
"Jazz quartet": {
"tempo": 130,
"time_sig": (4, 4),
"key": ("Bb", "major"),
"instruments": [(0, 0), (1, 32), (2, 65), (3, 0)] # Piano, Bass, Sax, Piano
},
"Valse romantique": {
"tempo": 160,
"time_sig": (3, 4),
"key": ("D", "major"),
"instruments": [(0, 0), (1, 40), (2, 42)] # Piano, Violon, Cello
},
"Synth ambient": {
"tempo": 80,
"time_sig": (4, 4),
"key": ("A", "minor"),
"instruments": [(0, 88), (1, 80), (2, 48)] # Pad, Lead, Strings
},
}
prompt_results = {}
if midi_model_loaded and generate_midi and test_prompts:
for name, params in presets.items():
print(f"\n--- {name} ---")
print(f" Tempo: {params['tempo']} BPM | Signature: {params['time_sig'][0]}/{params['time_sig'][1]}")
print(f" Tonalite: {params['key'][0]} {params['key'][1]}")
prompt = create_music_prompt(
tokenizer,
tempo=params["tempo"],
time_sig=params["time_sig"],
key=params["key"],
instruments=params["instruments"]
)
safe_name = name.lower().replace(" ", "_")
result = generate_and_save(
model, tokenizer,
prompt=prompt,
max_len=max_len,
temp=temperature,
top_k=top_k,
top_p=top_p,
filename=f"prompt_{safe_name}"
)
prompt_results[name] = result
if synthesize_audio:
audio = play_midi(result["score"], soundfont_path)
if audio is not None and save_results:
import scipy.io.wavfile
wav_path = OUTPUT_DIR / f"prompt_{safe_name}.wav"
scipy.io.wavfile.write(str(wav_path), 44100, audio)
# Recapitulatif
print(f"\nRecapitulatif des presets :")
print(f"{'Preset':<22} {'Events':<10} {'Pistes':<10} {'Temps (s)':<12}")
print("-" * 54)
for name, data in prompt_results.items():
print(f"{name:<22} {data['num_events']:<10} {data['num_tracks']:<10} "
f"{data['gen_time']:<12.2f}")
else:
print("Modele non charge, generation desactivee, ou prompts desactives")GENERATION AVEC PROMPTS MUSICAUX
=============================================
--- Piano classique ---
Tempo: 100 BPM | Signature: 4/4
Tonalite: C major
Evenements : 512 | Pistes : 2
Temps : 22.33s | Fichier : prompt_piano_classique.mid (3652 octets)
--- Jazz quartet ---
Tempo: 130 BPM | Signature: 4/4
Tonalite: Bb major
Evenements : 512 | Pistes : 10
Temps : 22.17s | Fichier : prompt_jazz_quartet.mid (4028 octets)
--- Valse romantique ---
Tempo: 160 BPM | Signature: 3/4
Tonalite: D major
Evenements : 512 | Pistes : 6
Temps : 22.91s | Fichier : prompt_valse_romantique.mid (4652 octets)
--- Synth ambient ---
Tempo: 80 BPM | Signature: 4/4
Tonalite: A minor
Evenements : 453 | Pistes : 5
Temps : 19.60s | Fichier : prompt_synth_ambient.mid (3510 octets)
Recapitulatif des presets :
Preset Events Pistes Temps (s)
------------------------------------------------------
Piano classique 512 2 22.33
Jazz quartet 512 10 22.17
Valse romantique 512 6 22.91
Synth ambient 453 5 19.60
Interpretation : Prompts musicaux
| Preset | Observation attendue |
|---|---|
| Piano classique | Piece solo, tonalite claire |
| Jazz quartet | Polyphonie riche, rythme swing |
| Valse romantique | Rythme ternaire 3/4, expressif |
| Synth ambient | Textures longues, atmospherique |
Points cles : 1. Le prompt définit le contexte initial, pas le contenu exact 2. Le modèle peut ajouter des instruments supplementaires au-dela du prompt 3. Le tempo et la tonalite sont généralement respectes 4. La signature temporelle influence fortement le ressenti rhythmique
Section 4 : Paramètres de sampling
Comme pour les LLM textuels, les paramètres de sampling controlent la diversite et la qualite de la generation.
| paramètre | Valeur par defaut | Plage | Impact |
|---|---|---|---|
temp |
1.0 | 0.1-1.2 | Diversite globale |
top_k |
20 | 1-128 | Filtre les tokens improbables |
top_p |
0.98 | 0.1-1.0 | Nucleus sampling |
Presets recommandes
| Style | temp | top_k | top_p | Résultat |
|---|---|---|---|---|
| Conservateur | 1.0 | 128 | 0.94 | Previsible, classique |
| Equilibre | 1.0 | 20 | 0.98 | Bon compromis |
| Creatif | 1.0 | 12 | 0.98 | Surprenant, varie |
# Test des parametres de sampling
print("TEST DES PARAMETRES DE SAMPLING")
print("=" * 45)
sampling_configs = {
"Conservateur": {"temp": 0.8, "top_k": 128, "top_p": 0.94},
"Equilibre": {"temp": 1.0, "top_k": 20, "top_p": 0.98},
"Creatif": {"temp": 1.1, "top_k": 12, "top_p": 0.98},
}
sampling_results = {}
if midi_model_loaded and generate_midi:
# Prompt commun : piano solo
common_prompt = create_music_prompt(
tokenizer, tempo=110, time_sig=(4, 4),
key=("C", "major"), instruments=[(0, 0)]
)
for name, params in sampling_configs.items():
print(f"\n--- {name} (temp={params['temp']}, top_k={params['top_k']}, top_p={params['top_p']}) ---")
safe_name = name.lower()
result = generate_and_save(
model, tokenizer,
prompt=common_prompt,
max_len=max_len,
temp=params["temp"],
top_k=params["top_k"],
top_p=params["top_p"],
filename=f"sampling_{safe_name}"
)
sampling_results[name] = result
if synthesize_audio:
play_midi(result["score"], soundfont_path)
# Recapitulatif
print(f"\nComparaison :")
print(f"{'Config':<16} {'temp':<8} {'top_k':<8} {'top_p':<8} {'Events':<10} {'Pistes':<10}")
print("-" * 60)
for name, data in sampling_results.items():
cfg = sampling_configs[name]
print(f"{name:<16} {cfg['temp']:<8} {cfg['top_k']:<8} {cfg['top_p']:<8} "
f"{data['num_events']:<10} {data['num_tracks']:<10}")
else:
print("Modele non charge ou generation desactivee")TEST DES PARAMETRES DE SAMPLING
=============================================
--- Conservateur (temp=0.8, top_k=128, top_p=0.94) ---
Evenements : 512 | Pistes : 6
Temps : 23.26s | Fichier : sampling_conservateur.mid (4071 octets)
--- Equilibre (temp=1.0, top_k=20, top_p=0.98) ---
Evenements : 512 | Pistes : 2
Temps : 22.03s | Fichier : sampling_equilibre.mid (3661 octets)
--- Creatif (temp=1.1, top_k=12, top_p=0.98) ---
Evenements : 512 | Pistes : 3
Temps : 22.72s | Fichier : sampling_creatif.mid (3578 octets)
Comparaison :
Config temp top_k top_p Events Pistes
------------------------------------------------------------
Conservateur 0.8 128 0.94 512 6
Equilibre 1.0 20 0.98 512 2
Creatif 1.1 12 0.98 512 3
Interpretation : Paramètres de sampling
| Configuration | Observation |
|---|---|
| Conservateur | Melodie previsible, accords standards, peu de surprises |
| Equilibre | Bon compromis entre coherence et originalite |
| Creatif | Harmonies inattendues, rythmes varies, parfois incoherent |
Points cles : 1. Contrairement aux LLM textuels, la temperature n’a pas besoin d’etre très basse pour des résultats coherents 2. Le top_k a plus d’impact que la temperature sur la diversite instrumentale 3. Un top_p proche de 1.0 permet plus de diversite harmonique
Exercice 3 : Comparaison de stratégies de sampling
Objectif : Comparer l’impact des paramètres de sampling (temp, top_k, top_p) sur la qualite musicale et la coherence d’une generation MIDI.
On dispose de 3 presets de sampling (Conservateur, Equilibre, Creatif). L’objectif est de générer un morceau avec chacun, puis d’analyser les différences observables dans les fichiers MIDI produits.
Indices : - top_k filtre les k tokens les plus probables : une valeur basse limite la diversite - La temperature contrôle la “platitude” de la distribution de probabilite - Pour comparer objectivement, utilisez le même seed et le même prompt musical - # étape 1 : Définir un prompt musical simple (piano solo, 100 BPM) - # étape 2 : générer 3 morceaux avec les 3 presets de sampling - # étape 3 : Comparer le nombre d’événements, le nombre de pistes, la taille du fichier - # Indice : Un morceau “creatif” aura généralement plus de pistes et d’événements inattendus
# Exercice 2 : Comparaison de strategies de sampling
# TODO etudiant : Implementer la comparaison des 3 presets de sampling
def compare_sampling_strategies(model, tokenizer, base_prompt=None):
"""
Genere 3 morceaux MIDI avec des parametres de sampling differents
et retourne un tableau comparatif.
Args:
model: Le modele midi-model charge
tokenizer: Le tokenizer MIDI
base_prompt: Prompt musical commun (si None, generation inconditionnelle)
Returns:
dict: Resultats comparatifs pour chaque preset
"""
# TODO etudiant : Definir les 3 presets de sampling
presets = None # TODO etudiant : dictionnaire avec 3 configurations
# TODO etudiant : Generer un morceau pour chaque preset
# Indice : Utilisez generate_and_save() avec les parametres de chaque preset
# TODO etudiant : Collecter les metriques (events, pistes, temps, taille)
# Indice : Chaque resultat de generate_and_save() contient ces informations
results = {} # TODO etudiant : remplir avec les resultats
return results
# Test (decommenter quand le modele est charge)
# results = compare_sampling_strategies(model, tokenizer)
print("Exercice a completer")Exercice a completer
Section 5 : Continuation de MIDI existant
midi-model peut continuer une sequence MIDI existante. On tokenise un fichier MIDI, on en prend les premiers événements comme prompt, et le modèle généré la suite.
| étape | Description |
|---|---|
| 1. Charger | Lire le fichier .mid |
| 2. Tokeniser | Convertir en tokens via MIDITokenizerV2 |
| 3. Tronquer | Garder les N premiers événements comme contexte |
| 4. générer | Le modèle complete la sequence |
# Continuation d'un MIDI genere precedemment
print("CONTINUATION MIDI")
print("=" * 45)
if midi_model_loaded and generate_midi and uncond_results:
# Utiliser le premier morceau genere comme base
source = uncond_results.get("morceau_1")
if source and source["path"].exists():
print(f"Source : {source['path'].name}")
# Lire le fichier MIDI
with open(str(source["path"]), "rb") as f:
midi_data = f.read()
# Tokeniser
midi_score = MIDI_module.midi2score(midi_data)
tokens = tokenizer.tokenize(
midi_score,
cc_eps=4,
tempo_eps=4,
remap_track_channel=True,
add_default_instr=True,
remove_empty_channels=False
)
# Prendre les 128 premiers evenements comme prompt
prompt_len = min(128, len(tokens))
prompt = np.array([tokens[:prompt_len]], dtype=np.int64)
print(f"Prompt : {prompt_len} evenements (sur {len(tokens)} total)")
# Generer la suite
print(f"\nGeneration de la continuation...")
result = generate_and_save(
model, tokenizer,
prompt=prompt,
max_len=max_len,
temp=temperature,
top_k=top_k,
top_p=top_p,
filename="continuation"
)
if synthesize_audio:
print("\nOriginal :")
play_midi(source["score"], soundfont_path)
print("\nContinuation :")
play_midi(result["score"], soundfont_path)
else:
print("Pas de morceau source disponible")
else:
print("Modele non charge, generation desactivee, ou pas de morceaux precedents")CONTINUATION MIDI
=============================================
Source : uncond_1.mid
Prompt : 128 evenements (sur 508 total)
Generation de la continuation...
Evenements : 512 | Pistes : 9
Temps : 18.52s | Fichier : continuation.mid (3636 octets)
Original :
Continuation :
Interpretation : Continuation
| Aspect | Observation |
|---|---|
| Coherence stylistique | La continuation conserve le style du morceau source |
| Instruments | Les mêmes instruments sont généralement maintenus |
| Transitions | Parfois abruptes, surtout avec peu de contexte |
Points cles : 1. Plus le prompt est long, plus la continuation est coherente 2. La fenêtre de contexte est limitee a 4096 événements 3. On peut utiliser un fichier MIDI externe (composition personnelle, partition)
Section 6 : Comparaison MIDI vs Audio (MusicGen)
Recapitulatif comparatif entre l’approche symbolique (ce notebook) et l’approche audio directe (notebook 02-3).
| critère | midi-model (MIDI) | MusicGen (Audio) |
|---|---|---|
| Entree | Prompt symbolique (instruments, tempo) | Description textuelle naturelle |
| Sortie | Fichier .mid (notes, durees) | Fichier .wav (signal audio) |
| Editabilite | Totale (note par note) | Aucune |
| Multi-instruments | Natif (16 canaux MIDI) | Melange dans le signal |
| Qualite sonore | Depend du synthetiseur (SoundFont) | Directement realiste |
| VRAM | ~2-4 GB | ~10-20 GB |
| Taille sortie | ~1-10 KB (.mid) | ~1-5 MB (.wav pour 10s) |
| Cas d’usage | Composition, arrangement, education | Musique de fond, ambiance |
| Post-production | DAW (FL Studio, Ableton, MuseScore) | Edition audio limitee |
Quand utiliser chaque approche ?
| Besoin | Recommandation |
|---|---|
| Musique de fond pour video | MusicGen (résultat sonore direct) |
| Composition assistee | midi-model (edition note par note) |
| Prototypage rapide | midi-model (leger, rapide) |
| Qualite production | MusicGen + post-production audio |
| Education musicale | midi-model (visualisation des notes) |
| GPU limite | midi-model (~2 GB vs ~10 GB) |
# Mode interactif - Generation personnalisee
if notebook_mode == "interactive" and not skip_widgets:
print("MODE INTERACTIF - GENERATION MIDI PERSONNALISEE")
print("=" * 55)
print("\nChoisissez un preset ou creez votre propre configuration :")
print(" 1. Piano solo (tempo libre)")
print(" 2. Duo violon + piano")
print(" 3. Orchestre (cordes + bois + cuivres)")
print(" 4. Generation libre (inconditionnelle)")
print(" (Laissez vide pour passer)")
try:
choice = input("\nChoix [1-4] : ").strip()
if choice and midi_model_loaded:
user_presets = {
"1": {"name": "Piano solo", "tempo": 100, "instruments": [(0, 0)]},
"2": {"name": "Duo violon-piano", "tempo": 90, "instruments": [(0, 0), (1, 40)]},
"3": {"name": "Orchestre", "tempo": 80, "instruments": [(0, 48), (1, 40), (2, 73), (3, 56)]},
}
if choice in user_presets:
p = user_presets[choice]
print(f"\nGeneration : {p['name']}")
prompt = create_music_prompt(
tokenizer, tempo=p["tempo"],
instruments=p["instruments"]
)
result = generate_and_save(
model, tokenizer, prompt=prompt,
max_len=max_len, temp=temperature,
top_k=top_k, top_p=top_p,
filename=f"custom_{p['name'].lower().replace(' ', '_')}"
)
if synthesize_audio:
play_midi(result["score"], soundfont_path)
elif choice == "4":
print("\nGeneration libre...")
result = generate_and_save(
model, tokenizer, prompt=None,
max_len=max_len, temp=temperature,
top_k=top_k, top_p=top_p,
filename="custom_libre"
)
if synthesize_audio:
play_midi(result["score"], soundfont_path)
else:
print("Choix non reconnu")
else:
print("Mode interactif ignore")
except (KeyboardInterrupt, EOFError):
print("Mode interactif interrompu")
except Exception as e:
error_type = type(e).__name__
if "StdinNotImplemented" in error_type or "input" in str(e).lower():
print("Mode interactif non disponible (execution automatisee)")
else:
print(f"Erreur : {error_type} - {str(e)[:100]}")
else:
print("Mode batch - Interface interactive desactivee")MODE INTERACTIF - GENERATION MIDI PERSONNALISEE
=======================================================
Choisissez un preset ou creez votre propre configuration :
1. Piano solo (tempo libre)
2. Duo violon + piano
3. Orchestre (cordes + bois + cuivres)
4. Generation libre (inconditionnelle)
(Laissez vide pour passer)
Mode interactif non disponible (execution automatisee)
Interpretation : Generation personnalisee
Le mode interactif permet d’experimenter rapidement avec différentes configurations instrumentales et tempors. Les résultats dependent de :
| Facteur | Impact |
|---|---|
| Nombre d’instruments | Trop d’instruments = saturation sonore |
| Tempo | Doit correspondre au style musical vise |
| Combinaison instruments | Certaines combinaisons fonctionnent mieux ensemble |
Conseils pratiques : - Commencer par 2-3 instruments maximum - Utiliser des instruments de familles différentes (rythmique + melodie + accompagnement) - Ajuster le tempo en fonction du style (jazz ~130, classique ~90-120, ambient ~70-90)
Bonnes pratiques et guide de generation
Construire de bons prompts
| Élément | Bon exemple | Mauvais exemple |
|---|---|---|
| Instruments | 2-4 instruments complementaires | 10 instruments simultanes |
| Tempo | Adapte au genre (jazz ~130, ambient ~80) | Tempo extreme (>200 ou <40) |
| Tonalite | Coherente avec le genre | Tonalite aléatoire |
Limites actuelles
| Limite | Description | Contournement |
|---|---|---|
| Pas de texte | Prompt symbolique, pas de description textuelle | Utiliser MusicGen pour text-to-music |
| Duree variable | Le nombre d’événements ne correspond pas a une duree fixe | Ajuster max_len iterativement |
| Qualite son | Depend du SoundFont utilise | Utiliser des SoundFont de haute qualite |
| Pas de paroles | MIDI est instrumental | Combiner avec un modèle vocal |
Note technique : Format de fichier MIDI
Le format MIDI (Musical Instrument Digital Interface) stocke la musique de facon symbolique :
| Élément | Description | Valeur typique |
|---|---|---|
| Ticks per beat | Resolution temporelle | 480 (standard) |
| Tracks | Pistes independantes | 1-16 pistes |
| Channels | Canaux MIDI (instruments) | 0-15 (16 canaux) |
| Events | Note on/off, control change | Milliers par morceau |
| Velocity | Intensite de la note | 0-127 |
Avantages du format symbolique : - Taille extremement compacte (1-10 KB vs 1-5 MB pour audio) - Edition note par note possible - Compatible avec tous les DAW (FL Studio, Ableton, Logic, GarageBand) - Transposition et changement de tempo sans perte de qualite
# Statistiques de session
print("STATISTIQUES DE SESSION")
print("=" * 45)
print(f"Date : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Modele : {model_id} ({config_name})")
print(f"Device : {device}")
print(f"Max events : {max_len}")
print(f"Parametres : temp={temperature}, top_k={top_k}, top_p={top_p}")
print(f"Modele charge : {'Oui' if midi_model_loaded else 'Non'}")
print(f"FluidSynth : {'Oui' if fluidsynth_available else 'Non'}")
if gpu_available:
vram_current = torch.cuda.memory_allocated(0) / (1024**3)
print(f"VRAM utilisee : {vram_current:.2f} GB")
if save_results:
mid_files = list(OUTPUT_DIR.glob('*.mid'))
wav_files = list(OUTPUT_DIR.glob('*.wav'))
mid_size = sum(f.stat().st_size for f in mid_files)
wav_size = sum(f.stat().st_size for f in wav_files) / (1024*1024)
print(f"\nFichiers MIDI : {len(mid_files)} ({mid_size} octets)")
print(f"Fichiers WAV : {len(wav_files)} ({wav_size:.1f} MB)")
print(f"Repertoire : {OUTPUT_DIR.relative_to(GENAI_ROOT)}")
# Liberation memoire
if midi_model_loaded:
print(f"\nLiberation du modele...")
del model
gc.collect()
if gpu_available:
torch.cuda.empty_cache()
print(f"Memoire liberee")
print(f"\nPROCHAINES ETAPES")
print(f"1. Comparer avec MusicGen (02-3) pour choisir l'approche adaptee")
print(f"2. Editer les fichiers .mid dans un DAW (MuseScore, FL Studio)")
print(f"3. Explorer les LoRA J-Pop et Touhou pour des styles specifiques")
print(f"4. Combiner MIDI + TTS pour des compositions avec voix")
print(f"\nNotebook Generation MIDI termine - {datetime.now().strftime('%H:%M:%S')}")STATISTIQUES DE SESSION
=============================================
Date : 2026-08-20 17:39:09
Modele : skytnt/midi-model-tv2o-medium (tv2o-medium)
Device : cuda
Max events : 512
Parametres : temp=1.0, top_k=20, top_p=0.98
Modele charge : Oui
FluidSynth : Oui
VRAM utilisee : 0.44 GB
Fichiers MIDI : 11 (42545 octets)
Fichiers WAV : 7 (50.4 MB)
Repertoire : outputs\audio\midi
Liberation du modele...
Memoire liberee
PROCHAINES ETAPES
1. Comparer avec MusicGen (02-3) pour choisir l'approche adaptee
2. Editer les fichiers .mid dans un DAW (MuseScore, FL Studio)
3. Explorer les LoRA J-Pop et Touhou pour des styles specifiques
4. Combiner MIDI + TTS pour des compositions avec voix
Notebook Generation MIDI termine - 17:39:09
EXERCICE - Composition guidee multi-instruments
Objectif
Composer une piece MIDI multi-instruments en utilisant les prompts musicaux et comparer différents paramètres de sampling.
Consignes
- Choisir un contexte musical parmi :
- Bande-son de jeu video (RPG medieval)
- Musique d’ascenseur (jazz leger)
- Generique de podcast (moderne, court)
- Configurer le prompt :
- Choisir 3-4 instruments adaptes au contexte
- Définir le tempo, la tonalite et la signature temporelle
- Justifier vos choix musicaux
- générer 3 variations :
- Variation A : paramètres conservateurs (top_k=128, temp=0.8)
- Variation B : paramètres equilibres (top_k=20, temp=1.0)
- Variation C : paramètres creatifs (top_k=12, temp=1.1)
- Analyser et comparer :
- Ecouter les 3 variations synthetisees
- Noter chaque variation sur : coherence (1-5), originalite (1-5), adequation au contexte (1-5)
- Identifier la meilleure variation et expliquer pourquoi
Indices :
- Utilisez
create_music_prompt()pour configurer le prompt - Utilisez
generate_and_save()avec différents paramètres - Utilisez
play_midi()pour ecouter les résultats - Consultez le dictionnaire
INSTRUMENTSpour les numéros de patch MIDI
Critères de succes
Soumission : PR avec titre “Exercice MIDI - [Votre Nom]”, fichiers générés et tableau comparatif