Generation 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 >>

# 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 musicaux
# 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

  1. Choisir un contexte musical parmi :
    • Bande-son de jeu video (RPG medieval)
    • Musique d’ascenseur (jazz leger)
    • Generique de podcast (moderne, court)
  2. Configurer le prompt :
    • Choisir 3-4 instruments adaptes au contexte
    • Définir le tempo, la tonalite et la signature temporelle
    • Justifier vos choix musicaux
  3. 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)
  4. 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 INSTRUMENTS pour 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

Retour au sommet