#!/usr/bin/env python """Compose and build in one step from genre knowledge base.""" import sys import os import json import argparse from pathlib import Path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.stdout.reconfigure(encoding="utf-8") from src.composer import compose_from_genre from scripts.build import build_project from src.flp_builder.writer import FLPWriter KNOWLEDGE_DIR = Path(__file__).parent.parent / "knowledge" / "genres" OUTPUT_DIR = Path(__file__).parent.parent / "output" def main(): parser = argparse.ArgumentParser(description="Compose and build from genre") parser.add_argument("genre", help="Genre filename (e.g. reggaeton_2009)") parser.add_argument("--key", "-k", default=None, help="Override key (e.g. Am)") parser.add_argument("--bpm", "-b", type=float, default=None, help="Override BPM") parser.add_argument("--bars", type=int, default=None, help="Override bar count") parser.add_argument("--output", "-o", default=None, help="Output .flp path") args = parser.parse_args() genre_file = KNOWLEDGE_DIR / f"{args.genre}.json" if not genre_file.exists(): print(json.dumps({"error": f"Genre not found: {genre_file}", "available": [p.stem for p in KNOWLEDGE_DIR.glob("*.json")]})) sys.exit(1) overrides = {} if args.key: overrides["keys"] = [args.key] if args.bpm: overrides["bpm"] = {"default": args.bpm} if args.bars: overrides["structure"] = {"sections": [{"bars": args.bars}]} composition = compose_from_genre(str(genre_file), overrides if overrides else None) project = build_project(composition) OUTPUT_DIR.mkdir(parents=True, exist_ok=True) output_path = args.output or str( OUTPUT_DIR / f"{args.genre}_{composition['meta']['key']}_{composition['meta']['bpm']}bpm.flp" ) writer = FLPWriter(project) writer.write(output_path) result = { "status": "ok", "output": output_path, "genre": args.genre, "key": composition["meta"]["key"], "bpm": composition["meta"]["bpm"], "chord_progression": composition["meta"]["chord_progression"], "tracks": [ {"role": t["role"], "notes": len(t.get("notes", []))} for t in composition["tracks"] ], "channel_names": [ch.name for ch in project.channels], "total_notes": sum(len(n) for t in composition["tracks"] for n in t.get("notes", [])), } print(json.dumps(result, indent=2, ensure_ascii=False)) if __name__ == "__main__": main()