|
|
<template>
|
|
|
<div class="app-container4">
|
|
|
<div class="voice-technology">
|
|
|
<div class="left">
|
|
|
<el-scrollbar height="640px" style="height: 93%;padding: 0 15px;">
|
|
|
<el-row>
|
|
|
<el-col :span="24">
|
|
|
<div class="TaskBackground">
|
|
|
<span style="color: #000000;">任务背景</span>
|
|
|
</div>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<div style="width: calc(100% - 186px);">
|
|
|
<p>
|
|
|
小张临近期末复习,突然手受伤了,写字打字都不是太方便,小张很着急,如何高效复习呢?小张在使用微信发语音过程中,突然想到了可以利用语音技术帮助自己<br />
|
|
|
复习备考,于是他网购了当下流行的小度智能屏,复习过程中遇到不懂的就问小度,重点知识还用手机进行了录音并转成文字整理到一起,大大减少了写字打字的工作量,让我们一起来看看语音技术是如何帮助我们<br />高效学习的吧
|
|
|
</p>
|
|
|
<div class="step">
|
|
|
<div class="step-item">
|
|
|
<span>步骤一</span>
|
|
|
</div>
|
|
|
<span
|
|
|
>小张在使用小度之前,需要让小度记住自己的声音以便唤醒小度。小张在APP上录了两遍“小度小度”,后面使用的时候,不管小张用多大的声音,不管语速快慢,小度都能准确识别,迅速被唤醒,这是为什么?</span
|
|
|
>
|
|
|
</div>
|
|
|
<div class="answer" v-for="item in arrList" :key="item">
|
|
|
<p>
|
|
|
<el-radio
|
|
|
v-model="item.stuanswer"
|
|
|
label="因为小张离的远的时候声音大,离的近的时候声音小"
|
|
|
size="large"
|
|
|
@change="answerChange"
|
|
|
>A.{{ item.optionA }}</el-radio
|
|
|
>
|
|
|
</p>
|
|
|
<p>
|
|
|
<el-radio
|
|
|
v-model="item.stuanswer"
|
|
|
label="因为小张说的不清楚的时候需要慢点,吐字清晰的话快点也没关系"
|
|
|
size="large"
|
|
|
@change="answerChange"
|
|
|
>B.{{ item.optionB }}</el-radio
|
|
|
>
|
|
|
</p>
|
|
|
<p>
|
|
|
<el-radio
|
|
|
v-model="item.stuanswer"
|
|
|
label="因为小张有自己特殊的,不同于任何人的音色,APP采集小张声音,记住了他的音色"
|
|
|
size="large"
|
|
|
@change="answerChange"
|
|
|
>c.{{ item.optionC }}</el-radio
|
|
|
>
|
|
|
</p>
|
|
|
</div>
|
|
|
<el-divider />
|
|
|
<div class="step">
|
|
|
<div class="step-item">
|
|
|
<span>步骤二</span>
|
|
|
</div>
|
|
|
<span
|
|
|
>小张唤醒小度,与小度进行互动问答,小度是如何识别小张的语音的?请探究其中的原理。</span
|
|
|
>
|
|
|
</div>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>2.1 语音信号采集</span
|
|
|
><br />小度可以通过内置麦克风设备,把小张的语音信号转化为电信号,请选择以下哪个最有可能是“小度小度”的语音信号
|
|
|
</p>
|
|
|
<div class="imges-centent" v-for="item in imgesList" :key="item">
|
|
|
<div class="imges-items">
|
|
|
<img :src="getAssetsFile(item.urlA)" alt="" />
|
|
|
<el-radio
|
|
|
v-model="item.studentAnswer"
|
|
|
label="A"
|
|
|
size="large"
|
|
|
@change="signalCahnge"
|
|
|
></el-radio>
|
|
|
</div>
|
|
|
<div class="imges-items">
|
|
|
<img :src="getAssetsFile(item.urlB)" alt="" />
|
|
|
<el-radio
|
|
|
v-model="item.studentAnswer"
|
|
|
label="B"
|
|
|
size="large"
|
|
|
@change="signalCahnge"
|
|
|
></el-radio>
|
|
|
</div>
|
|
|
<div class="imges-items">
|
|
|
<img :src="getAssetsFile(item.urlC)" alt="" />
|
|
|
<el-radio
|
|
|
v-model="item.studentAnswer"
|
|
|
label="C"
|
|
|
size="large"
|
|
|
@change="signalCahnge"
|
|
|
></el-radio>
|
|
|
</div>
|
|
|
<div class="imges-items">
|
|
|
<img :src="getAssetsFile(item.urlD)" alt="" />
|
|
|
<el-radio
|
|
|
v-model="item.studentAnswer"
|
|
|
label="D"
|
|
|
size="large"
|
|
|
@change="signalCahnge"
|
|
|
></el-radio>
|
|
|
</div>
|
|
|
</div>
|
|
|
<p>
|
|
|
我们可以想象经过特征提取后,计算机系统确定了每个字的读音特征,并把特征进行数字化向量化。我们想象用一个图形象化展示每个字的语音特征。
|
|
|
</p>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>2.2 预处理及特征提取</span
|
|
|
><br />
|
|
|
在一项语音识别任务中,我们需要做的第一件事就是数据预处理,不论是开源数据集还是业务数据集,在送入模型真正训练之前必不可少的一步就是将纷繁冗杂的数据整理成特定的文件格式并处理成计算机可识别的二进制特征向量形式储存在指定文件夹下。<br />
|
|
|
这一阶段主要是去除噪音、回声等干扰因素,将不清晰的语音信号变得清晰。此外,预处理阶段还包括分帧操作,即将连续的语音信号分割成一个个短的帧,这样可以更方便地提取语音特征。
|
|
|
</p>
|
|
|
<div class="Featurextraction">
|
|
|
<img
|
|
|
src="@/assets/images/szjj/11112_png.png"
|
|
|
alt=""
|
|
|
v-show="isPlaying !== true"
|
|
|
/>
|
|
|
<img
|
|
|
src="@/assets/images/szjj/11111.gif"
|
|
|
alt=""
|
|
|
v-show="isPlaying !== false"
|
|
|
/>
|
|
|
<audio
|
|
|
id="audio"
|
|
|
src="https://szjj.sztzjy.com:97/4302b582.mp3"
|
|
|
controls
|
|
|
ref="audioElement"
|
|
|
@play="handlePlay"
|
|
|
@pause="handlePause"
|
|
|
@ended="handleEnded"
|
|
|
></audio>
|
|
|
</div>
|
|
|
<p class="btn">
|
|
|
<el-button type="primary" @click="PretreatmentAndFeatureExtraction"
|
|
|
>预处理及特征提取</el-button
|
|
|
>
|
|
|
</p>
|
|
|
<div id="typewriter"></div>
|
|
|
<p>
|
|
|
我们可以想象经过特征提取后,计算机系统确定了每个字的读音特征,并把特征进行数字化向量化。我们想象用一个图形象化展示每个字的语音特征。
|
|
|
</p>
|
|
|
<p class="btn">
|
|
|
<el-button type="primary" @click="drawScatterPlot"
|
|
|
>点击显示形象化语音特征图</el-button
|
|
|
>
|
|
|
</p>
|
|
|
<div class="canvas">
|
|
|
<img
|
|
|
src="@/assets/images/szjj/u191.png"
|
|
|
alt=""
|
|
|
v-if="FeaturesShow !== false"
|
|
|
/>
|
|
|
</div>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>2.3 声学模型</span
|
|
|
><br />
|
|
|
声学模型相当于一个大字典,记录了每个字的发音特征,系统自动计算需要识别的语音每个字的语音特征与库里哪个字的发音匹配度最高,一般一个字可能匹配到库里1-3个匹配度最高的字符
|
|
|
</p>
|
|
|
<p class="btn">
|
|
|
<el-button type="primary" @click="matchingProcess"
|
|
|
>点击显示匹配过程</el-button
|
|
|
>
|
|
|
</p>
|
|
|
<canvas width="632px" height="134px" ref="scatterCanvas"></canvas>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>2.4 语言模型与语言处理</span
|
|
|
><br />
|
|
|
系统识别出匹配度最高的字符之后,需要把识别出的字组成一句话,这就需要语言模型与语言处理,根据语义结构、语法规则、语言的数学描述模型,结合<br />
|
|
|
上下文,最常用的词汇组合等,输出一句话。比如针对“语音识别技术”,系统会根据库中常用词汇组合把“技术”作为一个词汇,而不会输出“语技”这类没有意义的词汇。<br /><br />
|
|
|
请根据上述系统内置的音频,进行语音识别(可用微信的语音转文字功能),输出文字:<br /><br />
|
|
|
<el-input
|
|
|
placeholder=""
|
|
|
style="width: calc(100% - 40%); min-height: 37px"
|
|
|
v-model.trim="Identifyipu"
|
|
|
></el-input>
|
|
|
</p>
|
|
|
<p class="btn btn3">
|
|
|
<el-button type="primary" @click="IdentifyClick">提交</el-button>
|
|
|
</p>
|
|
|
<el-divider />
|
|
|
<div class="step">
|
|
|
<div class="step-item">
|
|
|
<span>步骤三</span>
|
|
|
</div>
|
|
|
<span
|
|
|
>小度识别到小张的语音,并把小张的话转换成了文字进行理解,而且根据自己训练后的强大知识储备库,给出了答案,那小度如何把答案说出来呢?<br />
|
|
|
这就用到了语音合成技术,接下来我们探究语音合成技术的原理吧。</span
|
|
|
>
|
|
|
</div>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>3.1 语言处理</span
|
|
|
>:模拟人对自然语言的理解过程——文本规整、词的切分、语法分析和语义分析,使计算机能够完全理解文本,并给出所需要的发音提示。这相当于去<br /><br />
|
|
|
库里根据文字字符去找对应的发音。汉语按音素的发音特征分类分为辅音、单元音、复元音、复鼻尾音四种,按音节结构分类为声母和韵母。并且由音素构成声母或韵母。有时,将含有声调的韵母称为调母。由单个调母或由声母与调母拼音成为音节。汉语的一个音节就是汉语一个字的音,即音节字。由音节字构成词,最后再由词构成句子。<br /><br />请标出以下文字的拼音:
|
|
|
</p>
|
|
|
<div class="spell">
|
|
|
<div class="spell-item" v-for="item in spellList" :key="item">
|
|
|
<span>{{ item.name }}</span>
|
|
|
<el-input
|
|
|
placeholder=""
|
|
|
style="width: 150px"
|
|
|
v-model.trim="item.value"
|
|
|
@change="spellChange"
|
|
|
></el-input>
|
|
|
</div>
|
|
|
</div>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>3.2 韵律处理</span
|
|
|
>:为合成语音规划出音段特征,如高音、音长和音强等,使合成语音能正确表达语意,听起来更加自然。<br />
|
|
|
比如发音不标准的外国人讲汉语,虽然每个字都不算完全读错,但是整体听起来会觉得一时理解不了,没法正确表达语意,这就是因为声调、读音等的不同导致的。<br /><br />
|
|
|
请选择“我爱中国”的标准配音。
|
|
|
</p>
|
|
|
<p class="dub">
|
|
|
|
|
|
<el-radio
|
|
|
v-model="DubbingSelection"
|
|
|
label="A"
|
|
|
value="A"
|
|
|
size="large"
|
|
|
@change="SelectionChange"
|
|
|
:disabled="DubbingSelection !== ''"
|
|
|
/>
|
|
|
<audio
|
|
|
id="audio"
|
|
|
src="https://szjj.sztzjy.com:97/ttsmaker-file-2024-3-11-10-24-58.mp3"
|
|
|
controls
|
|
|
></audio>
|
|
|
</p>
|
|
|
<p class="dub">
|
|
|
<el-radio
|
|
|
v-model="DubbingSelection"
|
|
|
label="B"
|
|
|
value="B"
|
|
|
size="large"
|
|
|
@change="SelectionChange"
|
|
|
:disabled="DubbingSelection !== ''"
|
|
|
/>
|
|
|
<audio
|
|
|
id="audio"
|
|
|
src="https://szjj.sztzjy.com:97/ttsmaker-file-2024-3-11-10-27-39.mp3"
|
|
|
controls
|
|
|
></audio>
|
|
|
</p>
|
|
|
<p>
|
|
|
<span style="font-weight: 600; font-size: 14px; color: #000000"
|
|
|
>3.3 声学处理</span
|
|
|
>:根据前两部分处理结果的要求输出语音,即合成语音。现在的语音合成可以让用户选择采用男声或者女声,是要欢快的,大气的,温情的等等各种风格,<br /><br />
|
|
|
合成的语音也越来越接近人的自然表达。<br /><br />
|
|
|
请把下面这段文字合成语音:<br />
|
|
|
人工智能的应用领域非常广泛。在医疗领域,人工智能可以辅助医生进行诊断和治疗决策,帮助解决疾病预防和医疗资源管理等问题。在金融领域,人工智能可以用于风险评估、欺诈检测和智能投资等方面。在交通领域,人工智能可以优化交通流量、减少交通事故并提高交通效率。在制造业中,人工智能可以实现智能化生产和预测性维护,提升生产效率和产品质量。
|
|
|
</p>
|
|
|
<p class="tsColor" style="padding-left: 20px">
|
|
|
<el-radio
|
|
|
v-model="synthesisradio"
|
|
|
label="男声"
|
|
|
value="A"
|
|
|
size="large"
|
|
|
@change="synthesischange"
|
|
|
/>
|
|
|
<el-radio
|
|
|
v-model="synthesisradio"
|
|
|
label="女声"
|
|
|
value="B"
|
|
|
size="large"
|
|
|
@change="synthesischange"
|
|
|
/>
|
|
|
</p>
|
|
|
<p class="dub" v-if="synthesisradio == '男声' && Load == 100">
|
|
|
<audio
|
|
|
id="audio"
|
|
|
src="https://szjj.sztzjy.com:97/ttsmaker-file-2024-3-11-10-38-32.mp3"
|
|
|
controls
|
|
|
></audio>
|
|
|
</p>
|
|
|
<p class="dub" v-if="synthesisradio == '女声' && Load == 100">
|
|
|
<audio
|
|
|
id="audio"
|
|
|
src="https://szjj.sztzjy.com:97/ttsmaker-file-2024-3-11-10-39-46.mp3"
|
|
|
controls
|
|
|
></audio>
|
|
|
</p>
|
|
|
<el-progress
|
|
|
:percentage="Load"
|
|
|
:text-inside="true"
|
|
|
:stroke-width="24"
|
|
|
status="success"
|
|
|
style="width: calc(100% - 40%)"
|
|
|
v-if="Load !== 0 && Load !== 100"
|
|
|
/>
|
|
|
<p class="btn btn4">
|
|
|
<el-button type="primary" @click="synthesisbtn">合成</el-button>
|
|
|
</p>
|
|
|
</div>
|
|
|
</el-scrollbar>
|
|
|
<div class="dialog-footer" style="margin-top:10px">
|
|
|
<el-button class="tj" @click="submit">提交任务</el-button>
|
|
|
<el-button class="tj" type="primary" @click="reStart">重新开始</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="right">
|
|
|
<span @click="voiceTechnologyVisible=true"></span>
|
|
|
</div>
|
|
|
<el-dialog
|
|
|
custom-class="gdialog"
|
|
|
v-model="voiceTechnologyVisible"
|
|
|
title="语音"
|
|
|
width="67%"
|
|
|
v-if="voiceTechnologyVisible"
|
|
|
>
|
|
|
<el-scrollbar height="700">
|
|
|
<p>声音的“三要素”指的是响度、音调和音色。</p>
|
|
|
<p>响度:指人耳感觉到的声音强弱,即声音响亮的程度,根据它可以把声音排成由轻到响的序列。响度的大小主要依赖于声强和频率,振幅越大响度越大,人和声源的距离越小,响度越大。<br/>
|
|
|
音调:声音的高低由频率决定,它代表了发声物体在1s内振动的次数,频率越高音调越高,人耳听觉范围20—20000Hz;20Hz以下称为次声波,20000Hz以上称为超声波。<br/>
|
|
|
音色:音色是人们区别具有同样响度、同样音调的两个声音之所以不同的特性,音色与声波的针对波形有关,或者与声音的频谱结构有关。人发出的声音的音色也因人而异,所以我们闭着眼也能听出是哪位熟人在讲话,同样的,<br/>我们也可以听出胡琴、钢琴、笛子、古筝等不同乐器发出的音调、响度都相同的声音。
|
|
|
</p>
|
|
|
<p>语音技术是指在计算机领域中的关键技术有自动语音识别技术(ASR)和语音合成技术(TTS)。</p>
|
|
|
<p>让计算机能听、能看、能说、能感觉,是未来人机交互的发展方向,其中语音成为未来最被看好的人机交互方式,语音比其他的交互方式有更多的优势。<br/>
|
|
|
最早的语音技术因“自动翻译电话”计划而起,包含了语音识别、自然语言理解和语音合成三项非常主要的技术。语音识别的研究工作可以追溯到20世纪50年代AT&T贝尔实验室的Audry系统,此后研究者们逐步突破了大词汇量、连<br/>续语音和非特定人这三大障碍。<br/>
|
|
|
让计算机说话需要用到语音合成技术,其核心是文语转换技术(Text to Speech),语音合成甚至已经应用到汽车的信息系统上,车主可以将下载到系统电脑中的文本文件、电子邮件、网络新闻或小说,转换成语音在车内收听。
|
|
|
</p>
|
|
|
<p><span class="headline">语音识别技术</span></p>
|
|
|
<p>语音识别技术是一项重要的人工智能技术,能够将人类的语音转化为文本或命令。随着人工智能和机器学习的发展,语音识别技术取得了巨大的进步,为人们提供了更智能、便捷和自然的交互方式。</p>
|
|
|
<p>语音识别技术的实现原理及框架如下:<br/>
|
|
|
<span class="headline">1. 语音采集</span><br/>
|
|
|
语音识别系统的第一步是采集语音。语音采集设备可以是手机、麦克风,或者其他的音频输入设备。采集的语音信号被转化为数字信号,供后续处理使用。</p>
|
|
|
<p><span class="headline">2. 预处理</span><br/>
|
|
|
预处理是语音识别的关键步骤之一。这一阶段主要是去除噪音、回声、静默等干扰因素,将不清晰的语音信号变得清晰。此外,预处理阶段还包括分帧操作,即将连续的语音信号分割成一个个短的帧,这样可以更方便地提取语音特征。</p>
|
|
|
<p><span class="headline">3.声学特征提取</span><br/>
|
|
|
模拟的语音信号进行采样得到波形数据之后,首先要输入到特征提取模块,提取出合适的声学特征参数供后续声学模型训练使用。好的声学特征应当考虑以下三个方面 的因素。第一,应当具有比较优秀的区分特性.以使声学模型不同的建模单元可以方便准确的建模。其次,特征提取也可以认为是语音信息的压缩编码过程,既需要将信道、说话人的因素消除保留与内容相关的信息,又需要在不损失过多有用信息的情况下使用尽量低的参数维度,便于高效准确的进行模型的训练。最后,需要考虑鲁棒性,即对环境噪声的抗干扰能力。</p>
|
|
|
<p><span class="headline">4.声学模型</span><br/>
|
|
|
如今主流语音识别系统都采用隐马尔科夫模型(HMM)作为声学模型,这是因为HMM具有很多优良特性。HMM模型的状态跳转模型很适合人类语音的短时平稳特性,可以对不断产生的观测值(语音信号)进行方便的统计建模;与HNN相伴生的动态规划算法可以有效地实现对可变长度的时间序列进行分段和分类的功能;HMM的应用范围广泛。只要选择不同的生成概率密度,离散分布或者连续分布,都可以使用HNM进行建模。HMM以及与之相关的技术在语音识别系统中处于最核心的地位。自从HMM的理论被提出以来(Baum and Easo,1967),它在语音信号处理及相关领域的应用范围变得越来越广泛,在语音识别领域起到核心角色的作用,它还广泛活跃精音的参数合成、语言理解、 机器翻译等其他领域。</p>
|
|
|
<p>以汉语为例:<br/>
|
|
|
汉语按音素的发音特征分类分为辅音、单元音、复元音、复鼻尾音四种,按音节结构分类为声母和韵母。并且由音素构成声母或韵母。有时,将含有声调的韵母称为调母。由单个调母或由声母与调母拼音成为音节。汉语的一个音节就是汉语一个字的音,即音节字。由音节字构成词,最后再由词构成句子。
|
|
|
汉语声母共有22个,其中包括零声母,韵母共有38个。按音素分类,汉语辅音共有22个,单元音13个,复元音13个,复鼻尾音16个。
|
|
|
目前常用的声学模型基元为声韵母、音节或词,根据实现目的不同来选取不同的基元。汉语加上语气词共有412个音节,包括轻音字,共有1282个有调音节字,所以当在小词汇表孤立词语音识别时常选用词作为基元,在大词汇表语音识别时常采用音节或声韵母建模,而在连续语音识别时,由于协同发音的影响,常采用声韵母建模。
|
|
|
基于统计的语音识别模型常用的就是HMM模型λ(N,M,π,A,B),涉及到HMM模型的相关理论包括模型的结构选取、模型的初始化、模型参数的重估以及相应的识别算法等</p>
|
|
|
<p><span class="headline">5.语言模型与语言处理</span><br/>
|
|
|
语言模型包括由识别语音命令构成的语法网络或由统计方法构成的语言模型,语言处理可以进行语法、语义分析。
|
|
|
语言模型对中、大词汇量的语音识别系统特别重要。当分类发生错误时可以根据语言学模型、语法结构、语义学进行判断纠正,特别是一些同音字则必须通过上下文结构才能确定词义。语言学理论包括语义结构、语法规则、语言的数学描述模型等有关方面。目前比较成功的语言模型通常是采用统计语法的语言模型与基于规则语法结构命令语言模型。语法结构可以限定不同词之间的相互连接关系,减少了识别系统的搜索空间,这有利于提高系统的识别</p>
|
|
|
<p>语音识别流程如下所示</p>
|
|
|
<p><img src="@/assets/images/szjj/u436.png" alt=""></p>
|
|
|
<p>标准的语音预处理流程</p>
|
|
|
<p><img src="@/assets/images/szjj/u443.png" alt=""></p>
|
|
|
<p>
|
|
|
FBank特征<br/>
|
|
|
Mel刻度,这是一个能模拟人耳接收声音规律的刻度,人耳在接收声音时呈现非线性状态,对高频的更不敏感,因此Mel刻度在低频区分辨度较高。Mel滤波器组就是一系列的三角形滤波器,把低频区的声音更好的提取出来,同时对高频区的声音进行一定的降噪。
|
|
|
</p>
|
|
|
<p>一个例子</p>
|
|
|
<p>input: 一段3秒长的音频 ,8K的采样率</p>
|
|
|
<p><img src="@/assets/images/szjj/u440.png" alt=""></p>
|
|
|
<p>原始语音向量 shape (3.5*8000, )</p>
|
|
|
<p>output:FBank特征</p>
|
|
|
<p><img src="@/assets/images/szjj/u446.png" alt=""></p>
|
|
|
<p>经过预处理后的向量 shape (350, 40)</p>
|
|
|
<p>
|
|
|
语音合成技术<br/>
|
|
|
在讲语音合成之前,我们首先要弄清楚一个问题:什么是语音,语音是由哪些成分构成的?<br/>
|
|
|
语音就是人说的话,它的记录形式是一段一段的波形。<br/>
|
|
|
语音有三大关键成分:信息音色和韵律。如果能将语音的成分充分自由地拆解和组合,将是对语音的巨大解放,未来将有无限的想象发展空间。<br/>
|
|
|
下面我们将它做一个拆解,先来看下语音信息,语音信息是指说话人说了什么内容。<br/>
|
|
|
语音作为信息的载体,是最自然便捷的沟通手段,在信息播报、人机交互上有着大量刚性需求,现在如此未来更是如此。<br/>
|
|
|
语音的音色是指这段话是谁说的,有一句俗语:未见其人先闻其声。<br/>
|
|
|
有时候说什么并不重要,更重要的是谁在说。班主任和大家分享一个伤感又有点温情的故事,一位身患肺癌的父亲在他生命的最后阶段联系了讯飞,他想把自己的声音做成一个音库,放置在智能音箱里面。在他逝世后,可以让智能音箱陪着他孩子长大,让孩子还能听到爸爸的声音。<br/>
|
|
|
韵律就是我们说话的方式,说话时我们声音的高低、快慢等。<br/>
|
|
|
借用某位非著名相声演员的话来说:人人都会说话,为什么你还要花钱来听我说话?<br/>
|
|
|
自然是因为你说的好,那【说得好】到底是一种什么概念呢?<br/>
|
|
|
举个例子,每次看《动物世界》除了被节目里各种新奇的动物吸引之外,还会被赵忠祥老师富有磁性的声音所吸引。<br/>
|
|
|
这就是他比旁人【说得好】,声音的惯性和魅力是无穷的,当某个熟悉的声音响起,人们就会自然而然的沉浸其中。<br/>
|
|
|
在未来,优质的声音IP将会作为重要的内容生产能力,受到重视和追捧,将语音从信息获取升级为艺术享受。
|
|
|
</p>
|
|
|
<p>语音合成核心技术原理</p>
|
|
|
<p>1、单元挑选波形拼接技术</p>
|
|
|
<p>
|
|
|
语音合成技术的本质是将文本信息转化成语音信息,在了解这项技术之前,我们先来看一个案例,现在有一句待合成文本:外交部评日本首相国会演说。<br/>
|
|
|
如果我们要将这句文本信息变成语音信息,首先需要在语音合成数据库里面挑选出这句文本信息所包含的元素,比如:外交部、日本等。<br/>
|
|
|
挑选完元素之后将这些元素按照一定的顺序组合排列,最后再输出我们想要合成的那句语音信息。
|
|
|
</p>
|
|
|
<p><img src="@/assets/images/szjj/u455.png" alt=""></p>
|
|
|
<p>以上这些就是一个简单的单元挑选波形拼接技术实现过程。<br/>单元挑选和波形拼接的关键技术点有2点:语料库设计和标注;目标代价和连接代价计算</p>
|
|
|
<p>2、基于HMM的参数语音合成</p>
|
|
|
<p>基于HMM的参数语音合成技术相比于单元挑选波形拼接技术,在操作层面上会更加流程化。<br/>我们来看下基于HMM的训练流程图,主要包括训练流程和合成流程。</p>
|
|
|
<p><img src="@/assets/images/szjj/u458.png" alt=""></p>
|
|
|
<p>将录制好的音库,提取出相应的语音参数,然后将标注数据和声学提取数据一同构建HMM的训练模型,通过上下文属性和问题集的决策树模型,构建训练后的HMM模型,这<br/>就是训练流程。</p>
|
|
|
<p>合成流程中我们通过对输入文本的分析,来进行上下文相关HMM训练的序列决策,再将生成后的语音送入参数合成器中,最后输出合成之后的语音。<br/>基于HMM的参数语音合成的关键技术有高质量语音声码器,以及基于上下文的决策树模型。</p>
|
|
|
</el-scrollbar>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<script setup>
|
|
|
// import { getUserId } from "@/utils/auth.js";
|
|
|
import { getUserInfo } from '@/utils/auth'
|
|
|
import * as commonality from "@/api/commonality";
|
|
|
import { getAssetsFile } from "@/utils/index.js";
|
|
|
import { onMounted, ref } from "vue";
|
|
|
const { proxy } = getCurrentInstance();
|
|
|
const voiceTechnologyVisible = ref(false);
|
|
|
const imgesList = ref([
|
|
|
{
|
|
|
id: 1,
|
|
|
urlA: "szjj/u478.png",
|
|
|
urlB: "szjj/u479.png",
|
|
|
urlC: "szjj/u480.png",
|
|
|
urlD: "szjj/u481.png",
|
|
|
studentAnswer: "",
|
|
|
},
|
|
|
]);
|
|
|
const spellList = ref([
|
|
|
{
|
|
|
id: 1,
|
|
|
name: "我",
|
|
|
value: "",
|
|
|
pinyin: "wo",
|
|
|
},
|
|
|
{
|
|
|
id: 2,
|
|
|
name: "爱",
|
|
|
value: "",
|
|
|
pinyin: "ai",
|
|
|
},
|
|
|
{
|
|
|
id: 3,
|
|
|
name: "中",
|
|
|
value: "",
|
|
|
pinyin: "zhong",
|
|
|
},
|
|
|
{
|
|
|
id: 4,
|
|
|
name: "国",
|
|
|
value: "",
|
|
|
pinyin: "guo",
|
|
|
},
|
|
|
]);
|
|
|
const arrList = ref([]);
|
|
|
const audioElement = ref(null);
|
|
|
let isPlaying = ref(false);
|
|
|
const Identifyipu = ref("");
|
|
|
const DubbingSelection = ref('');
|
|
|
const synthesisradio = ref("");
|
|
|
const Load = ref(0);
|
|
|
// 成功计数
|
|
|
const successCount = ref(0);
|
|
|
// 失败计数
|
|
|
const errorCount = ref(0);
|
|
|
const flowobj = ref({
|
|
|
featureExtraction: 0,
|
|
|
speechFeatureMap: 0,
|
|
|
matching: 0,
|
|
|
inputPinyin: "",
|
|
|
inputText: "",
|
|
|
standardDubbing: "",
|
|
|
synthesizedSpeech: 0,
|
|
|
voiceSignalAcquisition: "",
|
|
|
userId: JSON.parse(getUserInfo()).userId,
|
|
|
});
|
|
|
const EchoState = ref(false);
|
|
|
const restartState = ref(false);
|
|
|
const interval=ref(null);
|
|
|
const answerChange = () => {
|
|
|
// 选择答案
|
|
|
successCount.value = 1;
|
|
|
commonality
|
|
|
.submitVoice({ module: "语音技术", selection: arrList.value[0].stuanswer })
|
|
|
.then((res) => {
|
|
|
proxy.$modal.msgSuccess("回答正确");
|
|
|
restartState.value = false;
|
|
|
})
|
|
|
.catch((err) => {
|
|
|
errorCount.value = 1;
|
|
|
});
|
|
|
};
|
|
|
const signalCahnge = () => {
|
|
|
if (successCount.value < 1) {
|
|
|
imgesList.value[0].studentAnswer = "";
|
|
|
proxy.$modal.msgWarning("请先完成步骤一");
|
|
|
return;
|
|
|
}
|
|
|
let answer = "A";
|
|
|
flowobj.value.voiceSignalAcquisition = imgesList.value[0].studentAnswer;
|
|
|
if (imgesList.value[0].studentAnswer == answer) {
|
|
|
proxy.$modal.msgSuccess("回答正确");
|
|
|
} else {
|
|
|
errorCount.value = 2;
|
|
|
proxy.$modal.msgWarning("回答错误");
|
|
|
}
|
|
|
successCount.value = 2;
|
|
|
};
|
|
|
const handlePlay = () => {
|
|
|
isPlaying.value = true;
|
|
|
if (successCount.value < 2) {
|
|
|
proxy.$modal.msgWarning("请先完成步骤二的语音信号采集");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
successCount.value = 3;
|
|
|
// 在这里执行音频开始播放时的逻辑
|
|
|
};
|
|
|
const handleEnded = () => {
|
|
|
isPlaying.value = false;
|
|
|
};
|
|
|
const handlePause = () => {
|
|
|
isPlaying.value = false;
|
|
|
};
|
|
|
const PretreatmentAndFeatureExtraction = () => {
|
|
|
//预处理及特征提取
|
|
|
if (successCount.value < 3) {
|
|
|
proxy.$modal.msgWarning("请先完成以上步骤");
|
|
|
return;
|
|
|
}
|
|
|
flowobj.value.featureExtraction = 1;
|
|
|
successCount.value = 4;
|
|
|
const text = `[[0.0, 0.46952675, 0.93905351, ..., 0.0, 0.0],
|
|
|
[0.0, 0.0, ..., 0.0, 0.0],
|
|
|
[0.0, 0.0, ..., 0.0, 0.0],
|
|
|
...,
|
|
|
[0.0, 0.0, ..., 0.0, 0.0],
|
|
|
[0.0, 0.0, ..., 0.146500797, 0.07325398, 0.0]]`;
|
|
|
const typewriter = document.getElementById("typewriter");
|
|
|
let index = 0;
|
|
|
interval.value = setInterval(type, 100); // 100毫秒间隔,您可以根据需要调整这个值
|
|
|
function type() {
|
|
|
if (index < text.length) {
|
|
|
typewriter.textContent += text.charAt(index);
|
|
|
index++;
|
|
|
} else {
|
|
|
clearInterval(interval.value); // 当文本全部打印完毕后,清除定时器
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
const scatterPlotCanvas = ref(null);
|
|
|
const scatterCanvas = ref(null);
|
|
|
let timerCount = ref(0);
|
|
|
const FeaturesShow = ref(false);
|
|
|
function getTopMatches() {
|
|
|
scatterPlotCanvas.value.width = 632;
|
|
|
scatterPlotCanvas.value.height = 134;
|
|
|
return [
|
|
|
{
|
|
|
x: Math.random() * scatterPlotCanvas.value.width,
|
|
|
y: Math.random() * scatterPlotCanvas.value.height,
|
|
|
},
|
|
|
];
|
|
|
}
|
|
|
const drawScatterPlot = () => {
|
|
|
//点击显示形象化语音特征图
|
|
|
if (successCount.value < 4) {
|
|
|
proxy.$modal.msgError("请先完成以上步骤");
|
|
|
return;
|
|
|
}
|
|
|
flowobj.value.speechFeatureMap = 1;
|
|
|
successCount.value = 5;
|
|
|
FeaturesShow.value = true;
|
|
|
};
|
|
|
let timer = ref(null);
|
|
|
// 绘制散点图 scatterCanvas
|
|
|
const matchingProcess = () => {
|
|
|
if (successCount.value < 5) {
|
|
|
proxy.$modal.msgError("请先完成以上步骤");
|
|
|
return;
|
|
|
}
|
|
|
flowobj.value.matching = 1;
|
|
|
successCount.value = 6;
|
|
|
timer.value = setInterval(Execution, 1000);
|
|
|
};
|
|
|
// 匹配过程
|
|
|
const Execution = () => {
|
|
|
// 先打印当前值,然后再递增
|
|
|
timerCount.value++;
|
|
|
if (timerCount.value > 2) {
|
|
|
clearInterval(timer.value);
|
|
|
return; // 如果超过了10次,就停止执行后续代码
|
|
|
}
|
|
|
const ctx = scatterCanvas.value.getContext("2d");
|
|
|
const colors = ["#2975a2", "#5bac7e", "#f7cc70", "#ea629f"];
|
|
|
scatterCanvas.value.width = 632;
|
|
|
scatterCanvas.value.height = 134;
|
|
|
ctx.clearRect(0, 0, scatterCanvas.value.width, scatterCanvas.value.height);
|
|
|
const numberOfPointsPerColor = 10;
|
|
|
const minRadius = 2;
|
|
|
const maxRadius = 11;
|
|
|
function getRandomInt(min, max) {
|
|
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
|
}
|
|
|
colors.forEach((color, index) => {
|
|
|
for (let i = 0; i < numberOfPointsPerColor; i++) {
|
|
|
const x = getRandomInt(minRadius, scatterCanvas.value.width - minRadius);
|
|
|
const y = getRandomInt(minRadius, scatterCanvas.value.height - minRadius);
|
|
|
const radius = getRandomInt(minRadius, maxRadius);
|
|
|
if (
|
|
|
x + radius > scatterCanvas.value.width ||
|
|
|
y + radius > scatterCanvas.value.height
|
|
|
) {
|
|
|
// 如果点超出了边界,则重新生成
|
|
|
i--; // 减少循环计数器,以便重新尝试生成点
|
|
|
continue;
|
|
|
}
|
|
|
ctx.beginPath();
|
|
|
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
|
|
ctx.fillStyle = color;
|
|
|
ctx.fill();
|
|
|
ctx.closePath();
|
|
|
}
|
|
|
});
|
|
|
getTopMatches().forEach((match, index) => {
|
|
|
console.log(scatterCanvas.value.width, "scatterCanvas.value.width");
|
|
|
// 确保match.x和match.y在canvas范围内
|
|
|
const matchX = Math.max(
|
|
|
minRadius,
|
|
|
Math.min(match.x, scatterCanvas.value.width - minRadius)
|
|
|
);
|
|
|
const matchY = Math.max(
|
|
|
minRadius,
|
|
|
Math.min(match.y, scatterCanvas.value.height - minRadius)
|
|
|
);
|
|
|
const radius = getRandomInt(minRadius, maxRadius);
|
|
|
ctx.beginPath();
|
|
|
ctx.arc(matchX, matchY, radius, 0, Math.PI * 2);
|
|
|
ctx.fillStyle = "black";
|
|
|
ctx.fill();
|
|
|
ctx.closePath();
|
|
|
});
|
|
|
};
|
|
|
const IdentifyClick = () => {
|
|
|
function removePunctuation(text) {
|
|
|
// 移除标点符号,只保留字母和数字
|
|
|
return text.replace(/[^\w\s]/g, "");
|
|
|
}
|
|
|
function normalizeText(text) {
|
|
|
// 去空格
|
|
|
return removePunctuation(text.replace(/\s+/g, "")).toLowerCase();
|
|
|
}
|
|
|
const originalText =
|
|
|
"今天,我们在这里召开学校思想政治理论课教师座谈会。参加会议的主要是大中小学思政课一线教师。首先,我向在座各位老师,向全国大中小学思政课教师,致以诚挚的问候!古人说:“敬教劝学,建国之大本;兴贤育才,为政之先务。”教育是民族振兴、社会进步的重要基石,是功在当代、利在千秋的德政工程,对提高人民综合素质、促进人的全面发展、增强中华民族创新创造活力、实现中华民族伟大复兴具有决定性意义。青少年是祖国的未来、民族的希望。现在,我国各级各类学历教育在校生达到2.7亿,全国各类高等教育在学总规模达到3779万人。青少年阶段是人生的“拔节孕穗期”,这一时期心智逐渐健全,思维进入最活跃状态,最需要精心引导和栽培。“蒙以养正,圣功也。”就是说青少年教育最重要的是教给他们正确的思想,引导他们走正路。思政课是落实立德树人根本任务的关键课程,思政课作用不可替代,思政课教师队伍责任重大。";
|
|
|
if (successCount.value < 6) {
|
|
|
proxy.$modal.msgError("请先完成以上步骤");
|
|
|
return;
|
|
|
}
|
|
|
flowobj.value.inputText = Identifyipu.value;
|
|
|
successCount.value = 7;
|
|
|
// 对原始文本和用户输入进行规范化处理
|
|
|
const normalizedOriginalText = normalizeText(originalText);
|
|
|
const normalizedUserInput = normalizeText(Identifyipu.value);
|
|
|
if (normalizedOriginalText === normalizedUserInput) {
|
|
|
proxy.$modal.msgSuccess("语音识别正确");
|
|
|
} else {
|
|
|
proxy.$modal.msgWarning("语音识别错误");
|
|
|
errorCount.value = 3;
|
|
|
}
|
|
|
};
|
|
|
// 拼音触发
|
|
|
const spellChange = () => {
|
|
|
if (successCount.value < 7) {
|
|
|
proxy.$modal.msgWarning("请先步骤二");
|
|
|
spellList.value.forEach((item, index) => {
|
|
|
item.value = "";
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
successCount.value = 8;
|
|
|
const isAllFilled = spellList.value.every((item) => item.value);
|
|
|
if (isAllFilled) {
|
|
|
// 正则校验不能输入中文
|
|
|
const isAllCorrect = spellList.value.every((item) => {
|
|
|
if (containsChinese(item.value)) {
|
|
|
item.value = "";
|
|
|
return proxy.$modal.msgWarning("不能输入中文");
|
|
|
} else {
|
|
|
return item.value === item.pinyin;
|
|
|
}
|
|
|
});
|
|
|
if (isAllCorrect) {
|
|
|
proxy.$modal.msgSuccess("拼音输入正确");
|
|
|
} else {
|
|
|
errorCount.value = 4;
|
|
|
proxy.$modal.msgWarning("拼音输入错误");
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
function containsChinese(str) {
|
|
|
return /[\u4e00-\u9fa5]/.test(str);
|
|
|
}
|
|
|
const SelectionChange = () => {
|
|
|
if (successCount.value < 8) {
|
|
|
DubbingSelection.value = "";
|
|
|
proxy.$modal.msgWarning("请先完成以上步骤");
|
|
|
return;
|
|
|
}
|
|
|
flowobj.value.standardDubbing = DubbingSelection.value;
|
|
|
if (DubbingSelection.value === "A") {
|
|
|
proxy.$modal.msgSuccess("选择正确");
|
|
|
} else {
|
|
|
errorCount.value = 5;
|
|
|
proxy.$modal.msgWarning("选择错误");
|
|
|
}
|
|
|
successCount.value = 9;
|
|
|
};
|
|
|
const synthesischange = () => {
|
|
|
Load.value = 0;
|
|
|
};
|
|
|
// 合成
|
|
|
const synthesisbtn = () => {
|
|
|
if (successCount.value < 9) {
|
|
|
proxy.$modal.msgWarning("请先完成以上步骤");
|
|
|
return;
|
|
|
}
|
|
|
if (synthesisradio.value === "") {
|
|
|
proxy.$modal.msgWarning("请选择合成音色");
|
|
|
return;
|
|
|
}
|
|
|
flowobj.value.synthesizedSpeech = 1;
|
|
|
successCount.value = 10;
|
|
|
let timer = setInterval(() => {
|
|
|
Load.value += 10;
|
|
|
if (Load.value >= 100) {
|
|
|
clearInterval(timer);
|
|
|
proxy.$modal.msgSuccess("合成成功");
|
|
|
}
|
|
|
}, 1000);
|
|
|
};
|
|
|
const submit = () => {
|
|
|
console.log(successCount.value!==10, "successCount");
|
|
|
if (successCount.value !== 10 && EchoState.value == false) {
|
|
|
proxy.$modal.msgError("请先完成所有步骤");
|
|
|
return;
|
|
|
}
|
|
|
commonality
|
|
|
.submitTask({
|
|
|
module: "人工智能",
|
|
|
numberOfErrors: errorCount.value > 10 ? 10 : errorCount.value,
|
|
|
taskName: "语音技术",
|
|
|
userId: JSON.parse(getUserInfo()).userId,
|
|
|
})
|
|
|
.then((response) => {
|
|
|
proxy.$modal.msgSuccess("提交任务成功");
|
|
|
handleClose();
|
|
|
timerCount.value = 0;
|
|
|
|
|
|
})
|
|
|
.catch((error) => {});
|
|
|
};
|
|
|
// 销毁定时器
|
|
|
// 重新开始
|
|
|
const reStart = () => {
|
|
|
console.log(scatterCanvas.value,"successCount");
|
|
|
voiceTechnologyVisible.value = false;
|
|
|
successCount.value = 0;
|
|
|
restartState.value = true;
|
|
|
FeaturesShow.value = false;
|
|
|
timerCount.value = 0;
|
|
|
Identifyipu.value = "";
|
|
|
document.getElementById("typewriter").textContent=''
|
|
|
clearInterval(interval.value); // 当文本全部打印完毕后,清除定时器
|
|
|
// 清除画布的绘画内容
|
|
|
const ctx = scatterCanvas.value.getContext("2d");
|
|
|
ctx.clearRect(0, 0, scatterCanvas.value.width, scatterCanvas.value.height);
|
|
|
clearInterval(timer.value)
|
|
|
scatterCanvas.value = null;
|
|
|
arrList.value.forEach((item, index) => {
|
|
|
item.answer = "";
|
|
|
});
|
|
|
imgesList.value[0].studentAnswer = "";
|
|
|
spellList.value.forEach((item, index) => {
|
|
|
item.value = "";
|
|
|
});
|
|
|
DubbingSelection.value = "";
|
|
|
synthesisradio.value = "";
|
|
|
Load.value = 0;
|
|
|
arrList.value[0].stuanswer=''
|
|
|
// clearInterval(timer.value);
|
|
|
};
|
|
|
const handleClose = () => {
|
|
|
const ipt = spellList.value.map((item) => item.value).join(",");
|
|
|
flowobj.value.inputPinyin = ipt;
|
|
|
flowobj.value.numberOfErrors = errorCount.value;
|
|
|
flowobj.value.subState = successCount.value
|
|
|
flowobj.value.optionOne=arrList.value[0].stuanswer || ''
|
|
|
voiceTechnologyVisible.value = false;
|
|
|
console.log(flowobj.value,"flowobj.value");
|
|
|
if (restartState.value) return;
|
|
|
commonality.submitVoiceTask(flowobj.value).then((response) => {});
|
|
|
};
|
|
|
const dialogCloick = () => {
|
|
|
commonality.getVoiceList({ module: "语音技术" }).then((res) => {
|
|
|
arrList.value = res.data;
|
|
|
if (restartState.value) {
|
|
|
arrList.value.forEach((item, index) => {
|
|
|
item.answer = "";
|
|
|
item.stuanswer = "";
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
if (restartState.value) return;
|
|
|
commonality.getVoiceEcho({ userId: JSON.parse(getUserInfo()).userId }).then((res) => {
|
|
|
if (res.data.length == 0) return;
|
|
|
if(res.data[0].subState===10){
|
|
|
EchoState.value = true;
|
|
|
}
|
|
|
errorCount.value = res.data[0].numberOfErrors || 0;
|
|
|
successCount.value = res.data[0].subState || 0
|
|
|
imgesList.value[0].studentAnswer = res.data[0].voiceSignalAcquisition;
|
|
|
if (res.data[0].featureExtraction == 1) {
|
|
|
PretreatmentAndFeatureExtraction();
|
|
|
}
|
|
|
if (res.data[0].speechFeatureMap == 1) {
|
|
|
drawScatterPlot();
|
|
|
}
|
|
|
if (res.data[0].matching == 1) {
|
|
|
matchingProcess();
|
|
|
}
|
|
|
|
|
|
if (res.data[0].inputPinyin.length > 0) {
|
|
|
spellList.value[0].value = res.data[0].inputPinyin[0];
|
|
|
spellList.value[1].value = res.data[0].inputPinyin[1];
|
|
|
spellList.value[2].value = res.data[0].inputPinyin[2];
|
|
|
spellList.value[3].value = res.data[0].inputPinyin[3];
|
|
|
}
|
|
|
setTimeout(()=>{
|
|
|
arrList.value[0].stuanswer=res.data[0].optionOne
|
|
|
},100)
|
|
|
Identifyipu.value = res.data[0].inputText;
|
|
|
DubbingSelection.value = res.data[0].standardDubbing || ''
|
|
|
synthesisradio.value = res.data[0].synthesizedSpeech == 1 ? "男声" : "女声";
|
|
|
Load.value = 100;
|
|
|
});
|
|
|
};
|
|
|
// 页面销毁
|
|
|
onBeforeUnmount(() => {
|
|
|
handleClose()
|
|
|
|
|
|
clearInterval(timer.value);
|
|
|
})
|
|
|
onMounted(()=>{
|
|
|
dialogCloick()
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
:root {
|
|
|
--color: #333333;
|
|
|
}
|
|
|
// p{
|
|
|
// font-size: 12px;
|
|
|
// }
|
|
|
.voice-technology {
|
|
|
display: flex;
|
|
|
.left {
|
|
|
position: relative;
|
|
|
// width: calc(100% - 216px);
|
|
|
p{
|
|
|
color: #000000;
|
|
|
}
|
|
|
}
|
|
|
.right {
|
|
|
position: absolute;
|
|
|
right: 42px;
|
|
|
top: 180px;
|
|
|
width: 216px;
|
|
|
padding-left: 60px;
|
|
|
span {
|
|
|
display: inline-block;
|
|
|
width: 110px;
|
|
|
height: 143px;
|
|
|
background: url("@/assets/images/szjj/案例任务.png") no-repeat;
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// p{
|
|
|
// font-size: 12px;
|
|
|
// font-family: Source Han Sans CN;
|
|
|
// font-weight: 600;
|
|
|
// font-size: 12px;
|
|
|
// color: #333333;
|
|
|
// line-height: 20px;
|
|
|
// }
|
|
|
.TaskBackground {
|
|
|
span {
|
|
|
display: inline-block;
|
|
|
width: 181px;
|
|
|
height: 66px;
|
|
|
text-align: center;
|
|
|
line-height: 66px;
|
|
|
background: url("@/assets/images/szjj/任务背景.png") no-repeat !important;
|
|
|
background-size: 100% 100% !important;
|
|
|
font-family: Source Han Sans CN;
|
|
|
font-weight: bold;
|
|
|
font-size: 20px;
|
|
|
color: #FFFFFF;
|
|
|
}
|
|
|
}
|
|
|
.step {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
.step-item {
|
|
|
width: 52px;
|
|
|
height: 25px;
|
|
|
line-height: 25px;
|
|
|
text-align: center;
|
|
|
background: #5621f8;
|
|
|
border-radius: 12px;
|
|
|
margin-right: 8px;
|
|
|
span {
|
|
|
display: inline-block;
|
|
|
font-family: Source Han Sans CN;
|
|
|
font-weight: 600;
|
|
|
font-size: 12px;
|
|
|
color: #ffffff;
|
|
|
}
|
|
|
}
|
|
|
span {
|
|
|
font-size: 15px;
|
|
|
color: #000000;
|
|
|
// color: var(--color);
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
}
|
|
|
.answer {
|
|
|
p {
|
|
|
width: calc(100% - 40%);
|
|
|
height: 36px;
|
|
|
background: #e9ebfc;
|
|
|
border-radius: 4px;
|
|
|
padding: 0px 21px;
|
|
|
}
|
|
|
}
|
|
|
.imges-centent {
|
|
|
display: flex;
|
|
|
gap: 19px;
|
|
|
.imges-items {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
width: 246px;
|
|
|
height: 249px;
|
|
|
background: #f7f7f7;
|
|
|
border-radius: 4px;
|
|
|
border: 1px solid #dcdcdc;
|
|
|
img {
|
|
|
margin-bottom: 10px;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.Featurextraction {
|
|
|
width: calc(100% - 40%);
|
|
|
height: 93px;
|
|
|
background: #f7f7f7;
|
|
|
border-radius: 4px;
|
|
|
border: 1px solid #dcdcdc;
|
|
|
img {
|
|
|
width: 100%;
|
|
|
height: 50px;
|
|
|
}
|
|
|
#audio {
|
|
|
width: 100%;
|
|
|
height: 30px;
|
|
|
border-radius: 0;
|
|
|
background-color: #f1f3f4;
|
|
|
}
|
|
|
}
|
|
|
.btn {
|
|
|
.el-button {
|
|
|
height: 40px;
|
|
|
border: none;
|
|
|
font-size: 12px;
|
|
|
background: url("@/assets/images/szjj/按钮1.png") no-repeat;
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
margin-bottom: 20px;
|
|
|
}
|
|
|
.btn3 {
|
|
|
.el-button {
|
|
|
width: 136px;
|
|
|
background: url("@/assets/images/szjj/提交任务.png") no-repeat;
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
}
|
|
|
.btn4 {
|
|
|
.el-button {
|
|
|
width: 136px;
|
|
|
background: url("@/assets/images/szjj/语音合成.png") no-repeat;
|
|
|
background-size: 100% 100%;
|
|
|
}
|
|
|
}
|
|
|
.spell {
|
|
|
display: flex;
|
|
|
gap: 23px;
|
|
|
.spell-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
flex-direction: column;
|
|
|
margin-right: 20px;
|
|
|
span {
|
|
|
margin-bottom: 12px;
|
|
|
font-family: Source Han Sans CN;
|
|
|
font-weight: bold;
|
|
|
font-size: 16px;
|
|
|
color: #FFFFFF;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
.dub {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
width: calc(100% - 40%);
|
|
|
height: 58px;
|
|
|
padding-left: 20px;
|
|
|
background: #f7f7f7;
|
|
|
border-radius: 4px;
|
|
|
border: 1px solid #dcdcdc;
|
|
|
#audio {
|
|
|
width: 100%;
|
|
|
height: 30px;
|
|
|
border-radius: 0;
|
|
|
background-color: #f1f3f4;
|
|
|
}
|
|
|
}
|
|
|
#typewriter {
|
|
|
width: 460px;
|
|
|
height: 166px;
|
|
|
padding: 10px 1px 10px 1px;
|
|
|
color: #595a63;
|
|
|
font-size: 18px;
|
|
|
font-family: monospace;
|
|
|
white-space: nowrap;
|
|
|
overflow: hidden;
|
|
|
// border-right: 2px solid;
|
|
|
animation: typing 2s steps(20, end), blink-caret 1s step-end infinite;
|
|
|
font-family: monospace;
|
|
|
white-space: pre; /* 保持空格和换行符 */
|
|
|
background-color: #eef0f4;
|
|
|
}
|
|
|
|
|
|
@keyframes typing {
|
|
|
from {
|
|
|
width: 0;
|
|
|
}
|
|
|
to {
|
|
|
width: 460px;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@keyframes blink-caret {
|
|
|
// 50% { border-color: transparent }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
canvas,
|
|
|
.canvas {
|
|
|
width: 671px;
|
|
|
height: 134px;
|
|
|
border-radius: 4px;
|
|
|
background-color: #f7f7f7;
|
|
|
}
|
|
|
.tsColor{
|
|
|
.el-radio{
|
|
|
color: #000000;
|
|
|
}
|
|
|
}
|
|
|
.tj{
|
|
|
width: 136px;
|
|
|
height: 40px;
|
|
|
color: #000000 !important;
|
|
|
}
|
|
|
.cxks{
|
|
|
width: 136px;
|
|
|
height: 40px;
|
|
|
color: #000000 !important;
|
|
|
}
|
|
|
</style>
|