Files
kxkm_clown/scripts/compose_music.py
T
L'électron rare 491e6ad8a4 chantier-1+2: voice cloning XTTS-v2 + génération musicale /compose
## Chantier 1 — Voice Cloning
- scripts/xtts_clone.py: Coqui XTTS-v2 voice cloning (6s sample → voix)
- ws-chat.ts: synthesizeTTS avec détection sample → XTTS ou Piper
- PersonaDetail.tsx: upload/delete sample vocal
- app.ts: endpoints voice-sample (POST/GET/DELETE)

## Chantier 2 — Génération Musicale
- scripts/compose_music.py: ACE-Step fallback MusicGen
- /compose command (5min timeout, broadcast audio base64)
- Chat.tsx: player <audio controls> inline
- Eno persona (musique générative/ambient)
- Pharmacius: routing @Eno

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 09:00:01 +01:00

92 lines
3.2 KiB
Python

#!/usr/bin/env python3
"""
KXKM_Clown — Music Generation via ACE-Step 1.5
Generates music from text prompts locally.
Usage:
python scripts/compose_music.py \
--prompt "ambient drone with deep bass, musique concrete style" \
--duration 30 \
--output /tmp/music.wav
Fallback to MusicGen if ACE-Step not installed.
Install: pip install ace-step OR pip install transformers scipy
"""
import argparse, json, os, sys, time
def parse_args():
p = argparse.ArgumentParser(description="KXKM Music Generation")
p.add_argument("--prompt", required=True)
p.add_argument("--duration", type=int, default=30, help="Duration in seconds")
p.add_argument("--output", required=True)
p.add_argument("--style", default="experimental", help="Style hint")
return p.parse_args()
def generate_with_musicgen(prompt, duration, output):
"""Fallback: use Meta's MusicGen (smaller, more available)."""
from transformers import AutoProcessor, MusicgenForConditionalGeneration
import scipy.io.wavfile
import numpy as np
print("[compose] Loading MusicGen small...", file=sys.stderr)
processor = AutoProcessor.from_pretrained("facebook/musicgen-small")
model = MusicgenForConditionalGeneration.from_pretrained("facebook/musicgen-small")
inputs = processor(text=[prompt], padding=True, return_tensors="pt")
# ~256 tokens per second of audio
max_tokens = min(duration * 256, 1536) # cap at ~6s for musicgen-small
print(f"[compose] Generating {max_tokens} tokens...", file=sys.stderr)
audio_values = model.generate(**inputs, max_new_tokens=max_tokens)
sampling_rate = model.config.audio_encoder.sampling_rate
audio_data = audio_values[0, 0].cpu().numpy()
audio_int16 = (audio_data * 32767).astype(np.int16)
scipy.io.wavfile.write(output, rate=sampling_rate, data=audio_int16)
return {"generator": "musicgen-small", "sampling_rate": sampling_rate}
def main():
args = parse_args()
start = time.time()
result = {"status": "failed", "error": None}
try:
os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
full_prompt = f"{args.prompt}, {args.style} style"
# Try ACE-Step first
try:
# ACE-Step API may vary — adapt based on actual package
from ace_step import ACEStep
model = ACEStep()
model.generate(prompt=full_prompt, duration=args.duration, output_path=args.output)
gen_info = {"generator": "ace-step"}
except ImportError:
# Fallback to MusicGen
gen_info = generate_with_musicgen(full_prompt, args.duration, args.output)
duration = time.time() - start
file_size = os.path.getsize(args.output)
result = {
"status": "completed",
"outputFile": args.output,
"duration": round(duration, 2),
"fileSize": file_size,
"prompt": args.prompt[:200],
**gen_info,
}
print(f"[compose] Done in {duration:.1f}s -> {args.output} ({file_size} bytes)", file=sys.stderr)
except Exception as e:
result["error"] = str(e)
print(f"[compose] ERROR: {e}", file=sys.stderr)
print(json.dumps(result))
if __name__ == "__main__":
main()