feat: pattern-based generators from real track analysis, RPP structure fixes, randomization
- Reverse-engineer drum patterns from 2 real reggaeton tracks with librosa - Create patterns.py with extracted frequency data (kick/snare/hihat positions) - Rewrite rhythm.py with pattern-bank generators (dembow, dense, trapico, offbeat) - Rewrite melodic.py with section-aware generators and humanization - Add weighted random sample selection in SampleSelector (top-5 pool) - Add generate_structure() with randomized templates and energy variance - Fix RPP structure: TEMPO arity (3→4 args), string quoting for empty strings - Rewrite quick_drumloop_test.py with correct REAPER ground truth format - Add scripts/analyze_examples.py for reverse engineering audio tracks - Add --seed argument for reproducible generation - 72 tests passing
This commit is contained in:
@@ -16,6 +16,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from dataclasses import dataclass, field
|
||||
@@ -306,10 +307,27 @@ class SampleSelector:
|
||||
matches.sort(key=lambda m: m.score, reverse=True)
|
||||
return matches[:limit]
|
||||
|
||||
def select_one(self, role: str, **kwargs) -> Optional[dict]:
|
||||
"""Select the single best matching sample."""
|
||||
results = self.select(role=role, limit=1, **kwargs)
|
||||
return results[0].sample if results else None
|
||||
def select_one(
|
||||
self,
|
||||
role: str,
|
||||
seed: Optional[int] = None,
|
||||
**kwargs,
|
||||
) -> Optional[dict]:
|
||||
"""Select one sample using weighted random from top-5 candidates.
|
||||
|
||||
The top-5 candidates are selected with weights [5, 4, 3, 2, 1],
|
||||
favoring higher-scored results while allowing variation across calls.
|
||||
Pass seed for reproducible output.
|
||||
"""
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
results = self.select(role=role, limit=5, **kwargs)
|
||||
if not results:
|
||||
return None
|
||||
candidates = results[:5]
|
||||
weights = [5, 4, 3, 2, 1][: len(candidates)]
|
||||
selected = random.choices(candidates, weights=weights, k=1)[0]
|
||||
return selected.sample
|
||||
|
||||
def get_roles(self) -> list[str]:
|
||||
"""Get all available roles and their counts."""
|
||||
|
||||
Reference in New Issue
Block a user