60 lines
2.8 KiB
Python
60 lines
2.8 KiB
Python
import requests
|
|
import json
|
|
import re
|
|
|
|
class LLMAcousticBridge:
|
|
def __init__(self, model_name="phi3", host="http://localhost:11434"):
|
|
self.model_name = model_name
|
|
self.api_url = f"{host}/api/generate"
|
|
|
|
def _clean_json(self, text):
|
|
"""Вытаскивает чистый JSON из ответа нейросети."""
|
|
try:
|
|
match = re.search(r'\{.*\}', text, re.DOTALL)
|
|
if match:
|
|
return json.loads(match.group(0))
|
|
return json.loads(text)
|
|
except:
|
|
return None
|
|
|
|
def get_acoustic_profile(self, valence, arousal, scene_descriptions):
|
|
"""Просит LLM сгенерировать идеальный звук под описание."""
|
|
# Объединяем описания, если загружено несколько фото
|
|
context_str = " | ".join(scene_descriptions) if scene_descriptions else "abstract scene"
|
|
|
|
prompt = f"""You are an expert music producer and acoustic engineer.
|
|
Analyze the visual context and emotions to determine the ideal background music properties.
|
|
Emotions: Valence {valence:.1f}/9.0 (Positivity), Arousal {arousal:.1f}/9.0 (Energy).
|
|
Visual Context: {context_str}.
|
|
|
|
Map this scene to exactly 6 acoustic features. Values MUST be floats between 0.0 and 1.0.
|
|
1. "energy": (Loudness/Density. High for massive/busy scenes, Low for calm)
|
|
2. "flux": (Rhythmic sharpness/Beat. High for action/people/cars, Low for static nature)
|
|
3. "centroid": (Brightness: 0=Dark/Bass/Massive, 1=Bright/Treble/Light)
|
|
4. "pitch": (Fundamental frequency: 0=Low pitch/Huge objects, 1=High pitch/Small objects)
|
|
5. "hnr": (Harmonics-to-Noise: 0=Noisy/Distorted textures, 1=Clear/Melodic/Smooth textures)
|
|
6. "zcr": (Percussiveness. High for detailed noise like leaves/rain, Low for solid blocks)
|
|
|
|
Return ONLY a valid JSON object. Do not add any text or explanation.
|
|
Example: {{"energy": 0.5, "flux": 0.2, "centroid": 0.4, "pitch": 0.3, "hnr": 0.8, "zcr": 0.1}}"""
|
|
|
|
try:
|
|
response = requests.post(self.api_url, json={
|
|
"model": self.model_name,
|
|
"prompt": prompt,
|
|
"stream": False,
|
|
"format": "json"
|
|
}, timeout=30)
|
|
response.raise_for_status()
|
|
|
|
result_text = response.json().get("response", "")
|
|
profile = self._clean_json(result_text)
|
|
|
|
# Проверяем, что все нужные ключи есть
|
|
required_keys = ['energy', 'flux', 'centroid', 'pitch', 'hnr', 'zcr']
|
|
if profile and all(k in profile for k in required_keys):
|
|
return profile
|
|
return None
|
|
except Exception as e:
|
|
print(f"⚠️ Ошибка связи с локальной LLM: {e}")
|
|
return None |