ruiyun-human 1.0.13 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ruiyun-human.cjs.js +149 -28
- package/dist/ruiyun-human.es.js +149 -28
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/ruiyun-human.cjs.js
CHANGED
|
@@ -393,15 +393,89 @@ const TTS_MAX_RETRIES = 5;
|
|
|
393
393
|
const _sfc_main = {
|
|
394
394
|
__name: "index",
|
|
395
395
|
props: {
|
|
396
|
-
|
|
397
|
-
type:
|
|
398
|
-
default:
|
|
396
|
+
human: {
|
|
397
|
+
type: Object,
|
|
398
|
+
default: null
|
|
399
399
|
}
|
|
400
400
|
},
|
|
401
401
|
emits: ["loaded", "changed", "paused", "resumed", "audio-ready"],
|
|
402
402
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
403
403
|
const TTS_URL = "http://localhost:6039";
|
|
404
404
|
const ASR_URL = "http://localhost:6039";
|
|
405
|
+
const HUMAN_PRESETS = {
|
|
406
|
+
1: {
|
|
407
|
+
humanId: 1,
|
|
408
|
+
title: "西装动漫男",
|
|
409
|
+
// TODO: 替换为真实资源
|
|
410
|
+
folder: "http://192.168.3.107:8088/img/xiangyu-green-suit",
|
|
411
|
+
folder_mobile: "http://192.168.3.107:8088/img/xiangyu-green-suit-header",
|
|
412
|
+
standby: { interval: 100, standbyNum: 11 },
|
|
413
|
+
speak: { interval: 30, speakNum: 0 },
|
|
414
|
+
humanType: 1,
|
|
415
|
+
timbre: 1
|
|
416
|
+
},
|
|
417
|
+
2: {
|
|
418
|
+
humanId: 2,
|
|
419
|
+
title: "西装动漫女",
|
|
420
|
+
folder: "http://192.168.3.107:8088/img/anime-woman",
|
|
421
|
+
folder_mobile: "http://192.168.3.107:8088/img/anime-woman-header",
|
|
422
|
+
standby: { interval: 100, standbyNum: 1 },
|
|
423
|
+
speak: { interval: 30, speakNum: 75 },
|
|
424
|
+
humanType: 1,
|
|
425
|
+
timbre: 3
|
|
426
|
+
},
|
|
427
|
+
3: {
|
|
428
|
+
humanId: 3,
|
|
429
|
+
title: "真人形象男",
|
|
430
|
+
folder: "http://192.168.3.107:8088/img/real-man",
|
|
431
|
+
folder_mobile: "http://192.168.3.107:8088/img/real-man-header",
|
|
432
|
+
standby: { interval: 100, standbyNum: 1 },
|
|
433
|
+
speak: { interval: 30, speakNum: 139 },
|
|
434
|
+
humanType: 1,
|
|
435
|
+
timbre: 47
|
|
436
|
+
},
|
|
437
|
+
4: {
|
|
438
|
+
humanId: 4,
|
|
439
|
+
title: "真人形象女",
|
|
440
|
+
folder: "http://192.168.3.107:8088/img/real-woman",
|
|
441
|
+
folder_mobile: "http://192.168.3.107:8088/img/real-woman-header",
|
|
442
|
+
standby: { interval: 100, standbyNum: 2 },
|
|
443
|
+
speak: { interval: 40, speakNum: 139 },
|
|
444
|
+
humanType: 1,
|
|
445
|
+
timbre: 34
|
|
446
|
+
},
|
|
447
|
+
5: {
|
|
448
|
+
humanId: 5,
|
|
449
|
+
title: "Q版形象",
|
|
450
|
+
folder: "http://192.168.3.107:8088/img/xiangyu-q",
|
|
451
|
+
folder_mobile: "http://192.168.3.107:8088/img/xiangyu-q-header",
|
|
452
|
+
standby: { interval: 100, standbyNum: 28 },
|
|
453
|
+
speak: { interval: 30, speakNum: 118 },
|
|
454
|
+
humanType: 1,
|
|
455
|
+
timbre: 34
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
const isMobile = () => {
|
|
459
|
+
if (typeof navigator === "undefined") return false;
|
|
460
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|HarmonyOS/i.test(navigator.userAgent);
|
|
461
|
+
};
|
|
462
|
+
const resolveFolder = (human) => {
|
|
463
|
+
const finalType = human.humanType || (isMobile() ? 2 : 1);
|
|
464
|
+
if (finalType === 2 && human.folder_mobile) return human.folder_mobile;
|
|
465
|
+
return human.folder;
|
|
466
|
+
};
|
|
467
|
+
const resolveHuman = (input) => {
|
|
468
|
+
if (input == null) return null;
|
|
469
|
+
const humanId = typeof input === "number" ? input : input.humanId;
|
|
470
|
+
const preset = HUMAN_PRESETS[humanId];
|
|
471
|
+
if (!preset) {
|
|
472
|
+
console.error("[DigitalHuman] 未找到 humanId=", humanId, "的预设形象,可用 id:", Object.keys(HUMAN_PRESETS));
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
const merged = { ...preset, ...typeof input === "object" ? input : {} };
|
|
476
|
+
merged.folder = resolveFolder(merged);
|
|
477
|
+
return merged;
|
|
478
|
+
};
|
|
405
479
|
const props = __props;
|
|
406
480
|
const emit = __emit;
|
|
407
481
|
const containerRef = vue.ref(null);
|
|
@@ -423,27 +497,38 @@ const _sfc_main = {
|
|
|
423
497
|
standbyNum: currentHuman.value.standby.standbyNum,
|
|
424
498
|
speakNum: currentHuman.value.speak.speakNum
|
|
425
499
|
};
|
|
426
|
-
|
|
500
|
+
const makeImage = (url) => {
|
|
427
501
|
const image = new Image();
|
|
428
|
-
image.src = encodeURI(
|
|
429
|
-
|
|
502
|
+
image.src = encodeURI(url);
|
|
503
|
+
image.onload = () => {
|
|
504
|
+
image.__broken = false;
|
|
505
|
+
};
|
|
506
|
+
image.onerror = () => {
|
|
507
|
+
image.__broken = true;
|
|
508
|
+
console.warn("[DigitalHuman] 帧加载失败:", url);
|
|
509
|
+
};
|
|
510
|
+
return image;
|
|
511
|
+
};
|
|
512
|
+
for (let i = 1; i <= standbyNum; i++) {
|
|
513
|
+
frames.value.standby.push(makeImage(`${folder}/standby/standby (${i}).png`));
|
|
514
|
+
}
|
|
515
|
+
if (speakNum <= 0) {
|
|
516
|
+
console.warn("[DigitalHuman] 当前形象 speakNum=0,说话动画暂用待机帧替代");
|
|
517
|
+
frames.value.speakStart = [...frames.value.standby];
|
|
518
|
+
frames.value.speakLoop = [...frames.value.standby];
|
|
519
|
+
frames.value.speakEnd = [...frames.value.standby];
|
|
520
|
+
return;
|
|
430
521
|
}
|
|
431
522
|
const startEnd = Math.floor(speakNum * 0.6);
|
|
432
523
|
const loopEnd = Math.floor(speakNum * 0.9);
|
|
433
524
|
for (let i = 1; i <= startEnd; i++) {
|
|
434
|
-
|
|
435
|
-
image.src = encodeURI(`${folder}/speak/speak (${i}).png`);
|
|
436
|
-
frames.value.speakStart.push(image);
|
|
525
|
+
frames.value.speakStart.push(makeImage(`${folder}/speak/speak (${i}).png`));
|
|
437
526
|
}
|
|
438
527
|
for (let i = startEnd; i <= loopEnd; i++) {
|
|
439
|
-
|
|
440
|
-
image.src = encodeURI(`${folder}/speak/speak (${i}).png`);
|
|
441
|
-
frames.value.speakLoop.push(image);
|
|
528
|
+
frames.value.speakLoop.push(makeImage(`${folder}/speak/speak (${i}).png`));
|
|
442
529
|
}
|
|
443
530
|
for (let i = loopEnd; i <= speakNum; i++) {
|
|
444
|
-
|
|
445
|
-
image.src = encodeURI(`${folder}/speak/speak (${i}).png`);
|
|
446
|
-
frames.value.speakEnd.push(image);
|
|
531
|
+
frames.value.speakEnd.push(makeImage(`${folder}/speak/speak (${i}).png`));
|
|
447
532
|
}
|
|
448
533
|
};
|
|
449
534
|
const draw = (image) => {
|
|
@@ -604,20 +689,38 @@ const _sfc_main = {
|
|
|
604
689
|
};
|
|
605
690
|
allFrames.forEach((img) => {
|
|
606
691
|
if (img.complete) {
|
|
692
|
+
if (!img.naturalWidth) img.__broken = true;
|
|
607
693
|
checkLoaded();
|
|
608
694
|
} else {
|
|
609
|
-
img.onload =
|
|
610
|
-
|
|
695
|
+
img.onload = () => {
|
|
696
|
+
img.__broken = false;
|
|
697
|
+
checkLoaded();
|
|
698
|
+
};
|
|
699
|
+
img.onerror = () => {
|
|
700
|
+
img.__broken = true;
|
|
701
|
+
checkLoaded();
|
|
702
|
+
};
|
|
611
703
|
}
|
|
612
704
|
});
|
|
613
705
|
};
|
|
614
706
|
const init = () => {
|
|
615
707
|
vue.nextTick(() => {
|
|
708
|
+
var _a, _b;
|
|
616
709
|
if (!containerRef.value || !canvasRef.value) return;
|
|
617
|
-
|
|
618
|
-
if (!
|
|
619
|
-
|
|
710
|
+
const rawInput = props.human;
|
|
711
|
+
if (!rawInput) return;
|
|
712
|
+
const human = resolveHuman(rawInput);
|
|
713
|
+
if (!human) return;
|
|
714
|
+
const prevType = (_a = currentHuman.value) == null ? void 0 : _a.humanType;
|
|
715
|
+
if (((_b = currentHuman.value) == null ? void 0 : _b.humanId) === human.humanId && prevType === human.humanType) {
|
|
716
|
+
Object.assign(currentHuman.value, human);
|
|
717
|
+
return;
|
|
620
718
|
}
|
|
719
|
+
abortTTS();
|
|
720
|
+
stop();
|
|
721
|
+
timestamp.value = null;
|
|
722
|
+
frameIndex.value = 1;
|
|
723
|
+
currentHuman.value = human;
|
|
621
724
|
const canvas = canvasRef.value;
|
|
622
725
|
const dpr = window.devicePixelRatio || 1;
|
|
623
726
|
const width = containerRef.value.clientWidth;
|
|
@@ -694,6 +797,7 @@ const _sfc_main = {
|
|
|
694
797
|
return pcmToWavBlob(buf);
|
|
695
798
|
};
|
|
696
799
|
const requestTTSAudio = async (text, signal) => {
|
|
800
|
+
var _a;
|
|
697
801
|
let lastError = null;
|
|
698
802
|
for (let attempt = 1; attempt <= TTS_MAX_RETRIES; attempt++) {
|
|
699
803
|
if (signal == null ? void 0 : signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
@@ -704,7 +808,7 @@ const _sfc_main = {
|
|
|
704
808
|
clientid: VOICE_CLIENT_ID,
|
|
705
809
|
"tenant-id": VOICE_TENANT_ID
|
|
706
810
|
},
|
|
707
|
-
body: JSON.stringify({ text, user: TTS_USER, timbre:
|
|
811
|
+
body: JSON.stringify({ text, user: TTS_USER, timbre: ((_a = currentHuman.value) == null ? void 0 : _a.timbre) ?? 1 }),
|
|
708
812
|
signal
|
|
709
813
|
});
|
|
710
814
|
if (!res.ok) {
|
|
@@ -865,6 +969,27 @@ const _sfc_main = {
|
|
|
865
969
|
});
|
|
866
970
|
}
|
|
867
971
|
};
|
|
972
|
+
vue.watch(
|
|
973
|
+
() => [props.human],
|
|
974
|
+
() => {
|
|
975
|
+
init();
|
|
976
|
+
},
|
|
977
|
+
{ deep: true }
|
|
978
|
+
);
|
|
979
|
+
const switchHuman = (humanId) => {
|
|
980
|
+
const next = resolveHuman(humanId);
|
|
981
|
+
if (!next) return;
|
|
982
|
+
abortTTS();
|
|
983
|
+
stop();
|
|
984
|
+
timestamp.value = null;
|
|
985
|
+
frameIndex.value = 1;
|
|
986
|
+
currentHuman.value = next;
|
|
987
|
+
loadFrames();
|
|
988
|
+
waitFramesLoaded(() => {
|
|
989
|
+
draw(frames.value.standby[0]);
|
|
990
|
+
emit("changed", currentHuman.value);
|
|
991
|
+
});
|
|
992
|
+
};
|
|
868
993
|
__expose({
|
|
869
994
|
standby,
|
|
870
995
|
speak,
|
|
@@ -873,13 +998,9 @@ const _sfc_main = {
|
|
|
873
998
|
resume,
|
|
874
999
|
playTTS,
|
|
875
1000
|
startRecord,
|
|
876
|
-
stopRecord
|
|
1001
|
+
stopRecord,
|
|
1002
|
+
switchHuman
|
|
877
1003
|
});
|
|
878
|
-
vue.watch(() => props.humans, (val) => {
|
|
879
|
-
if (val && val.length > 0 && !currentHuman.value) {
|
|
880
|
-
init();
|
|
881
|
-
}
|
|
882
|
-
}, { deep: true });
|
|
883
1004
|
vue.onMounted(() => {
|
|
884
1005
|
initRecorder();
|
|
885
1006
|
init();
|
|
@@ -905,7 +1026,7 @@ const _sfc_main = {
|
|
|
905
1026
|
};
|
|
906
1027
|
}
|
|
907
1028
|
};
|
|
908
|
-
const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1029
|
+
const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-eb00f641"]]);
|
|
909
1030
|
DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
|
|
910
1031
|
const components = [DemoButton, DemoInput, DigitalHuman];
|
|
911
1032
|
const install = (app) => {
|
package/dist/ruiyun-human.es.js
CHANGED
|
@@ -391,15 +391,89 @@ const TTS_MAX_RETRIES = 5;
|
|
|
391
391
|
const _sfc_main = {
|
|
392
392
|
__name: "index",
|
|
393
393
|
props: {
|
|
394
|
-
|
|
395
|
-
type:
|
|
396
|
-
default:
|
|
394
|
+
human: {
|
|
395
|
+
type: Object,
|
|
396
|
+
default: null
|
|
397
397
|
}
|
|
398
398
|
},
|
|
399
399
|
emits: ["loaded", "changed", "paused", "resumed", "audio-ready"],
|
|
400
400
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
401
401
|
const TTS_URL = "http://localhost:6039";
|
|
402
402
|
const ASR_URL = "http://localhost:6039";
|
|
403
|
+
const HUMAN_PRESETS = {
|
|
404
|
+
1: {
|
|
405
|
+
humanId: 1,
|
|
406
|
+
title: "西装动漫男",
|
|
407
|
+
// TODO: 替换为真实资源
|
|
408
|
+
folder: "http://192.168.3.107:8088/img/xiangyu-green-suit",
|
|
409
|
+
folder_mobile: "http://192.168.3.107:8088/img/xiangyu-green-suit-header",
|
|
410
|
+
standby: { interval: 100, standbyNum: 11 },
|
|
411
|
+
speak: { interval: 30, speakNum: 0 },
|
|
412
|
+
humanType: 1,
|
|
413
|
+
timbre: 1
|
|
414
|
+
},
|
|
415
|
+
2: {
|
|
416
|
+
humanId: 2,
|
|
417
|
+
title: "西装动漫女",
|
|
418
|
+
folder: "http://192.168.3.107:8088/img/anime-woman",
|
|
419
|
+
folder_mobile: "http://192.168.3.107:8088/img/anime-woman-header",
|
|
420
|
+
standby: { interval: 100, standbyNum: 1 },
|
|
421
|
+
speak: { interval: 30, speakNum: 75 },
|
|
422
|
+
humanType: 1,
|
|
423
|
+
timbre: 3
|
|
424
|
+
},
|
|
425
|
+
3: {
|
|
426
|
+
humanId: 3,
|
|
427
|
+
title: "真人形象男",
|
|
428
|
+
folder: "http://192.168.3.107:8088/img/real-man",
|
|
429
|
+
folder_mobile: "http://192.168.3.107:8088/img/real-man-header",
|
|
430
|
+
standby: { interval: 100, standbyNum: 1 },
|
|
431
|
+
speak: { interval: 30, speakNum: 139 },
|
|
432
|
+
humanType: 1,
|
|
433
|
+
timbre: 47
|
|
434
|
+
},
|
|
435
|
+
4: {
|
|
436
|
+
humanId: 4,
|
|
437
|
+
title: "真人形象女",
|
|
438
|
+
folder: "http://192.168.3.107:8088/img/real-woman",
|
|
439
|
+
folder_mobile: "http://192.168.3.107:8088/img/real-woman-header",
|
|
440
|
+
standby: { interval: 100, standbyNum: 2 },
|
|
441
|
+
speak: { interval: 40, speakNum: 139 },
|
|
442
|
+
humanType: 1,
|
|
443
|
+
timbre: 34
|
|
444
|
+
},
|
|
445
|
+
5: {
|
|
446
|
+
humanId: 5,
|
|
447
|
+
title: "Q版形象",
|
|
448
|
+
folder: "http://192.168.3.107:8088/img/xiangyu-q",
|
|
449
|
+
folder_mobile: "http://192.168.3.107:8088/img/xiangyu-q-header",
|
|
450
|
+
standby: { interval: 100, standbyNum: 28 },
|
|
451
|
+
speak: { interval: 30, speakNum: 118 },
|
|
452
|
+
humanType: 1,
|
|
453
|
+
timbre: 34
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
const isMobile = () => {
|
|
457
|
+
if (typeof navigator === "undefined") return false;
|
|
458
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|HarmonyOS/i.test(navigator.userAgent);
|
|
459
|
+
};
|
|
460
|
+
const resolveFolder = (human) => {
|
|
461
|
+
const finalType = human.humanType || (isMobile() ? 2 : 1);
|
|
462
|
+
if (finalType === 2 && human.folder_mobile) return human.folder_mobile;
|
|
463
|
+
return human.folder;
|
|
464
|
+
};
|
|
465
|
+
const resolveHuman = (input) => {
|
|
466
|
+
if (input == null) return null;
|
|
467
|
+
const humanId = typeof input === "number" ? input : input.humanId;
|
|
468
|
+
const preset = HUMAN_PRESETS[humanId];
|
|
469
|
+
if (!preset) {
|
|
470
|
+
console.error("[DigitalHuman] 未找到 humanId=", humanId, "的预设形象,可用 id:", Object.keys(HUMAN_PRESETS));
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
const merged = { ...preset, ...typeof input === "object" ? input : {} };
|
|
474
|
+
merged.folder = resolveFolder(merged);
|
|
475
|
+
return merged;
|
|
476
|
+
};
|
|
403
477
|
const props = __props;
|
|
404
478
|
const emit = __emit;
|
|
405
479
|
const containerRef = ref(null);
|
|
@@ -421,27 +495,38 @@ const _sfc_main = {
|
|
|
421
495
|
standbyNum: currentHuman.value.standby.standbyNum,
|
|
422
496
|
speakNum: currentHuman.value.speak.speakNum
|
|
423
497
|
};
|
|
424
|
-
|
|
498
|
+
const makeImage = (url) => {
|
|
425
499
|
const image = new Image();
|
|
426
|
-
image.src = encodeURI(
|
|
427
|
-
|
|
500
|
+
image.src = encodeURI(url);
|
|
501
|
+
image.onload = () => {
|
|
502
|
+
image.__broken = false;
|
|
503
|
+
};
|
|
504
|
+
image.onerror = () => {
|
|
505
|
+
image.__broken = true;
|
|
506
|
+
console.warn("[DigitalHuman] 帧加载失败:", url);
|
|
507
|
+
};
|
|
508
|
+
return image;
|
|
509
|
+
};
|
|
510
|
+
for (let i = 1; i <= standbyNum; i++) {
|
|
511
|
+
frames.value.standby.push(makeImage(`${folder}/standby/standby (${i}).png`));
|
|
512
|
+
}
|
|
513
|
+
if (speakNum <= 0) {
|
|
514
|
+
console.warn("[DigitalHuman] 当前形象 speakNum=0,说话动画暂用待机帧替代");
|
|
515
|
+
frames.value.speakStart = [...frames.value.standby];
|
|
516
|
+
frames.value.speakLoop = [...frames.value.standby];
|
|
517
|
+
frames.value.speakEnd = [...frames.value.standby];
|
|
518
|
+
return;
|
|
428
519
|
}
|
|
429
520
|
const startEnd = Math.floor(speakNum * 0.6);
|
|
430
521
|
const loopEnd = Math.floor(speakNum * 0.9);
|
|
431
522
|
for (let i = 1; i <= startEnd; i++) {
|
|
432
|
-
|
|
433
|
-
image.src = encodeURI(`${folder}/speak/speak (${i}).png`);
|
|
434
|
-
frames.value.speakStart.push(image);
|
|
523
|
+
frames.value.speakStart.push(makeImage(`${folder}/speak/speak (${i}).png`));
|
|
435
524
|
}
|
|
436
525
|
for (let i = startEnd; i <= loopEnd; i++) {
|
|
437
|
-
|
|
438
|
-
image.src = encodeURI(`${folder}/speak/speak (${i}).png`);
|
|
439
|
-
frames.value.speakLoop.push(image);
|
|
526
|
+
frames.value.speakLoop.push(makeImage(`${folder}/speak/speak (${i}).png`));
|
|
440
527
|
}
|
|
441
528
|
for (let i = loopEnd; i <= speakNum; i++) {
|
|
442
|
-
|
|
443
|
-
image.src = encodeURI(`${folder}/speak/speak (${i}).png`);
|
|
444
|
-
frames.value.speakEnd.push(image);
|
|
529
|
+
frames.value.speakEnd.push(makeImage(`${folder}/speak/speak (${i}).png`));
|
|
445
530
|
}
|
|
446
531
|
};
|
|
447
532
|
const draw = (image) => {
|
|
@@ -602,20 +687,38 @@ const _sfc_main = {
|
|
|
602
687
|
};
|
|
603
688
|
allFrames.forEach((img) => {
|
|
604
689
|
if (img.complete) {
|
|
690
|
+
if (!img.naturalWidth) img.__broken = true;
|
|
605
691
|
checkLoaded();
|
|
606
692
|
} else {
|
|
607
|
-
img.onload =
|
|
608
|
-
|
|
693
|
+
img.onload = () => {
|
|
694
|
+
img.__broken = false;
|
|
695
|
+
checkLoaded();
|
|
696
|
+
};
|
|
697
|
+
img.onerror = () => {
|
|
698
|
+
img.__broken = true;
|
|
699
|
+
checkLoaded();
|
|
700
|
+
};
|
|
609
701
|
}
|
|
610
702
|
});
|
|
611
703
|
};
|
|
612
704
|
const init = () => {
|
|
613
705
|
nextTick(() => {
|
|
706
|
+
var _a, _b;
|
|
614
707
|
if (!containerRef.value || !canvasRef.value) return;
|
|
615
|
-
|
|
616
|
-
if (!
|
|
617
|
-
|
|
708
|
+
const rawInput = props.human;
|
|
709
|
+
if (!rawInput) return;
|
|
710
|
+
const human = resolveHuman(rawInput);
|
|
711
|
+
if (!human) return;
|
|
712
|
+
const prevType = (_a = currentHuman.value) == null ? void 0 : _a.humanType;
|
|
713
|
+
if (((_b = currentHuman.value) == null ? void 0 : _b.humanId) === human.humanId && prevType === human.humanType) {
|
|
714
|
+
Object.assign(currentHuman.value, human);
|
|
715
|
+
return;
|
|
618
716
|
}
|
|
717
|
+
abortTTS();
|
|
718
|
+
stop();
|
|
719
|
+
timestamp.value = null;
|
|
720
|
+
frameIndex.value = 1;
|
|
721
|
+
currentHuman.value = human;
|
|
619
722
|
const canvas = canvasRef.value;
|
|
620
723
|
const dpr = window.devicePixelRatio || 1;
|
|
621
724
|
const width = containerRef.value.clientWidth;
|
|
@@ -692,6 +795,7 @@ const _sfc_main = {
|
|
|
692
795
|
return pcmToWavBlob(buf);
|
|
693
796
|
};
|
|
694
797
|
const requestTTSAudio = async (text, signal) => {
|
|
798
|
+
var _a;
|
|
695
799
|
let lastError = null;
|
|
696
800
|
for (let attempt = 1; attempt <= TTS_MAX_RETRIES; attempt++) {
|
|
697
801
|
if (signal == null ? void 0 : signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
@@ -702,7 +806,7 @@ const _sfc_main = {
|
|
|
702
806
|
clientid: VOICE_CLIENT_ID,
|
|
703
807
|
"tenant-id": VOICE_TENANT_ID
|
|
704
808
|
},
|
|
705
|
-
body: JSON.stringify({ text, user: TTS_USER, timbre:
|
|
809
|
+
body: JSON.stringify({ text, user: TTS_USER, timbre: ((_a = currentHuman.value) == null ? void 0 : _a.timbre) ?? 1 }),
|
|
706
810
|
signal
|
|
707
811
|
});
|
|
708
812
|
if (!res.ok) {
|
|
@@ -863,6 +967,27 @@ const _sfc_main = {
|
|
|
863
967
|
});
|
|
864
968
|
}
|
|
865
969
|
};
|
|
970
|
+
watch(
|
|
971
|
+
() => [props.human],
|
|
972
|
+
() => {
|
|
973
|
+
init();
|
|
974
|
+
},
|
|
975
|
+
{ deep: true }
|
|
976
|
+
);
|
|
977
|
+
const switchHuman = (humanId) => {
|
|
978
|
+
const next = resolveHuman(humanId);
|
|
979
|
+
if (!next) return;
|
|
980
|
+
abortTTS();
|
|
981
|
+
stop();
|
|
982
|
+
timestamp.value = null;
|
|
983
|
+
frameIndex.value = 1;
|
|
984
|
+
currentHuman.value = next;
|
|
985
|
+
loadFrames();
|
|
986
|
+
waitFramesLoaded(() => {
|
|
987
|
+
draw(frames.value.standby[0]);
|
|
988
|
+
emit("changed", currentHuman.value);
|
|
989
|
+
});
|
|
990
|
+
};
|
|
866
991
|
__expose({
|
|
867
992
|
standby,
|
|
868
993
|
speak,
|
|
@@ -871,13 +996,9 @@ const _sfc_main = {
|
|
|
871
996
|
resume,
|
|
872
997
|
playTTS,
|
|
873
998
|
startRecord,
|
|
874
|
-
stopRecord
|
|
999
|
+
stopRecord,
|
|
1000
|
+
switchHuman
|
|
875
1001
|
});
|
|
876
|
-
watch(() => props.humans, (val) => {
|
|
877
|
-
if (val && val.length > 0 && !currentHuman.value) {
|
|
878
|
-
init();
|
|
879
|
-
}
|
|
880
|
-
}, { deep: true });
|
|
881
1002
|
onMounted(() => {
|
|
882
1003
|
initRecorder();
|
|
883
1004
|
init();
|
|
@@ -903,7 +1024,7 @@ const _sfc_main = {
|
|
|
903
1024
|
};
|
|
904
1025
|
}
|
|
905
1026
|
};
|
|
906
|
-
const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1027
|
+
const DigitalHuman = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-eb00f641"]]);
|
|
907
1028
|
DigitalHuman.install = (app) => app.component("DigitalHuman", DigitalHuman);
|
|
908
1029
|
const components = [DemoButton, DemoInput, DigitalHuman];
|
|
909
1030
|
const install = (app) => {
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.demo-btn[data-v-9a48516a]{padding:6px 14px;border-radius:4px;border:none;cursor:pointer;font-size:14px}.primary[data-v-9a48516a]{background:#409eff;color:#fff}.success[data-v-9a48516a]{background:#67c23a;color:#fff}.warning[data-v-9a48516a]{background:#e6a23c;color:#fff}.demo-input-wrap[data-v-28a3493c]{display:inline-flex;align-items:center;border:1px solid #dcdcdc;border-radius:4px;padding:0 10px}.demo-input-wrap[data-v-28a3493c]:focus-within{border-color:#409eff}.demo-input[data-v-28a3493c]{border:none;outline:none;padding:8px 6px;flex:1}.prefix[data-v-28a3493c],.suffix[data-v-28a3493c]{color:#909399}.digital-human[data-v-
|
|
1
|
+
.demo-btn[data-v-9a48516a]{padding:6px 14px;border-radius:4px;border:none;cursor:pointer;font-size:14px}.primary[data-v-9a48516a]{background:#409eff;color:#fff}.success[data-v-9a48516a]{background:#67c23a;color:#fff}.warning[data-v-9a48516a]{background:#e6a23c;color:#fff}.demo-input-wrap[data-v-28a3493c]{display:inline-flex;align-items:center;border:1px solid #dcdcdc;border-radius:4px;padding:0 10px}.demo-input-wrap[data-v-28a3493c]:focus-within{border-color:#409eff}.demo-input[data-v-28a3493c]{border:none;outline:none;padding:8px 6px;flex:1}.prefix[data-v-28a3493c],.suffix[data-v-28a3493c]{color:#909399}.digital-human[data-v-eb00f641]{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.digital-human canvas[data-v-eb00f641]{max-width:100%;max-height:100%}
|