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