Commit a6f49c92 authored by Dazzle Wu's avatar Dazzle Wu

chore: 视频草稿参数调整

parent fab78130
import { AudioConfig, LanType, VoiceType } from '@/store/types/creation' import { AudioConfig, LangType, VoiceType } from '@/store/types/creation'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
function defaultAudioSetting(): AudioConfig { function defaultAudioSetting(): AudioConfig {
return { return {
lanType: LanType.CANTONESE, langType: LangType.CANTONESE,
voiceType: VoiceType.CANTONESE_FEMALE, voiceType: VoiceType.CANTONESE_FEMALE,
} }
} }
...@@ -16,8 +16,8 @@ export const useAudioSettingStore = defineStore('audio-setting-store', { ...@@ -16,8 +16,8 @@ export const useAudioSettingStore = defineStore('audio-setting-store', {
state: (): AudioConfig => getLocalState(), state: (): AudioConfig => getLocalState(),
actions: { actions: {
setLanType(lanType: LanType) { setLanType(langType: LangType) {
this.lanType = lanType this.langType = langType
}, },
setVoiceType(voiceType: VoiceType) { setVoiceType(voiceType: VoiceType) {
......
import { DraftConfig, DriveType, TaskType } from '@/store/types/creation' import { DraftConfig, DriveType, LangType, TaskType } from '@/store/types/creation'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
function defaultDigitalCreation(): DraftConfig { function defaultDigitalCreation(): DraftConfig {
...@@ -37,6 +37,7 @@ function defaultDigitalCreation(): DraftConfig { ...@@ -37,6 +37,7 @@ function defaultDigitalCreation(): DraftConfig {
logoUrl: null, logoUrl: null,
bgmUrl: null, bgmUrl: null,
materialUrl: null, materialUrl: null,
pronunciationLanguageL: LangType.CANTONESE,
} }
} }
...@@ -72,10 +73,22 @@ export const useDigitalCreationStore = defineStore('digital-creation-store', { ...@@ -72,10 +73,22 @@ export const useDigitalCreationStore = defineStore('digital-creation-store', {
this.speed = speed this.speed = speed
}, },
setVolume(volume: string) {
this.volume = volume
},
setPitch(pitch: string) { setPitch(pitch: string) {
this.pitch = pitch this.pitch = pitch
}, },
setWidth(width: number) {
this.width = width
},
setHeight(height: number) {
this.height = height
},
setX(x: number) { setX(x: number) {
this.x = x this.x = x
}, },
......
...@@ -15,9 +15,9 @@ export enum DriveType { ...@@ -15,9 +15,9 @@ export enum DriveType {
VOICE = 'VOICE', VOICE = 'VOICE',
} }
export enum LanType { export enum LangType {
CANTONESE, CANTONESE = 'CANTONESE',
MANDARIN, MANDARIN = 'MANDARIN',
} }
export enum VoiceType { export enum VoiceType {
...@@ -27,7 +27,7 @@ export enum VoiceType { ...@@ -27,7 +27,7 @@ export enum VoiceType {
} }
export interface AudioConfig { export interface AudioConfig {
lanType: LanType langType: LangType
voiceType: VoiceType voiceType: VoiceType
} }
...@@ -110,11 +110,12 @@ export interface TimbreItem { ...@@ -110,11 +110,12 @@ export interface TimbreItem {
iconUrl: string | null iconUrl: string | null
} }
export interface TextScript { export interface AudioSetting {
codec: string codec: string
sampleRate: number sampleRate: number
speed: number speed: number
volume: number volume: number
pitch?: number
voiceType: number voiceType: number
content: string content: string
} }
...@@ -154,6 +155,7 @@ export interface DraftConfig { ...@@ -154,6 +155,7 @@ export interface DraftConfig {
logoUrl: string | null logoUrl: string | null
bgmUrl: string | null bgmUrl: string | null
materialUrl: string | null materialUrl: string | null
pronunciationLanguageL: LangType
memberId?: number memberId?: number
modifiedTime?: string modifiedTime?: string
} }
......
...@@ -3,15 +3,15 @@ import { fetchDigitalHumanTimbreList, fetchTimbreByExample } from '@/apis/digita ...@@ -3,15 +3,15 @@ import { fetchDigitalHumanTimbreList, fetchTimbreByExample } from '@/apis/digita
import { useAudioSettingStore } from '@/store/modules/audio-setting' import { useAudioSettingStore } from '@/store/modules/audio-setting'
import { useDigitalCreationStore } from '@/store/modules/creation' import { useDigitalCreationStore } from '@/store/modules/creation'
import { LanType, TimbreItem, VoiceType } from '@/store/types/creation' import { LangType, TimbreItem, VoiceType } from '@/store/types/creation'
import { computed, onMounted, ref, watch } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import DigitalAudioCard from './digital-audio-card.vue' import DigitalAudioCard from './digital-audio-card.vue'
const audioSettingStore = useAudioSettingStore() const audioSettingStore = useAudioSettingStore()
const digitalCreationStore = useDigitalCreationStore() const digitalCreationStore = useDigitalCreationStore()
const lanList = ref([ const lanList = ref([
{ key: LanType.CANTONESE, label: '粵語' }, { key: LangType.CANTONESE, label: '粵語' },
{ key: LanType.MANDARIN, label: '普通話' }, { key: LangType.MANDARIN, label: '普通話' },
]) ])
const sexValue = ref(0) const sexValue = ref(0)
const sexList = [ const sexList = [
...@@ -25,9 +25,9 @@ const digitalTimbreMaleList = ref<TimbreItem[]>([]) ...@@ -25,9 +25,9 @@ const digitalTimbreMaleList = ref<TimbreItem[]>([])
const showAll = ref(false) const showAll = ref(false)
const searchName = ref('') const searchName = ref('')
const lanType = computed({ const langType = computed({
get() { get() {
return audioSettingStore.lanType return audioSettingStore.langType
}, },
set(value) { set(value) {
audioSettingStore.setLanType(value) audioSettingStore.setLanType(value)
...@@ -43,6 +43,15 @@ const speed = computed({ ...@@ -43,6 +43,15 @@ const speed = computed({
}, },
}) })
const volume = computed({
get() {
return Number(digitalCreationStore.volume)
},
set(value) {
digitalCreationStore.setVolume(String(value))
},
})
const pitch = computed({ const pitch = computed({
get() { get() {
return Number(digitalCreationStore.pitch) return Number(digitalCreationStore.pitch)
...@@ -58,7 +67,7 @@ watch( ...@@ -58,7 +67,7 @@ watch(
if (len && !digitalTimbreValue.value) { if (len && !digitalTimbreValue.value) {
if (digitalCreationStore.person) { if (digitalCreationStore.person) {
digitalTimbreValue.value = digitalTimbreList.value.find((i) => i.timebreId === digitalCreationStore.person) digitalTimbreValue.value = digitalTimbreList.value.find((i) => i.timebreId === digitalCreationStore.person)
lanType.value = LanType.MANDARIN langType.value = LangType.MANDARIN
} else { } else {
digitalTimbreValue.value = digitalTimbreList.value[0] digitalTimbreValue.value = digitalTimbreList.value[0]
} }
...@@ -67,9 +76,9 @@ watch( ...@@ -67,9 +76,9 @@ watch(
) )
watch( watch(
() => lanType.value, () => langType.value,
(newVal) => { (newVal) => {
if (newVal === LanType.CANTONESE) { if (newVal === LangType.CANTONESE) {
audioSettingStore.setVoiceType(VoiceType.CANTONESE_FEMALE) audioSettingStore.setVoiceType(VoiceType.CANTONESE_FEMALE)
} else { } else {
if (digitalTimbreValue.value?.sex === '男') { if (digitalTimbreValue.value?.sex === '男') {
...@@ -116,11 +125,11 @@ function handleClickAudioCard(timbreItem: TimbreItem) { ...@@ -116,11 +125,11 @@ function handleClickAudioCard(timbreItem: TimbreItem) {
<div class="h-full overflow-y-auto px-4 py-2"> <div class="h-full overflow-y-auto px-4 py-2">
<div v-if="!showAll"> <div v-if="!showAll">
<div class="flex justify-end pb-3"> <div class="flex justify-end pb-3">
<HorizontalTabs v-model:value="lanType" :list="lanList" /> <HorizontalTabs v-model:value="langType" :list="lanList" />
</div> </div>
<DigitalAudioCard <DigitalAudioCard
v-if="lanType === LanType.MANDARIN" v-if="langType === LangType.MANDARIN"
:value="digitalTimbreValue" :value="digitalTimbreValue"
show-toggle show-toggle
@toggle="showAll = true" @toggle="showAll = true"
...@@ -133,6 +142,11 @@ function handleClickAudioCard(timbreItem: TimbreItem) { ...@@ -133,6 +142,11 @@ function handleClickAudioCard(timbreItem: TimbreItem) {
<div class="w-10">{{ speed }}</div> <div class="w-10">{{ speed }}</div>
</div> </div>
<div class="mt-4 flex items-center gap-2"> <div class="mt-4 flex items-center gap-2">
<div class="w-12">音量:</div>
<n-slider v-model:value="volume" class="flex-1" :max="15" :min="0" :step="1" />
<div class="w-10">{{ volume }}</div>
</div>
<div v-if="langType === LangType.MANDARIN" class="mt-4 flex items-center gap-2">
<div class="w-12">語調:</div> <div class="w-12">語調:</div>
<n-slider v-model:value="pitch" class="flex-1" :max="15" :min="0" :step="1" /> <n-slider v-model:value="pitch" class="flex-1" :max="15" :min="0" :step="1" />
<div class="w-10">{{ pitch }}</div> <div class="w-10">{{ pitch }}</div>
......
<script setup lang="ts"> <script setup lang="ts">
import { useAudioSettingStore } from '@/store/modules/audio-setting' import { useAudioSettingStore } from '@/store/modules/audio-setting'
import { useDigitalCreationStore } from '@/store/modules/creation' import { useDigitalCreationStore } from '@/store/modules/creation'
import { TextScript, VoiceType } from '@/store/types/creation' import { AudioSetting, LangType, VoiceType } from '@/store/types/creation'
import { computed, onMounted, onUnmounted, ref } from 'vue' import { computed, onMounted, onUnmounted, ref } from 'vue'
let voiceType = VoiceType.CANTONESE_FEMALE
let contentData = ''
let websocket: WebSocket let websocket: WebSocket
const url = 'wss://ai-api-sit.gsstcloud.com/websocket/textToSpeechTC.ws' const url = 'wss://ai-api-sit.gsstcloud.com/websocket/textToSpeechTC.ws'
...@@ -18,6 +16,15 @@ const previewContentWidth = ref(0) ...@@ -18,6 +16,15 @@ const previewContentWidth = ref(0)
const previewContentHeight = ref(0) const previewContentHeight = ref(0)
const previewContent = ref<HTMLElement>() const previewContent = ref<HTMLElement>()
const digitalAudio = ref<HTMLAudioElement>() const digitalAudio = ref<HTMLAudioElement>()
const audioSetting = ref<AudioSetting>({
codec: 'mp3',
sampleRate: 16000,
content: '',
voiceType: VoiceType.CANTONESE_FEMALE,
speed: 5,
volume: 5,
pitch: 5,
})
const resizeObserver = new ResizeObserver((entries) => { const resizeObserver = new ResizeObserver((entries) => {
const { contentRect } = entries[0] const { contentRect } = entries[0]
previewContentWidth.value = contentRect.width previewContentWidth.value = contentRect.width
...@@ -78,17 +85,12 @@ function disconnectWebSocket() { ...@@ -78,17 +85,12 @@ function disconnectWebSocket() {
} }
function sendDataToWebSocket() { function sendDataToWebSocket() {
voiceType = audioSettingStore.voiceType audioSetting.value.content = digitalCreationStore.text
contentData = digitalCreationStore.text audioSetting.value.voiceType = audioSettingStore.voiceType
const payload: TextScript = { audioSetting.value.speed = Number(digitalCreationStore.speed)
codec: 'mp3', audioSetting.value.volume = Number(digitalCreationStore.volume)
sampleRate: 16000, audioSetting.value.pitch = Number(digitalCreationStore.pitch)
speed: Number(digitalCreationStore.speed), websocket.send(JSON.stringify(audioSetting.value))
volume: Number(digitalCreationStore.volume),
voiceType: voiceType,
content: contentData,
}
websocket.send(JSON.stringify(payload))
} }
function generatePreview() { function generatePreview() {
...@@ -101,11 +103,14 @@ function controlAudio() { ...@@ -101,11 +103,14 @@ function controlAudio() {
digitalAudio.value?.pause() digitalAudio.value?.pause()
return return
} }
if (contentData !== digitalCreationStore.text) { if (
audioData.value = '' audioSetting.value.content !== digitalCreationStore.text ||
return audioSetting.value.voiceType !== audioSettingStore.voiceType ||
} audioSetting.value.speed !== Number(digitalCreationStore.speed) ||
if (voiceType !== audioSettingStore.voiceType) { audioSetting.value.volume !== Number(digitalCreationStore.volume) ||
(audioSettingStore.langType === LangType.MANDARIN &&
audioSetting.value.pitch !== Number(digitalCreationStore.pitch))
) {
audioData.value = '' audioData.value = ''
return return
} }
...@@ -146,7 +151,7 @@ function controlAudio() { ...@@ -146,7 +151,7 @@ function controlAudio() {
:loading="isConnected" :loading="isConnected"
:disabled="!digitalCreationStore.text" :disabled="!digitalCreationStore.text"
@click="generatePreview" @click="generatePreview"
>生成预览</n-button >生成預覽</n-button
> >
<CustomIcon <CustomIcon
v-else v-else
......
...@@ -3,13 +3,13 @@ import { createDigitalHumanVideoTask, saveDraftConfig } from '@/apis/digital-cre ...@@ -3,13 +3,13 @@ import { createDigitalHumanVideoTask, saveDraftConfig } from '@/apis/digital-cre
import { fetchUniversalCurrency } from '@/apis/user' import { fetchUniversalCurrency } from '@/apis/user'
import { useDigitalCreationStore } from '@/store/modules/creation' import { useDigitalCreationStore } from '@/store/modules/creation'
import { BaseVideoTask, DraftConfig } from '@/store/types/creation' import { BaseVideoTask, DraftConfig } from '@/store/types/creation'
import { onMounted, onUnmounted, ref } from 'vue' import { onMounted, onUnmounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
const router = useRouter() const router = useRouter()
const digitalCreationStore = useDigitalCreationStore() const digitalCreationStore = useDigitalCreationStore()
const editDraftName = ref(false) const editDraftName = ref(false)
const saveSuccess = ref(false) const autoSaveSuccess = ref(false)
const showExportModal = ref(false) const showExportModal = ref(false)
const ratioValue = ref(720) const ratioValue = ref(720)
const ratioList = [ const ratioList = [
...@@ -22,6 +22,19 @@ const transparent = [ ...@@ -22,6 +22,19 @@ const transparent = [
] ]
let timer: any let timer: any
watch(
() => ratioValue.value,
(newVal) => {
if (newVal === 1080) {
digitalCreationStore.setWidth(1080)
digitalCreationStore.setHeight(1920)
} else {
digitalCreationStore.setWidth(720)
digitalCreationStore.setHeight(1280)
}
},
)
onMounted(() => { onMounted(() => {
timer = setInterval(() => { timer = setInterval(() => {
saveDraft() saveDraft()
...@@ -34,15 +47,15 @@ onUnmounted(() => { ...@@ -34,15 +47,15 @@ onUnmounted(() => {
}) })
// 保存为草稿 // 保存为草稿
async function saveDraft() { async function saveDraft(autoSave: boolean = true) {
const payload: DraftConfig = { const payload: DraftConfig = {
...digitalCreationStore.$state, ...digitalCreationStore.$state,
draftName: digitalCreationStore.draftName, draftName: digitalCreationStore.draftName,
} }
const res = await saveDraftConfig<DraftConfig>(payload) const res = await saveDraftConfig<DraftConfig>(payload)
if (res.code === 0) { if (res.code === 0) {
autoSave ? (autoSaveSuccess.value = true) : window.$message.success('保存成功')
digitalCreationStore.updateDigitalCreation(res.data) digitalCreationStore.updateDigitalCreation(res.data)
saveSuccess.value = true
} }
} }
...@@ -112,11 +125,11 @@ async function getUniversalCurrency() { ...@@ -112,11 +125,11 @@ async function getUniversalCurrency() {
<div class="flex items-center"> <div class="flex items-center">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div v-if="saveSuccess" class="flex items-center gap-2"> <div v-if="autoSaveSuccess" class="flex items-center gap-2">
<CustomIcon class="text-green" icon="ep:success-filled" /> <CustomIcon class="text-green" icon="ep:success-filled" />
<span>已自動保存</span> <span>已自動保存</span>
</div> </div>
<n-button class="!rounded-md" @click="saveDraft"> 保存爲草稿 </n-button> <n-button class="!rounded-md" @click="saveDraft(false)"> 保存爲草稿 </n-button>
<n-button class="!rounded-md" type="info" @click="showExportModal = true"> 導出視頻 </n-button> <n-button class="!rounded-md" type="info" @click="showExportModal = true"> 導出視頻 </n-button>
</div> </div>
</div> </div>
......
<script setup lang="ts"> <script setup lang="ts">
import { fetchDigitalHumanTemplateStatus, fetchDraftConfigById } from '@/apis/digital-creation' import { fetchDigitalHumanTemplateStatus, fetchDraftConfigById } from '@/apis/digital-creation'
import { useDigitalCreationStore } from '@/store/modules/creation' import { useDigitalCreationStore } from '@/store/modules/creation'
import { DigitalTemplate, DraftConfig } from '@/store/types/creation' import { DigitalTemplate, DraftConfig, LangType } from '@/store/types/creation'
import { onMounted } from 'vue' import { onMounted } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import HeaderBar from './header-bar.vue' import HeaderBar from './header-bar.vue'
...@@ -40,8 +40,8 @@ async function getDigitalTemplate(id: number) { ...@@ -40,8 +40,8 @@ async function getDigitalTemplate(id: number) {
inputAudioUrl: digitalTemplate.inputAudioUrl, inputAudioUrl: digitalTemplate.inputAudioUrl,
callbackUrl: digitalTemplate.callbackUrl, callbackUrl: digitalTemplate.callbackUrl,
figureId: digitalTemplate.figureId, figureId: digitalTemplate.figureId,
width: digitalTemplate.videoParams.width || 0, width: 720,
height: digitalTemplate.videoParams.height || 0, height: 1280,
transparent: digitalTemplate.videoParams.transparent ? 'Y' : 'N', transparent: digitalTemplate.videoParams.transparent ? 'Y' : 'N',
cameraId: digitalTemplate.dhParams.cameraId, cameraId: digitalTemplate.dhParams.cameraId,
x: digitalTemplate.dhParams.position.x || 0, x: digitalTemplate.dhParams.position.x || 0,
...@@ -58,6 +58,7 @@ async function getDigitalTemplate(id: number) { ...@@ -58,6 +58,7 @@ async function getDigitalTemplate(id: number) {
logoUrl: digitalTemplate.logoParams?.logoUrl || null, logoUrl: digitalTemplate.logoParams?.logoUrl || null,
bgmUrl: digitalTemplate.bgmParams?.bgmUrl || null, bgmUrl: digitalTemplate.bgmParams?.bgmUrl || null,
materialUrl: digitalTemplate.materialUrl, materialUrl: digitalTemplate.materialUrl,
pronunciationLanguageL: LangType.CANTONESE,
} }
digitalCreationStore.updateDigitalCreation(draftConfig) digitalCreationStore.updateDigitalCreation(draftConfig)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment