<!DOCTYPE html>
<html lang="zh" xmlns:th="http://www.thymeleaf.org">
<head>
    <th:block th:include="include :: header('腾讯云音色克隆')" />
</head>
<body class="gray-bg">
<div class="container-div">
    <div class="row">
        <div class="col-sm-12 search-collapse">
            <pre><strong>腾讯云音色克隆注意事项：</strong>
1. 本页面复用“腾讯云TTS配置”中的 SecretId、SecretKey、ResourceId。
2. 按腾讯云 SyncDubbing 文档的“示例3 音色克隆自定义属性”调用，同步返回可用 VoiceId。
3. 上传时会同时传入 VoiceProfile，支持自定义名称、性别、年龄、语言、描述。
4. 建议上传 5-20 秒、单人单声道、尽量干净的 wav/mp3/m4a 音频。
5. 克隆成功后会自动把 VoiceId 写入音色管理，voice_source=tx_tts1，可直接用于外呼任务。
6. 如需只截取音频中的一小段做克隆，可填写开始/结束秒数，留空则默认取前 20 秒。</pre>
        </div>

        <div class="col-sm-12 search-collapse">
            <form id="cloneForm">
                <div class="select-list">
                    <ul>
                        <li>
                            <label>音色名称：</label>
                            <input type="text" id="voice_name" name="voice_name" />
                        </li>
                        <li>
                            <label>音频语言：</label>
                            <select id="audio_lang" name="audio_lang">
                                <option value="zh-CN">中文</option>
                                <option value="yue-HK">粤语</option>
                                <option value="en-US">英文</option>
                                <option value="ja-JP">日语</option>
                                <option value="ko-KR">韩语</option>
                            </select>
                        </li>
                        <li>
                            <label>性别：</label>
                            <select id="gender" name="gender">
                                <option value="">默认</option>
                                <option value="female">女声</option>
                                <option value="male">男声</option>
                            </select>
                        </li>
                        <li>
                            <label>年龄：</label>
                            <select id="age" name="age">
                                <option value="">默认</option>
                                <option value="child">儿童</option>
                                <option value="youth">青年</option>
                                <option value="middle">中年</option>
                                <option value="old">老年</option>
                            </select>
                        </li>
                        <li>
                            <label>开始秒数：</label>
                            <input type="number" step="0.1" min="0" id="range_start" name="range_start" placeholder="可留空" />
                        </li>
                        <li>
                            <label>结束秒数：</label>
                            <input type="number" step="0.1" min="0" id="range_end" name="range_end" placeholder="可留空" />
                        </li>
                        <li style="width: 100%">
                            <label>音色描述：</label>
                            <input type="text" id="description" name="description" style="width: 520px" placeholder="可选，例如：温柔亲切的客服女声" />
                        </li>
                        <li>
                            <a class="btn btn-info btn-xs" href="javascript:void(0)" onclick="$.operate.importFile()">
                                <i class="fa fa-upload"></i> &nbsp;上传音频并克隆
                            </a>
                        </li>
                    </ul>
                </div>
            </form>
        </div>

        <div class="col-sm-12 search-collapse">
            <form id="resultForm">
                <div class="select-list">
                    <ul>
                        <li style="width: 100%">
                            <label>返回 VoiceId：</label>
                            <input type="text" id="voiceId" name="voiceId" style="width: 700px" />
                        </li>
                        <li>
                            <label>音色名称：</label>
                            <input type="text" id="savedVoiceName" readonly="readonly" />
                        </li>
                        <li>
                            <label>RequestId：</label>
                            <input type="text" id="requestId" readonly="readonly" />
                        </li>
                    </ul>
                </div>
            </form>
        </div>

        <div class="col-sm-12 search-collapse" id="tts_area" style="display: none">
            <form id="ttsForm">
                <div class="select-list">
                    <ul>
                        <li>
                            <label>测试语言：</label>
                            <select id="text_lang" name="text_lang">
                                <option value="zh-CN">中文</option>
                                <option value="yue-HK">粤语</option>
                                <option value="en-US">英文</option>
                                <option value="ja-JP">日语</option>
                                <option value="ko-KR">韩语</option>
                            </select>
                        </li>
                        <li style="width: 100%">
                            <label>测试文本：</label>
                            <textarea id="tts_text" name="tts_text" cols="80" rows="4" placeholder="请输入在线试听文本"></textarea>
                        </li>
                        <li>
                            <a class="btn btn-primary btn-rounded btn-sm" href="javascript:void(0)" onclick="$.operate.ttsTest()">
                                <i class="fa fa-volume-up"></i> &nbsp;在线试听
                            </a>
                        </li>
                        <li style="width: 100%">
                            <audio id="test_player" controls="controls" style="width: 520px; display: none"></audio>
                        </li>
                    </ul>
                </div>
            </form>
        </div>
    </div>
</div>

<th:block th:include="include :: footer" />
<script th:inline="javascript">
    var prefix = ctx + "aicall/txvoiceclone";

    $.operate.importFile = function() {
        var voiceName = $("#voice_name").val().trim();
        if (voiceName === "") {
            $.modal.msgError("请先填写音色名称");
            $("#voice_name")[0].focus();
            return;
        }

        var rangeStart = $("#range_start").val().trim();
        var rangeEnd = $("#range_end").val().trim();
        if (rangeStart !== "" && rangeEnd !== "" && parseFloat(rangeEnd) <= parseFloat(rangeStart)) {
            $.modal.msgError("结束秒数必须大于开始秒数");
            return;
        }

        var input = document.createElement('input');
        input.type = 'file';
        input.accept = '.wav,.mp3,.m4a,.aac,.ogg';
        input.onchange = function(event) {
            var file = event.target.files[0];
            if (!file) {
                return;
            }
            var formData = new FormData();
            formData.append('file', file);
            formData.append('voice_name', voiceName);
            formData.append('audio_lang', $("#audio_lang").val());
            formData.append('gender', $("#gender").val());
            formData.append('age', $("#age").val());
            formData.append('description', $("#description").val().trim());
            formData.append('range_start', rangeStart);
            formData.append('range_end', rangeEnd);

            $.modal.msg("正在上传音频并调用腾讯云克隆，请稍后...");
            $.ajax({
                url: prefix + "/uploadAndClone",
                type: "POST",
                data: formData,
                processData: false,
                contentType: false,
                success: function(respJson) {
                    if (respJson.code !== 0) {
                        $.modal.msgError(respJson.msg || "腾讯音色克隆失败");
                        return;
                    }
                    var data = respJson.data || {};
                    $("#voiceId").val(data.voiceId || "");
                    $("#savedVoiceName").val(data.voiceName || "");
                    $("#requestId").val(data.requestId || "");
                    $("#tts_area").css("display", "block");
                    $.modal.msgSuccess(respJson.msg || "腾讯音色克隆成功");
                },
                error: function() {
                    $.modal.msgError("腾讯音色克隆失败");
                }
            });
        };
        input.click();
    };

    $.operate.ttsTest = function() {
        if ($.operate.ttsTest.processing === true) {
            $.modal.msg("请等待当前试听完成");
            return;
        }

        var voiceId = $("#voiceId").val().trim();
        if (voiceId === "") {
            $.modal.msgError("请先完成音色克隆并拿到 VoiceId");
            $("#voiceId")[0].focus();
            return;
        }

        var text = $("#tts_text").val().trim();
        if (text === "") {
            $.modal.msgError("请输入测试文本");
            $("#tts_text")[0].focus();
            return;
        }

        $.operate.ttsTest.processing = true;
        var formData = new FormData();
        formData.append('voiceId', voiceId);
        formData.append('text', text);
        formData.append('text_lang', $("#text_lang").val());
        $.ajax({
            url: prefix + "/ttsTest",
            type: "POST",
            data: formData,
            processData: false,
            contentType: false,
            success: function(respJson) {
                if (respJson.code !== 0) {
                    $.modal.msgError(respJson.msg || "腾讯在线试听失败");
                    return;
                }
                var data = respJson.data || {};
                var player = $("#test_player");
                var audioSrc = 'data:' + (data.audioMime || 'audio/wav') + ';base64,' + data.audioBase64;
                player.css("display", "block");
                player.attr("src", audioSrc);
                player[0].play();
                $.modal.msgSuccess(respJson.msg || "腾讯在线试听成功");
            },
            complete: function() {
                $.operate.ttsTest.processing = false;
            },
            error: function() {
                $.operate.ttsTest.processing = false;
                $.modal.msgError("腾讯在线试听失败");
            }
        });
    };
</script>
</body>
</html>
