""" 语音测试相关路由 """ import os import json import tempfile from flask import Blueprint, request, jsonify, render_template, current_app from flask_login import login_required, current_user from app.services.cosyvoice_service import cosyvoice_service from werkzeug.utils import secure_filename import logging logger = logging.getLogger(__name__) voice_test_bp = Blueprint('voice_test', __name__) @voice_test_bp.route('/voice-test') @login_required def voice_test_page(): """语音测试页面""" return render_template('voice_test/index.html') @voice_test_bp.route('/api/voice-test/connection', methods=['POST']) @login_required def test_connection(): """测试CosyVoice服务连接""" try: result = cosyvoice_service.test_connection() return jsonify(result) except Exception as e: logger.error(f"连接测试失败: {str(e)}") return jsonify({ "success": False, "message": f"测试失败: {str(e)}" }) @voice_test_bp.route('/api/voice-test/voices', methods=['GET']) @login_required def get_voices(): """获取可用音色列表""" try: voices = cosyvoice_service.get_available_voices() return jsonify({ "success": True, "voices": voices }) except Exception as e: logger.error(f"获取音色列表失败: {str(e)}") return jsonify({ "success": False, "message": f"获取失败: {str(e)}" }) @voice_test_bp.route('/api/voice-test/generate/preset', methods=['POST']) @login_required def generate_with_preset_voice(): """使用预训练音色生成语音""" try: data = request.get_json() text = data.get('text', '') voice = data.get('voice', '中文女') seed = data.get('seed', 42) speed = data.get('speed', 1.0) if not text: return jsonify({ "success": False, "message": "请输入要合成的文本" }) # 生成语音 stream_audio, full_audio = cosyvoice_service.generate_speech_with_preset_voice( text=text, voice=voice, seed=seed, speed=speed ) if full_audio: return jsonify({ "success": True, "message": "语音生成成功", "audio_url": full_audio, "stream_audio_url": stream_audio }) else: return jsonify({ "success": False, "message": "语音生成失败" }) except Exception as e: logger.error(f"预训练音色生成失败: {str(e)}") return jsonify({ "success": False, "message": f"生成失败: {str(e)}" }) @voice_test_bp.route('/api/voice-test/generate/natural', methods=['POST']) @login_required def generate_with_natural_control(): """使用自然语言控制生成语音""" try: data = request.get_json() text = data.get('text', '') instruction = data.get('instruction', '请用温柔甜美的女声朗读') seed = data.get('seed', 42) if not text: return jsonify({ "success": False, "message": "请输入要合成的文本" }) # 生成语音 stream_audio, full_audio = cosyvoice_service.generate_speech_with_natural_control( text=text, instruction=instruction, seed=seed ) if full_audio: return jsonify({ "success": True, "message": "语音生成成功", "audio_url": full_audio, "stream_audio_url": stream_audio }) else: return jsonify({ "success": False, "message": "语音生成失败" }) except Exception as e: logger.error(f"自然语言控制生成失败: {str(e)}") return jsonify({ "success": False, "message": f"生成失败: {str(e)}" }) @voice_test_bp.route('/api/voice-test/upload-audio', methods=['POST']) @login_required def upload_audio(): """上传音频文件用于语音克隆""" try: if 'audio' not in request.files: return jsonify({ "success": False, "message": "请选择音频文件" }) file = request.files['audio'] if file.filename == '': return jsonify({ "success": False, "message": "请选择音频文件" }) # 检查文件类型 allowed_extensions = {'wav', 'mp3', 'm4a', 'flac'} if not ('.' in file.filename and file.filename.rsplit('.', 1)[1].lower() in allowed_extensions): return jsonify({ "success": False, "message": "不支持的音频格式,请使用WAV、MP3、M4A或FLAC格式" }) # 保存文件到临时目录 filename = secure_filename(file.filename) temp_dir = tempfile.gettempdir() file_path = os.path.join(temp_dir, f"voice_clone_{current_user.id}_{filename}") file.save(file_path) # 尝试识别音频内容 recognized_text = cosyvoice_service.recognize_audio(file_path) return jsonify({ "success": True, "message": "音频上传成功", "file_path": file_path, "recognized_text": recognized_text }) except Exception as e: logger.error(f"音频上传失败: {str(e)}") return jsonify({ "success": False, "message": f"上传失败: {str(e)}" }) @voice_test_bp.route('/api/voice-test/generate/clone', methods=['POST']) @login_required def generate_with_voice_cloning(): """使用语音克隆生成语音""" try: data = request.get_json() text = data.get('text', '') reference_audio_path = data.get('reference_audio_path', '') reference_text = data.get('reference_text', '') seed = data.get('seed', 42) if not text: return jsonify({ "success": False, "message": "请输入要合成的文本" }) if not reference_audio_path or not os.path.exists(reference_audio_path): return jsonify({ "success": False, "message": "请先上传参考音频" }) # 生成语音 stream_audio, full_audio = cosyvoice_service.generate_speech_with_voice_cloning( text=text, reference_audio_path=reference_audio_path, reference_text=reference_text, seed=seed ) if full_audio: return jsonify({ "success": True, "message": "语音克隆成功", "audio_url": full_audio, "stream_audio_url": stream_audio }) else: return jsonify({ "success": False, "message": "语音克隆失败" }) except Exception as e: logger.error(f"语音克隆失败: {str(e)}") return jsonify({ "success": False, "message": f"克隆失败: {str(e)}" }) @voice_test_bp.route('/api/voice-test/random-seed', methods=['GET']) @login_required def get_random_seed(): """获取随机种子""" try: seed = cosyvoice_service.generate_random_seed() return jsonify({ "success": True, "seed": seed }) except Exception as e: logger.error(f"获取随机种子失败: {str(e)}") return jsonify({ "success": False, "message": f"获取失败: {str(e)}" })