Compare commits
No commits in common. "main" and "master" have entirely different histories.
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
|
nohup.out
|
||||||
|
py3/
|
||||||
|
*.egg-info/
|
||||||
|
*.pid
|
||||||
59
ah.py
Normal file
59
ah.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
"""songrate standalone service - 音乐评估 GPU 服务 (端口 8900)"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from ahserver.webapp import webapp
|
||||||
|
from ahserver.serverenv import ServerEnv
|
||||||
|
from appPublic.registerfunction import RegisterFunction
|
||||||
|
from appPublic.log import debug
|
||||||
|
|
||||||
|
# Add songrate package to path
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from songrate.evaluator import evaluate_song
|
||||||
|
|
||||||
|
async def do_evaluate(request, *args, **kw):
|
||||||
|
"""POST /api/evaluate - 评估歌曲"""
|
||||||
|
env = request._run_ns
|
||||||
|
pkw = env.params_kw
|
||||||
|
|
||||||
|
scene = getattr(pkw, 'scene', 'pop') or 'pop'
|
||||||
|
filepath = getattr(pkw, 'filepath', '') or ''
|
||||||
|
audio_path = getattr(pkw, 'audio_path', '') or ''
|
||||||
|
|
||||||
|
if not filepath:
|
||||||
|
filepath = audio_path
|
||||||
|
|
||||||
|
if not filepath:
|
||||||
|
return json.dumps({"error": "missing filepath or audio_path"}, ensure_ascii=False)
|
||||||
|
|
||||||
|
# If it's a relative/web path, resolve to local
|
||||||
|
if not os.path.isabs(filepath):
|
||||||
|
from ahserver.filestorage import FileStorage
|
||||||
|
fs = FileStorage()
|
||||||
|
filepath = fs.realPath(filepath)
|
||||||
|
|
||||||
|
if not os.path.exists(filepath):
|
||||||
|
return json.dumps({"error": f"file not found: {filepath}"}, ensure_ascii=False)
|
||||||
|
|
||||||
|
# Run evaluation in thread pool (GPU bound)
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
with ThreadPoolExecutor(max_workers=1) as pool:
|
||||||
|
result = await loop.run_in_executor(pool, evaluate_song, filepath, scene)
|
||||||
|
|
||||||
|
return json.dumps(result, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
def init():
|
||||||
|
rf = RegisterFunction()
|
||||||
|
rf.register('evaluate', do_evaluate)
|
||||||
|
debug('songrate service initialized')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
webapp(init)
|
||||||
15
conf/config.json
Normal file
15
conf/config.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"website":{
|
||||||
|
"paths":[["$[workdir]$/wwwroot",""]],
|
||||||
|
"client_max_size":500000000,
|
||||||
|
"host":"0.0.0.0",
|
||||||
|
"port":8900,
|
||||||
|
"coding":"utf-8",
|
||||||
|
"indexes":["index.html","index.dspy"],
|
||||||
|
"processors":[[".dspy","dspy"]],
|
||||||
|
"startswiths":[
|
||||||
|
{"leading":"/api/evaluate","registerfunction":"evaluate"},
|
||||||
|
{"leading":"/idfile","registerfunction":"idfile"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
28
songrate_audio_patch.py
Normal file
28
songrate_audio_patch.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"""Patch songrate's load_audio to use soundfile instead of torchaudio"""
|
||||||
|
import soundfile as sf
|
||||||
|
import numpy as np
|
||||||
|
import torch
|
||||||
|
|
||||||
|
def load_audio_patched(filepath, sr=22050):
|
||||||
|
"""使用 soundfile 加载音频,避免 torchcodec 依赖"""
|
||||||
|
# 读取音频
|
||||||
|
data, orig_sr = sf.read(filepath)
|
||||||
|
|
||||||
|
# 转单声道
|
||||||
|
if len(data.shape) > 1:
|
||||||
|
data = np.mean(data, axis=1)
|
||||||
|
|
||||||
|
# 重采样 (使用 librosa)
|
||||||
|
if orig_sr != sr:
|
||||||
|
import librosa
|
||||||
|
data = librosa.resample(data, orig_sr=orig_sr, target_sr=sr)
|
||||||
|
|
||||||
|
# 转 tensor 并移到 GPU
|
||||||
|
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
|
||||||
|
waveform = torch.from_numpy(data).float().to(device)
|
||||||
|
|
||||||
|
return waveform, sr
|
||||||
|
|
||||||
|
# Monkey patch
|
||||||
|
import songrate.analyzers
|
||||||
|
songrate.analyzers.load_audio = load_audio_patched
|
||||||
Loading…
x
Reference in New Issue
Block a user