feat: reggaeton production system with intelligent sample selection and FLP generation
This commit is contained in:
122
scripts/batch_generate.py
Normal file
122
scripts/batch_generate.py
Normal file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Batch FLP generator — produces 50 unique reggaeton FLP+JSON pairs.
|
||||
|
||||
Usage:
|
||||
python scripts/batch_generate.py [--count 50] [--out-dir output/batch]
|
||||
|
||||
Output structure:
|
||||
output/batch_{timestamp}/
|
||||
reggaeton_000_95bpm_Am_i-VII-VI-VII.json
|
||||
reggaeton_000_95bpm_Am_i-VII-VI-VII.flp
|
||||
reggaeton_001_90bpm_Dm_i-iv-VII-III.json
|
||||
...
|
||||
manifest.json ← list of all generated songs with metadata
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parents[1]))
|
||||
|
||||
from src.composer.variation import generate_batch
|
||||
from src.flp_builder.builder import FLPBuilder
|
||||
from src.flp_builder.schema import SongDefinition
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Filename helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_UNSAFE_RE = re.compile(r'[^\w\-]')
|
||||
|
||||
|
||||
def sanitize_filename(s: str) -> str:
|
||||
"""Replace unsafe filename chars with _."""
|
||||
return _UNSAFE_RE.sub('_', s)
|
||||
|
||||
|
||||
def make_filename(idx: int, song: SongDefinition) -> str:
|
||||
"""Build stem like ``reggaeton_000_95bpm_Am_i_VII_VI_VII`` (no extension)."""
|
||||
prog_safe = sanitize_filename(song.progression_name)
|
||||
return f"reggaeton_{idx:03d}_{song.meta.bpm}bpm_{song.meta.key}_{prog_safe}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Manifest
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def build_manifest(songs: list[SongDefinition], filenames: list[str]) -> dict:
|
||||
"""Build manifest dict with per-song metadata."""
|
||||
entries = []
|
||||
for idx, (song, stem) in enumerate(zip(songs, filenames)):
|
||||
bar_count = int(max(item.bar + item.bars for item in song.items))
|
||||
entries.append({
|
||||
"idx": idx,
|
||||
"filename": stem,
|
||||
"bpm": song.meta.bpm,
|
||||
"key": song.meta.key,
|
||||
"progression": song.progression_name,
|
||||
"title": song.meta.title,
|
||||
"bars": bar_count,
|
||||
})
|
||||
return {
|
||||
"generated_at": datetime.now().isoformat(),
|
||||
"count": len(songs),
|
||||
"songs": entries,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Main
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Batch FLP generator")
|
||||
parser.add_argument("--count", type=int, default=50,
|
||||
help="Number of songs to generate (default: 50)")
|
||||
parser.add_argument("--out-dir", default="",
|
||||
help="Output directory (default: output/batch_{timestamp})")
|
||||
args = parser.parse_args()
|
||||
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
out_dir = Path(args.out_dir) if args.out_dir else Path("output") / f"batch_{timestamp}"
|
||||
out_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
print(f"Generating {args.count} songs -> {out_dir}")
|
||||
|
||||
songs = generate_batch(args.count)
|
||||
builder = FLPBuilder()
|
||||
filenames: list[str] = []
|
||||
|
||||
for idx, song in enumerate(songs):
|
||||
stem = make_filename(idx, song)
|
||||
filenames.append(stem)
|
||||
|
||||
# Write JSON
|
||||
json_path = out_dir / f"{stem}.json"
|
||||
json_path.write_text(song.to_json(), encoding="utf-8")
|
||||
|
||||
# Write FLP
|
||||
flp_path = out_dir / f"{stem}.flp"
|
||||
flp_bytes = builder.build(song)
|
||||
flp_path.write_bytes(flp_bytes)
|
||||
|
||||
bar_count = int(max(item.bar + item.bars for item in song.items))
|
||||
print(f" [{idx+1:>3}/{args.count}] {stem}.flp {len(flp_bytes):>9,}b {bar_count}bars")
|
||||
|
||||
# Write manifest
|
||||
manifest = build_manifest(songs, filenames)
|
||||
(out_dir / "manifest.json").write_text(
|
||||
json.dumps(manifest, indent=2), encoding="utf-8"
|
||||
)
|
||||
|
||||
total_size = sum((out_dir / f"{f}.flp").stat().st_size for f in filenames)
|
||||
print(f"\nDone. {args.count} FLPs in {out_dir}")
|
||||
print(f" Total size: {total_size:,} bytes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user