ASR失败
This commit is contained in:
parent
730da3da26
commit
07b263a45c
|
|
@ -155,7 +155,7 @@ class Settings(BaseSettings):
|
|||
)
|
||||
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
env_file=[".env", "../.env"], # 先查找当前目录,再查找父目录
|
||||
env_file_encoding="utf-8",
|
||||
case_sensitive=True,
|
||||
extra="ignore",
|
||||
|
|
|
|||
70
lover/main_simple.py
Normal file
70
lover/main_simple.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import sys
|
||||
import os
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
import logging
|
||||
import dashscope
|
||||
from pathlib import Path
|
||||
|
||||
from lover.routers import voice_call as voice_call_router
|
||||
from lover.response import ApiResponse
|
||||
from lover.config import settings
|
||||
|
||||
# 初始化 DashScope API Key
|
||||
if settings.DASHSCOPE_API_KEY:
|
||||
dashscope.api_key = settings.DASHSCOPE_API_KEY
|
||||
|
||||
# 配置日志
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
|
||||
app = FastAPI(title="LOVER API - Simple")
|
||||
|
||||
# 创建 TTS 文件目录
|
||||
tts_dir = Path("public/tts")
|
||||
tts_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 挂载静态文件服务(用于提供 TTS 音频文件)
|
||||
app.mount("/tts", StaticFiles(directory=str(tts_dir)), name="tts")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"], # 简化 CORS 配置
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 只包含语音通话路由
|
||||
app.include_router(voice_call_router.router)
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception_handler(request: Request, exc: HTTPException):
|
||||
detail = exc.detail
|
||||
msg = detail if isinstance(detail, str) else str(detail)
|
||||
return JSONResponse(
|
||||
status_code=exc.status_code,
|
||||
content={"code": exc.status_code, "msg": msg, "data": None},
|
||||
)
|
||||
|
||||
@app.exception_handler(Exception)
|
||||
async def generic_exception_handler(request: Request, exc: Exception):
|
||||
logging.exception("Unhandled error", exc_info=exc)
|
||||
return JSONResponse(
|
||||
status_code=500,
|
||||
content={"code": 500, "msg": "服务器内部错误", "data": None},
|
||||
)
|
||||
|
||||
@app.get("/health", response_model=ApiResponse[dict])
|
||||
async def health():
|
||||
return ApiResponse(code=1, msg="ok", data={"status": "ok"})
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=30102)
|
||||
|
|
@ -712,6 +712,282 @@ class VoiceCallSession:
|
|||
|
||||
|
||||
@router.post("/call/asr")
|
||||
async def json_asr(
|
||||
request: dict,
|
||||
user: AuthedUser = Depends(get_current_user)
|
||||
):
|
||||
"""JSON ASR:接收 base64 编码的音频数据并返回识别结果"""
|
||||
try:
|
||||
# 从请求中提取音频数据
|
||||
if 'audio_data' not in request:
|
||||
logger.error("请求中缺少 audio_data 字段")
|
||||
raise HTTPException(status_code=400, detail="缺少 audio_data 字段")
|
||||
|
||||
audio_base64 = request['audio_data']
|
||||
audio_format = request.get('format', 'mp3')
|
||||
|
||||
logger.info(f"收到 JSON ASR 请求,格式: {audio_format}")
|
||||
|
||||
# 解码 base64 音频数据
|
||||
try:
|
||||
import base64
|
||||
audio_data = base64.b64decode(audio_base64)
|
||||
logger.info(f"解码音频数据成功,大小: {len(audio_data)} 字节")
|
||||
except Exception as decode_error:
|
||||
logger.error(f"base64 解码失败: {decode_error}")
|
||||
raise HTTPException(status_code=400, detail="音频数据解码失败")
|
||||
|
||||
# 检查音频数据是否为空
|
||||
if not audio_data:
|
||||
logger.error("解码后的音频数据为空")
|
||||
raise HTTPException(status_code=400, detail="音频数据为空")
|
||||
|
||||
# 计算预期的音频时长
|
||||
if audio_format.lower() == 'mp3':
|
||||
# MP3 文件,粗略估算时长
|
||||
expected_duration = len(audio_data) / 16000 # 粗略估算
|
||||
logger.info(f"MP3 音频数据,预估时长: {expected_duration:.2f} 秒")
|
||||
else:
|
||||
# PCM 格式:16kHz 单声道 16bit,每秒需要 32000 字节
|
||||
expected_duration = len(audio_data) / 32000
|
||||
logger.info(f"PCM 音频数据,预期时长: {expected_duration:.2f} 秒")
|
||||
|
||||
if expected_duration < 0.1:
|
||||
logger.warning("音频时长太短,可能无法识别")
|
||||
test_text = f"音频时长太短({expected_duration:.2f}秒),请说话时间长一些"
|
||||
from ..response import success_response
|
||||
return success_response({"text": test_text})
|
||||
|
||||
# 检查 DashScope 配置
|
||||
if not settings.DASHSCOPE_API_KEY:
|
||||
logger.error("未配置 DASHSCOPE_API_KEY")
|
||||
test_text = f"ASR 未配置,收到 {expected_duration:.1f}秒 音频"
|
||||
from ..response import success_response
|
||||
return success_response({"text": test_text})
|
||||
|
||||
# 设置 API Key
|
||||
dashscope.api_key = settings.DASHSCOPE_API_KEY
|
||||
|
||||
# 使用 DashScope 进行批量 ASR
|
||||
logger.info("开始调用 DashScope ASR...")
|
||||
|
||||
try:
|
||||
from dashscope.audio.asr import Transcription
|
||||
from ..oss_utils import upload_audio_file, delete_audio_file, test_oss_connection
|
||||
|
||||
# 首先测试 OSS 连接
|
||||
logger.info("测试 OSS 连接...")
|
||||
if not test_oss_connection():
|
||||
# OSS 连接失败,使用临时方案
|
||||
logger.warning("OSS 连接失败,使用临时测试方案")
|
||||
test_text = f"OSS 暂不可用,但成功接收到 {expected_duration:.1f}秒 {audio_format.upper()} 音频数据({len(audio_data)} 字节)"
|
||||
from ..response import success_response
|
||||
return success_response({"text": test_text})
|
||||
|
||||
logger.info("OSS 连接测试通过")
|
||||
|
||||
# 上传音频文件到 OSS
|
||||
logger.info(f"上传 {audio_format.upper()} 音频到 OSS...")
|
||||
file_url = upload_audio_file(audio_data, audio_format)
|
||||
logger.info(f"音频文件上传成功: {file_url}")
|
||||
|
||||
# 调用 DashScope ASR
|
||||
try:
|
||||
logger.info("调用 DashScope Transcription API...")
|
||||
logger.info(f"使用文件 URL: {file_url}")
|
||||
|
||||
task_response = Transcription.async_call(
|
||||
model='paraformer-v2',
|
||||
file_urls=[file_url],
|
||||
parameters={
|
||||
'format': audio_format,
|
||||
'sample_rate': 16000,
|
||||
'enable_words': False
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"ASR 任务响应: status_code={task_response.status_code}")
|
||||
|
||||
if task_response.status_code != 200:
|
||||
error_msg = getattr(task_response, 'message', 'Unknown error')
|
||||
logger.error(f"ASR 任务创建失败: {error_msg}")
|
||||
raise Exception(f"ASR 任务创建失败: {error_msg}")
|
||||
|
||||
task_id = task_response.output.task_id
|
||||
logger.info(f"ASR 任务已创建: {task_id}")
|
||||
|
||||
# 等待识别完成
|
||||
logger.info("等待 ASR 识别完成...")
|
||||
import time
|
||||
|
||||
max_wait_time = 30
|
||||
start_time = time.time()
|
||||
|
||||
transcribe_response = None
|
||||
|
||||
try:
|
||||
import threading
|
||||
import queue
|
||||
|
||||
result_queue = queue.Queue()
|
||||
exception_queue = queue.Queue()
|
||||
|
||||
def wait_for_result():
|
||||
try:
|
||||
result = Transcription.wait(task=task_id)
|
||||
result_queue.put(result)
|
||||
except Exception as e:
|
||||
exception_queue.put(e)
|
||||
|
||||
# 启动等待线程
|
||||
wait_thread = threading.Thread(target=wait_for_result)
|
||||
wait_thread.daemon = True
|
||||
wait_thread.start()
|
||||
|
||||
# 轮询检查结果或超时
|
||||
while time.time() - start_time < max_wait_time:
|
||||
try:
|
||||
transcribe_response = result_queue.get_nowait()
|
||||
logger.info("ASR 任务完成")
|
||||
break
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
try:
|
||||
exception = exception_queue.get_nowait()
|
||||
logger.error(f"ASR 等待过程中出错: {exception}")
|
||||
raise exception
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
logger.info(f"ASR 任务仍在处理中... 已等待 {elapsed:.1f}秒")
|
||||
time.sleep(2)
|
||||
|
||||
if transcribe_response is None:
|
||||
logger.error(f"ASR 任务超时({max_wait_time}秒)")
|
||||
from ..response import success_response
|
||||
return success_response({"text": f"语音识别处理时间较长,请稍后重试(音频时长: {expected_duration:.1f}秒)"})
|
||||
|
||||
except Exception as wait_error:
|
||||
logger.error(f"ASR 等待过程中出错: {wait_error}")
|
||||
from ..response import success_response
|
||||
return success_response({"text": f"语音识别服务暂时不可用,请稍后重试"})
|
||||
|
||||
logger.info(f"ASR 识别响应: status_code={transcribe_response.status_code}")
|
||||
|
||||
if transcribe_response.status_code != 200:
|
||||
error_msg = getattr(transcribe_response, 'message', 'Unknown error')
|
||||
logger.error(f"ASR 识别失败: {error_msg}")
|
||||
raise Exception(f"ASR 识别失败: {error_msg}")
|
||||
|
||||
# 检查任务状态
|
||||
result = transcribe_response.output
|
||||
logger.info(f"ASR 任务状态: {result.task_status}")
|
||||
|
||||
if result.task_status == "SUCCEEDED":
|
||||
logger.info("ASR 识别成功,开始解析结果...")
|
||||
elif result.task_status == "FAILED":
|
||||
error_code = getattr(result, 'code', 'Unknown')
|
||||
error_message = getattr(result, 'message', 'Unknown error')
|
||||
|
||||
logger.error(f"ASR 任务失败: {error_code} - {error_message}")
|
||||
|
||||
if error_code == "SUCCESS_WITH_NO_VALID_FRAGMENT":
|
||||
user_message = "音频中未检测到有效语音,请确保录音时有说话内容"
|
||||
elif error_code == "DECODE_ERROR":
|
||||
user_message = "音频格式解码失败,请检查录音设置"
|
||||
logger.error("音频解码失败 - 可能的原因:")
|
||||
logger.error("1. 音频格式不正确或损坏")
|
||||
logger.error("2. 编码参数不匹配(建议:16kHz, 单声道, 64kbps)")
|
||||
logger.error("3. 文件头信息缺失或错误")
|
||||
elif error_code == "FILE_DOWNLOAD_FAILED":
|
||||
user_message = "无法下载音频文件,请检查网络连接"
|
||||
elif error_code == "AUDIO_FORMAT_UNSUPPORTED":
|
||||
user_message = "音频格式不支持,请使用标准格式录音"
|
||||
else:
|
||||
user_message = f"语音识别失败: {error_message}"
|
||||
|
||||
from ..response import success_response
|
||||
return success_response({"text": user_message})
|
||||
else:
|
||||
logger.warning(f"ASR 任务状态未知: {result.task_status}")
|
||||
from ..response import success_response
|
||||
return success_response({"text": f"语音识别状态异常: {result.task_status}"})
|
||||
|
||||
# 解析识别结果
|
||||
text_result = ""
|
||||
|
||||
if hasattr(result, 'results') and result.results:
|
||||
logger.info(f"找到 results 字段,长度: {len(result.results)}")
|
||||
|
||||
for i, item in enumerate(result.results):
|
||||
if isinstance(item, dict) and 'transcription_url' in item and item['transcription_url']:
|
||||
transcription_url = item['transcription_url']
|
||||
logger.info(f"找到 transcription_url: {transcription_url}")
|
||||
|
||||
try:
|
||||
import requests
|
||||
response = requests.get(transcription_url, timeout=10)
|
||||
if response.status_code == 200:
|
||||
transcription_data = response.json()
|
||||
logger.info(f"转录数据: {transcription_data}")
|
||||
|
||||
if 'transcripts' in transcription_data:
|
||||
for transcript in transcription_data['transcripts']:
|
||||
if 'text' in transcript:
|
||||
text_result += transcript['text'] + " "
|
||||
logger.info(f"提取转录文本: {transcript['text']}")
|
||||
|
||||
if text_result.strip():
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"处理 transcription_url 失败: {e}")
|
||||
|
||||
text_result = text_result.strip()
|
||||
|
||||
if not text_result:
|
||||
logger.warning("ASR 未识别到文本内容")
|
||||
text_result = f"未识别到语音内容({expected_duration:.1f}秒音频)"
|
||||
|
||||
logger.info(f"最终 ASR 识别结果: {text_result}")
|
||||
|
||||
from ..response import success_response
|
||||
return success_response({"text": text_result})
|
||||
|
||||
finally:
|
||||
# 清理 OSS 上的临时文件
|
||||
try:
|
||||
delete_audio_file(file_url)
|
||||
logger.info("OSS 临时文件已清理")
|
||||
except Exception as e:
|
||||
logger.warning(f"清理 OSS 文件失败: {e}")
|
||||
|
||||
except Exception as asr_error:
|
||||
logger.error(f"DashScope ASR 调用失败: {asr_error}", exc_info=True)
|
||||
|
||||
error_msg = str(asr_error)
|
||||
if "OSS" in error_msg:
|
||||
test_text = f"OSS 配置问题,收到 {expected_duration:.1f}秒 音频"
|
||||
elif "Transcription" in error_msg:
|
||||
test_text = f"ASR 服务异常,收到 {expected_duration:.1f}秒 音频"
|
||||
else:
|
||||
test_text = f"ASR 处理失败,收到 {expected_duration:.1f}秒 音频"
|
||||
|
||||
logger.info(f"返回备用文本: {test_text}")
|
||||
|
||||
from ..response import success_response
|
||||
return success_response({"text": test_text})
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"JSON ASR 处理错误: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"ASR 处理失败: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/call/batch_asr")
|
||||
async def batch_asr(
|
||||
audio: UploadFile = File(...),
|
||||
user: AuthedUser = Depends(get_current_user)
|
||||
|
|
|
|||
147
test_asr_fix.py
Normal file
147
test_asr_fix.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试 ASR 修复是否有效
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('.')
|
||||
|
||||
import requests
|
||||
import base64
|
||||
import wave
|
||||
import struct
|
||||
import math
|
||||
import logging
|
||||
|
||||
# 设置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def create_speech_like_audio():
|
||||
"""创建类似语音的音频数据"""
|
||||
sample_rate = 16000
|
||||
duration = 3 # 3 秒
|
||||
|
||||
# 生成更复杂的音频,模拟语音特征
|
||||
samples = []
|
||||
for i in range(sample_rate * duration):
|
||||
t = i / sample_rate
|
||||
|
||||
# 基频(模拟人声基频)
|
||||
f0 = 150 + 50 * math.sin(2 * math.pi * 0.5 * t) # 变化的基频
|
||||
|
||||
# 多个谐波分量
|
||||
sample = 0
|
||||
for harmonic in range(1, 6): # 前5个谐波
|
||||
amplitude = 1.0 / harmonic # 谐波幅度递减
|
||||
sample += amplitude * math.sin(2 * math.pi * f0 * harmonic * t)
|
||||
|
||||
# 添加包络(模拟语音的动态变化)
|
||||
envelope = 0.5 * (1 + math.sin(2 * math.pi * 2 * t)) # 2Hz 的包络变化
|
||||
|
||||
# 添加一些噪声(模拟语音的复杂性)
|
||||
noise = 0.1 * (math.sin(2 * math.pi * 1000 * t) + 0.5 * math.sin(2 * math.pi * 2000 * t))
|
||||
|
||||
# 组合所有分量
|
||||
final_sample = (sample + noise) * envelope * 0.3 # 控制总体音量
|
||||
|
||||
# 转换为 16-bit 整数
|
||||
sample_int = int(16000 * final_sample)
|
||||
sample_int = max(-32767, min(32767, sample_int))
|
||||
samples.append(sample_int)
|
||||
|
||||
# 转换为字节数据
|
||||
audio_bytes = bytearray()
|
||||
for sample in samples:
|
||||
audio_bytes.extend(struct.pack('<h', sample))
|
||||
|
||||
logger.info(f"创建类语音音频数据,大小: {len(audio_bytes)} 字节,时长: {duration} 秒")
|
||||
return bytes(audio_bytes)
|
||||
|
||||
def test_asr_endpoint():
|
||||
"""测试 ASR 端点"""
|
||||
|
||||
# 创建测试音频数据
|
||||
audio_data = create_speech_like_audio()
|
||||
|
||||
# 转换为 base64
|
||||
audio_base64 = base64.b64encode(audio_data).decode('utf-8')
|
||||
logger.info(f"Base64 编码长度: {len(audio_base64)}")
|
||||
|
||||
# 准备请求数据
|
||||
request_data = {
|
||||
'audio_data': audio_base64,
|
||||
'format': 'mp3' # 虽然实际是PCM,但测试格式处理
|
||||
}
|
||||
|
||||
# 发送请求到后端
|
||||
url = "http://192.168.1.141:30102/voice/call/asr"
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer test_token'
|
||||
}
|
||||
|
||||
logger.info(f"发送 ASR 请求到: {url}")
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=request_data, headers=headers, timeout=60)
|
||||
|
||||
logger.info(f"响应状态码: {response.status_code}")
|
||||
logger.info(f"响应头: {response.headers}")
|
||||
logger.info(f"响应内容: {response.text}")
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
result = response.json()
|
||||
logger.info(f"✅ ASR 请求成功")
|
||||
logger.info(f"识别结果: {result}")
|
||||
|
||||
if 'data' in result and 'text' in result['data']:
|
||||
text = result['data']['text']
|
||||
logger.info(f"🎯 识别文本: {text}")
|
||||
|
||||
# 检查是否是预期的错误消息
|
||||
if "音频格式解码失败" in text:
|
||||
logger.info("✅ 收到格式错误提示,说明 ASR 流程正常工作")
|
||||
return True
|
||||
elif "未识别到语音内容" in text:
|
||||
logger.info("✅ 收到无语音提示,说明 ASR 流程正常工作")
|
||||
return True
|
||||
elif "OSS" in text:
|
||||
logger.info("✅ 收到 OSS 相关提示,说明流程到达了 OSS 阶段")
|
||||
return True
|
||||
else:
|
||||
logger.info("✅ 收到其他响应,ASR 流程正常")
|
||||
return True
|
||||
else:
|
||||
logger.warning("响应格式不符合预期")
|
||||
return False
|
||||
|
||||
except Exception as json_error:
|
||||
logger.error(f"解析 JSON 响应失败: {json_error}")
|
||||
return False
|
||||
else:
|
||||
logger.error(f"❌ ASR 请求失败: {response.status_code}")
|
||||
logger.error(f"错误内容: {response.text}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.error("❌ 请求超时")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
logger.error("❌ 连接失败,请确保后端服务正在运行")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 请求异常: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("开始测试 ASR 修复...")
|
||||
success = test_asr_endpoint()
|
||||
|
||||
if success:
|
||||
logger.info("🎉 ASR 修复测试成功!")
|
||||
logger.info("现在可以在前端测试录音功能了")
|
||||
else:
|
||||
logger.error("💥 ASR 修复测试失败")
|
||||
sys.exit(1)
|
||||
108
test_dashscope_asr_simple.py
Normal file
108
test_dashscope_asr_simple.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
简单测试 DashScope ASR 功能
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('.')
|
||||
|
||||
import dashscope
|
||||
from dashscope.audio.asr import Transcription
|
||||
from lover.config import settings
|
||||
from lover.oss_utils import upload_audio_file, delete_audio_file
|
||||
import logging
|
||||
|
||||
# 设置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def test_dashscope_asr():
|
||||
"""测试 DashScope ASR 功能"""
|
||||
|
||||
# 设置 API Key
|
||||
dashscope.api_key = settings.DASHSCOPE_API_KEY
|
||||
logger.info(f"DashScope API Key: {settings.DASHSCOPE_API_KEY[:10]}***")
|
||||
|
||||
# 创建测试音频数据(模拟 MP3 文件头)
|
||||
# 这是一个最小的 MP3 文件头,虽然不是真正的音频,但可以测试 API 调用
|
||||
mp3_header = bytes([
|
||||
0xFF, 0xFB, 0x90, 0x00, # MP3 同步字和头信息
|
||||
0x00, 0x00, 0x00, 0x00, # 填充数据
|
||||
]) + b"fake mp3 audio data" * 100
|
||||
|
||||
logger.info(f"创建测试 MP3 数据,大小: {len(mp3_header)} 字节")
|
||||
|
||||
try:
|
||||
# 上传到 OSS
|
||||
logger.info("上传测试音频到 OSS...")
|
||||
file_url = upload_audio_file(mp3_header, "mp3")
|
||||
logger.info(f"上传成功: {file_url}")
|
||||
|
||||
# 验证 URL 格式
|
||||
if not file_url.startswith('https://'):
|
||||
logger.error(f"URL 格式错误: {file_url}")
|
||||
return False
|
||||
|
||||
# 调用 DashScope ASR
|
||||
logger.info("调用 DashScope ASR...")
|
||||
logger.info(f"文件 URL: {file_url}")
|
||||
|
||||
task_response = Transcription.async_call(
|
||||
model='paraformer-v2',
|
||||
file_urls=[file_url],
|
||||
parameters={
|
||||
'format': 'mp3',
|
||||
'sample_rate': 16000,
|
||||
'enable_words': False
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"ASR 任务响应: status_code={task_response.status_code}")
|
||||
logger.info(f"ASR 任务响应: {task_response}")
|
||||
|
||||
if task_response.status_code == 200:
|
||||
task_id = task_response.output.task_id
|
||||
logger.info(f"任务创建成功: {task_id}")
|
||||
|
||||
# 等待结果(简单等待,不处理结果)
|
||||
logger.info("等待 ASR 结果...")
|
||||
try:
|
||||
result = Transcription.wait(task=task_id)
|
||||
logger.info(f"ASR 结果: status_code={result.status_code}")
|
||||
logger.info(f"ASR 结果: {result}")
|
||||
|
||||
if result.status_code == 200:
|
||||
logger.info(f"任务状态: {result.output.task_status}")
|
||||
if result.output.task_status == "FAILED":
|
||||
logger.info(f"失败原因: {getattr(result.output, 'code', 'Unknown')}")
|
||||
logger.info(f"失败消息: {getattr(result.output, 'message', 'Unknown')}")
|
||||
else:
|
||||
logger.error(f"获取结果失败: {result.status_code}")
|
||||
|
||||
except Exception as wait_error:
|
||||
logger.error(f"等待结果失败: {wait_error}")
|
||||
else:
|
||||
logger.error(f"任务创建失败: {task_response.status_code}")
|
||||
if hasattr(task_response, 'message'):
|
||||
logger.error(f"错误消息: {task_response.message}")
|
||||
|
||||
# 清理文件
|
||||
logger.info("清理 OSS 文件...")
|
||||
delete_audio_file(file_url)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"测试失败: {e}")
|
||||
logger.error(f"错误类型: {type(e)}")
|
||||
import traceback
|
||||
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_dashscope_asr()
|
||||
if success:
|
||||
logger.info("🎉 DashScope ASR 测试完成")
|
||||
else:
|
||||
logger.error("💥 DashScope ASR 测试失败")
|
||||
sys.exit(1)
|
||||
127
test_frontend_asr.py
Normal file
127
test_frontend_asr.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
模拟前端发送 ASR 请求
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('.')
|
||||
|
||||
import requests
|
||||
import base64
|
||||
import wave
|
||||
import struct
|
||||
import math
|
||||
import logging
|
||||
|
||||
# 设置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def create_test_mp3_like_data():
|
||||
"""创建模拟 MP3 数据(实际上是简单的音频数据)"""
|
||||
# 创建一些音频数据来模拟前端录音
|
||||
sample_rate = 16000
|
||||
duration = 2 # 2 秒
|
||||
frequency = 300 # 低频音,模拟人声
|
||||
|
||||
# 生成音频样本
|
||||
samples = []
|
||||
for i in range(sample_rate * duration):
|
||||
t = i / sample_rate
|
||||
# 生成复合波形,模拟语音
|
||||
sample1 = math.sin(2 * math.pi * frequency * t)
|
||||
sample2 = 0.5 * math.sin(2 * math.pi * frequency * 2 * t)
|
||||
sample3 = 0.3 * math.sin(2 * math.pi * frequency * 3 * t)
|
||||
|
||||
# 添加包络,模拟语音的动态变化
|
||||
envelope = math.exp(-t * 0.5) * (1 + 0.5 * math.sin(2 * math.pi * 2 * t))
|
||||
|
||||
combined = (sample1 + sample2 + sample3) * envelope
|
||||
sample_int = int(16000 * combined)
|
||||
sample_int = max(-32767, min(32767, sample_int))
|
||||
samples.append(sample_int)
|
||||
|
||||
# 转换为字节数据(模拟 MP3 编码后的数据)
|
||||
audio_bytes = bytearray()
|
||||
for sample in samples:
|
||||
audio_bytes.extend(struct.pack('<h', sample))
|
||||
|
||||
logger.info(f"创建模拟音频数据,大小: {len(audio_bytes)} 字节")
|
||||
return bytes(audio_bytes)
|
||||
|
||||
def test_health_endpoint():
|
||||
"""测试健康检查端点"""
|
||||
url = "http://127.0.0.1:30101/health"
|
||||
|
||||
try:
|
||||
response = requests.get(url, timeout=10)
|
||||
logger.info(f"健康检查响应状态码: {response.status_code}")
|
||||
logger.info(f"健康检查响应内容: {response.text}")
|
||||
return response.status_code == 200
|
||||
except Exception as e:
|
||||
logger.error(f"健康检查失败: {e}")
|
||||
return False
|
||||
|
||||
def test_frontend_asr_request():
|
||||
"""测试前端 ASR 请求"""
|
||||
|
||||
# 创建测试音频数据
|
||||
audio_data = create_test_mp3_like_data()
|
||||
|
||||
# 转换为 base64
|
||||
audio_base64 = base64.b64encode(audio_data).decode('utf-8')
|
||||
logger.info(f"Base64 编码长度: {len(audio_base64)}")
|
||||
|
||||
# 准备请求数据
|
||||
request_data = {
|
||||
'audio_data': audio_base64,
|
||||
'format': 'mp3'
|
||||
}
|
||||
|
||||
# 发送请求到后端
|
||||
url = "http://127.0.0.1:30101/voice/call/asr"
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer test_token' # 使用测试 token
|
||||
}
|
||||
|
||||
logger.info(f"发送 ASR 请求到: {url}")
|
||||
|
||||
try:
|
||||
response = requests.post(url, json=request_data, headers=headers, timeout=60)
|
||||
|
||||
logger.info(f"响应状态码: {response.status_code}")
|
||||
logger.info(f"响应内容: {response.text}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
logger.info(f"✅ ASR 请求成功")
|
||||
logger.info(f"识别结果: {result}")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"❌ ASR 请求失败: {response.status_code}")
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
logger.error("❌ 请求超时")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 请求异常: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("开始测试后端连接...")
|
||||
|
||||
# 先测试健康检查
|
||||
if not test_health_endpoint():
|
||||
logger.error("💥 后端健康检查失败")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info("开始测试前端 ASR 请求...")
|
||||
success = test_frontend_asr_request()
|
||||
|
||||
if success:
|
||||
logger.info("🎉 前端 ASR 请求测试成功")
|
||||
else:
|
||||
logger.error("💥 前端 ASR 请求测试失败")
|
||||
sys.exit(1)
|
||||
62
test_oss_upload.py
Normal file
62
test_oss_upload.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
测试 OSS 上传功能
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('.')
|
||||
|
||||
from lover.oss_utils import test_oss_connection, upload_audio_file, delete_audio_file
|
||||
import logging
|
||||
|
||||
# 设置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def test_oss_upload():
|
||||
"""测试 OSS 上传功能"""
|
||||
|
||||
# 1. 测试连接
|
||||
logger.info("=== 测试 OSS 连接 ===")
|
||||
if not test_oss_connection():
|
||||
logger.error("OSS 连接失败,无法继续测试")
|
||||
return False
|
||||
|
||||
# 2. 创建测试音频数据
|
||||
logger.info("=== 创建测试音频数据 ===")
|
||||
test_audio_data = b"fake audio data for testing" * 1000 # 创建一些测试数据
|
||||
logger.info(f"测试数据大小: {len(test_audio_data)} 字节")
|
||||
|
||||
# 3. 上传测试
|
||||
logger.info("=== 测试上传 ===")
|
||||
try:
|
||||
file_url = upload_audio_file(test_audio_data, "mp3")
|
||||
logger.info(f"上传成功,URL: {file_url}")
|
||||
|
||||
# 验证 URL 格式
|
||||
if file_url.startswith('https://'):
|
||||
logger.info("✅ URL 格式正确")
|
||||
else:
|
||||
logger.error(f"❌ URL 格式错误: {file_url}")
|
||||
return False
|
||||
|
||||
# 4. 删除测试
|
||||
logger.info("=== 测试删除 ===")
|
||||
if delete_audio_file(file_url):
|
||||
logger.info("✅ 删除成功")
|
||||
else:
|
||||
logger.warning("⚠️ 删除失败")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 上传测试失败: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_oss_upload()
|
||||
if success:
|
||||
logger.info("🎉 OSS 上传测试通过")
|
||||
else:
|
||||
logger.error("💥 OSS 上传测试失败")
|
||||
sys.exit(1)
|
||||
142
test_real_audio.py
Normal file
142
test_real_audio.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
使用真实音频文件测试 DashScope ASR
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.append('.')
|
||||
|
||||
import dashscope
|
||||
from dashscope.audio.asr import Transcription
|
||||
from lover.config import settings
|
||||
from lover.oss_utils import upload_audio_file, delete_audio_file
|
||||
import logging
|
||||
import wave
|
||||
import struct
|
||||
|
||||
# 设置日志
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def create_test_wav():
|
||||
"""创建一个简单的 WAV 测试文件"""
|
||||
# 创建 1 秒的 16kHz 单声道 WAV 文件
|
||||
sample_rate = 16000
|
||||
duration = 1 # 1 秒
|
||||
frequency = 440 # A4 音符
|
||||
|
||||
# 生成正弦波
|
||||
samples = []
|
||||
import math
|
||||
for i in range(sample_rate * duration):
|
||||
t = i / sample_rate
|
||||
# 生成简单的正弦波,幅度控制在合理范围内
|
||||
sample = int(16000 * math.sin(2 * math.pi * frequency * t))
|
||||
# 确保在 16-bit 范围内
|
||||
sample = max(-32767, min(32767, sample))
|
||||
samples.append(sample)
|
||||
|
||||
# 写入 WAV 文件
|
||||
wav_file = "test_audio.wav"
|
||||
with wave.open(wav_file, 'wb') as wav:
|
||||
wav.setnchannels(1) # 单声道
|
||||
wav.setsampwidth(2) # 16-bit
|
||||
wav.setframerate(sample_rate) # 16kHz
|
||||
|
||||
# 写入样本数据
|
||||
for sample in samples:
|
||||
wav.writeframes(struct.pack('<h', sample))
|
||||
|
||||
logger.info(f"创建测试 WAV 文件: {wav_file}")
|
||||
return wav_file
|
||||
|
||||
def test_real_audio_asr():
|
||||
"""使用真实音频文件测试 ASR"""
|
||||
|
||||
# 设置 API Key
|
||||
dashscope.api_key = settings.DASHSCOPE_API_KEY
|
||||
logger.info(f"DashScope API Key: {settings.DASHSCOPE_API_KEY[:10]}***")
|
||||
|
||||
# 创建测试 WAV 文件
|
||||
wav_file = create_test_wav()
|
||||
|
||||
try:
|
||||
# 读取 WAV 文件
|
||||
with open(wav_file, 'rb') as f:
|
||||
wav_data = f.read()
|
||||
|
||||
logger.info(f"WAV 文件大小: {len(wav_data)} 字节")
|
||||
|
||||
# 上传到 OSS
|
||||
logger.info("上传 WAV 文件到 OSS...")
|
||||
file_url = upload_audio_file(wav_data, "wav")
|
||||
logger.info(f"上传成功: {file_url}")
|
||||
|
||||
# 调用 DashScope ASR
|
||||
logger.info("调用 DashScope ASR...")
|
||||
|
||||
task_response = Transcription.async_call(
|
||||
model='paraformer-v2',
|
||||
file_urls=[file_url],
|
||||
parameters={
|
||||
'format': 'wav', # 使用 WAV 格式
|
||||
'sample_rate': 16000,
|
||||
'enable_words': False
|
||||
}
|
||||
)
|
||||
|
||||
logger.info(f"ASR 任务响应: status_code={task_response.status_code}")
|
||||
|
||||
if task_response.status_code == 200:
|
||||
task_id = task_response.output.task_id
|
||||
logger.info(f"任务创建成功: {task_id}")
|
||||
|
||||
# 等待结果
|
||||
logger.info("等待 ASR 结果...")
|
||||
try:
|
||||
result = Transcription.wait(task=task_id)
|
||||
logger.info(f"ASR 结果: status_code={result.status_code}")
|
||||
|
||||
if result.status_code == 200:
|
||||
logger.info(f"任务状态: {result.output.task_status}")
|
||||
if result.output.task_status == "FAILED":
|
||||
logger.info(f"失败原因: {getattr(result.output, 'code', 'Unknown')}")
|
||||
logger.info(f"失败消息: {getattr(result.output, 'message', 'Unknown')}")
|
||||
elif result.output.task_status == "SUCCEEDED":
|
||||
logger.info("✅ ASR 识别成功!")
|
||||
logger.info(f"结果: {result.output}")
|
||||
else:
|
||||
logger.error(f"获取结果失败: {result.status_code}")
|
||||
|
||||
except Exception as wait_error:
|
||||
logger.error(f"等待结果失败: {wait_error}")
|
||||
else:
|
||||
logger.error(f"任务创建失败: {task_response.status_code}")
|
||||
|
||||
# 清理文件
|
||||
logger.info("清理文件...")
|
||||
delete_audio_file(file_url)
|
||||
os.remove(wav_file)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"测试失败: {e}")
|
||||
import traceback
|
||||
logger.error(f"错误堆栈: {traceback.format_exc()}")
|
||||
|
||||
# 清理文件
|
||||
try:
|
||||
os.remove(wav_file)
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = test_real_audio_asr()
|
||||
if success:
|
||||
logger.info("🎉 真实音频 ASR 测试完成")
|
||||
else:
|
||||
logger.error("💥 真实音频 ASR 测试失败")
|
||||
sys.exit(1)
|
||||
|
|
@ -455,135 +455,9 @@
|
|||
})
|
||||
}
|
||||
} else {
|
||||
// 备用方案:onFrameRecorded 不工作,通过WebSocket发送完整文件
|
||||
console.warn('⚠️ 未收到音频帧,使用备用方案:通过WebSocket发送完整文件')
|
||||
|
||||
if (!res.tempFilePath) {
|
||||
console.error('❌ 没有录音文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 使用之前成功的文件读取方法
|
||||
let filePath = res.tempFilePath
|
||||
if (!filePath.startsWith('/') && !filePath.includes('://')) {
|
||||
if (typeof plus !== 'undefined' && plus.io) {
|
||||
filePath = plus.io.convertLocalFileSystemURL(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('📁 读取文件:', filePath)
|
||||
const that = this
|
||||
|
||||
if (typeof plus !== 'undefined' && plus.io) {
|
||||
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
|
||||
entry.file((file) => {
|
||||
const reader = new plus.io.FileReader()
|
||||
reader.onload = async (e) => {
|
||||
const dataUrl = e.target.result
|
||||
const base64 = dataUrl.split(',')[1]
|
||||
const binaryString = atob(base64)
|
||||
const bytes = new Uint8Array(binaryString.length)
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i)
|
||||
}
|
||||
|
||||
console.log('✅ 文件读取成功,开始通过WebSocket发送')
|
||||
console.log('📊 音频数据大小:', bytes.length, 'bytes')
|
||||
|
||||
// 检查WebSocket连接状态
|
||||
if (!that.socketTask || that.socketTask.readyState !== 1) {
|
||||
console.error('❌ WebSocket未连接,无法发送音频')
|
||||
uni.showToast({
|
||||
title: 'WebSocket未连接',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 显示处理进度
|
||||
uni.showLoading({
|
||||
title: '识别中...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
try {
|
||||
// 通过WebSocket发送完整音频文件
|
||||
// 分块发送大文件,避免WebSocket消息过大
|
||||
const chunkSize = 8192 // 8KB per chunk
|
||||
const totalChunks = Math.ceil(bytes.length / chunkSize)
|
||||
|
||||
console.log(`📦 将音频分为 ${totalChunks} 个片段发送`)
|
||||
|
||||
for (let i = 0; i < totalChunks; i++) {
|
||||
const start = i * chunkSize
|
||||
const end = Math.min(start + chunkSize, bytes.length)
|
||||
const chunk = bytes.slice(start, end)
|
||||
|
||||
console.log(`📤 发送第 ${i + 1}/${totalChunks} 片,大小: ${chunk.byteLength} bytes`)
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
that.socketTask.send({
|
||||
data: chunk.buffer,
|
||||
success: () => {
|
||||
console.log(`✅ 第 ${i + 1} 片发送成功`)
|
||||
resolve()
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error(`❌ 第 ${i + 1} 片发送失败:`, err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 小延迟避免发送过快
|
||||
if (i < totalChunks - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
}
|
||||
}
|
||||
|
||||
// 发送结束标记,触发ASR处理
|
||||
await new Promise((resolve, reject) => {
|
||||
console.log('📤 发送结束标记 "end"')
|
||||
that.socketTask.send({
|
||||
data: 'end',
|
||||
success: () => {
|
||||
console.log('✅ 结束标记发送成功')
|
||||
resolve()
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('❌ 结束标记发送失败:', err)
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
console.log('🎉 完整音频文件已通过WebSocket发送完成')
|
||||
uni.hideLoading()
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ WebSocket发送失败:', error)
|
||||
uni.hideLoading()
|
||||
uni.showToast({
|
||||
title: '发送失败: ' + error.message,
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
reader.onerror = (error) => {
|
||||
console.error('❌ 文件读取失败:', error)
|
||||
}
|
||||
|
||||
reader.readAsDataURL(file)
|
||||
}, (error) => {
|
||||
console.error('❌ 获取文件失败:', error)
|
||||
})
|
||||
}, (error) => {
|
||||
console.error('❌ 解析文件路径失败:', error)
|
||||
})
|
||||
} else {
|
||||
console.error('❌ plus.io 不可用')
|
||||
}
|
||||
// 新的处理方式:不通过WebSocket,而是在onStop回调中通过HTTP发送
|
||||
console.log('⚠️ 未收到音频帧,将在 onStop 回调中通过 HTTP 发送到 ASR 端点')
|
||||
console.log('⚠️ 不发送 WebSocket 信号,避免触发旧的 finalize_asr 流程')
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -598,6 +472,63 @@
|
|||
console.log('📊 是否收到过音频帧:', hasReceivedFrames)
|
||||
|
||||
this.isRecording = false
|
||||
|
||||
// 处理录音文件
|
||||
if (!res.tempFilePath) {
|
||||
console.error('❌ 没有录音文件')
|
||||
hasReceivedFrames = false // 重置标记
|
||||
frameCount = 0 // 重置计数
|
||||
return
|
||||
}
|
||||
|
||||
console.log('📁 开始处理录音文件:', res.tempFilePath)
|
||||
|
||||
// 使用之前成功的文件读取方法
|
||||
let filePath = res.tempFilePath
|
||||
if (!filePath.startsWith('/') && !filePath.includes('://')) {
|
||||
if (typeof plus !== 'undefined' && plus.io) {
|
||||
filePath = plus.io.convertLocalFileSystemURL(filePath)
|
||||
}
|
||||
}
|
||||
|
||||
console.log('📁 转换后文件路径:', filePath)
|
||||
const that = this
|
||||
|
||||
if (typeof plus !== 'undefined' && plus.io) {
|
||||
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
|
||||
entry.file((file) => {
|
||||
const reader = new plus.io.FileReader()
|
||||
reader.onload = async (e) => {
|
||||
const dataUrl = e.target.result
|
||||
const base64 = dataUrl.split(',')[1]
|
||||
const binaryString = atob(base64)
|
||||
const bytes = new Uint8Array(binaryString.length)
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i)
|
||||
}
|
||||
|
||||
console.log('✅ 文件读取成功,开始发送到ASR')
|
||||
console.log('📊 音频数据大小:', bytes.length, 'bytes')
|
||||
|
||||
// 发送到ASR端点进行处理
|
||||
try {
|
||||
const response = await that.sendAudioToASR(bytes)
|
||||
console.log('✅ ASR处理成功:', response)
|
||||
} catch (error) {
|
||||
console.error('❌ ASR处理失败:', error)
|
||||
}
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}, (error) => {
|
||||
console.error('❌ 文件读取失败:', error)
|
||||
})
|
||||
}, (error) => {
|
||||
console.error('❌ 文件路径解析失败:', error)
|
||||
})
|
||||
} else {
|
||||
console.error('❌ plus.io 不可用')
|
||||
}
|
||||
|
||||
hasReceivedFrames = false // 重置标记
|
||||
frameCount = 0 // 重置计数
|
||||
})
|
||||
|
|
@ -763,8 +694,8 @@
|
|||
duration: 600000, // 10 分钟
|
||||
sampleRate: 16000, // 必须 16kHz,匹配服务器
|
||||
numberOfChannels: 1, // 单声道
|
||||
encodeBitRate: 128000, // 128kbps,适合语音
|
||||
format: 'mp3', // 改用 MP3 格式,可能更稳定
|
||||
encodeBitRate: 64000, // 降低到 64kbps,更适合语音识别
|
||||
format: 'mp3', // 使用 MP3 格式
|
||||
audioSource: 'mic' // 明确指定麦克风作为音频源
|
||||
// 完全移除 frameSize,避免任何实时处理
|
||||
}
|
||||
|
|
@ -1338,6 +1269,73 @@
|
|||
delta: 2,
|
||||
});
|
||||
},
|
||||
// 发送音频到ASR端点进行处理
|
||||
async sendAudioToASR(audioBytes) {
|
||||
console.log('📤 开始发送音频到ASR端点')
|
||||
console.log('📊 音频数据大小:', audioBytes.length, 'bytes')
|
||||
|
||||
// 显示加载提示
|
||||
uni.showLoading({
|
||||
title: '语音识别中...',
|
||||
mask: true
|
||||
})
|
||||
|
||||
try {
|
||||
// 将音频数据转换为base64
|
||||
let base64Audio = ''
|
||||
for (let i = 0; i < audioBytes.length; i++) {
|
||||
base64Audio += String.fromCharCode(audioBytes[i])
|
||||
}
|
||||
base64Audio = btoa(base64Audio)
|
||||
|
||||
console.log('📤 发送ASR请求...')
|
||||
const response = await uni.request({
|
||||
url: this.baseURLPy + '/voice/call/asr',
|
||||
method: 'POST',
|
||||
header: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + uni.getStorageSync("token")
|
||||
},
|
||||
data: {
|
||||
audio_data: base64Audio,
|
||||
format: 'mp3'
|
||||
}
|
||||
})
|
||||
|
||||
console.log('✅ ASR响应:', response)
|
||||
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading()
|
||||
|
||||
if (response.statusCode === 200 && response.data) {
|
||||
const result = response.data
|
||||
console.log('✅ ASR识别结果:', result.text)
|
||||
|
||||
// 显示识别结果
|
||||
uni.showToast({
|
||||
title: `识别: ${result.text}`,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
})
|
||||
|
||||
return result
|
||||
} else {
|
||||
throw new Error(`ASR请求失败: ${response.statusCode}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ ASR请求失败:', error)
|
||||
|
||||
// 隐藏加载提示
|
||||
uni.hideLoading()
|
||||
|
||||
uni.showToast({
|
||||
title: 'ASR识别失败',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
throw error
|
||||
}
|
||||
},
|
||||
goRecharge() {
|
||||
uni.showToast({
|
||||
title: '充值功能开发中',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Windows 本地开发 - 混合架构
|
||||
export const baseURL = 'http://192.168.1.141:30100' // PHP 处理用户管理和界面
|
||||
// export const baseURL = 'http://1.15.149.240:30100' // PHP 处理用户管理和界面
|
||||
export const baseURLPy = 'http://192.168.1.141:30101' // FastAPI 处理 AI 功能
|
||||
export const baseURLPy = 'http://192.168.1.141:30102' // FastAPI 处理 AI 功能 (更新端口)
|
||||
// export const baseURLPy = 'http://1.15.149.240:30101' // FastAPI 处理 AI 功能
|
||||
|
||||
// 远程服务器 - 需要时取消注释
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user