[TOC]
歌声合成原理
- 歌唱产生的音乐成为歌声。歌唱更注意着重于通过横膈膜和腹部甚至下腹部肌肉来调整呼吸, 从而更好地控制音高、音色等。
- 歌唱中的颤音是指音高急剧上下波动的声音,是由空气有控制地通 过放松的喉部而产生。
- 一般包括两个过程:乐谱的分析和声音的产生。
- 乐谱分析:调、节奏、旋律和语义
音乐四要素
- 音的高低:音高 (最重要):发声体的振动频率决定,是周期的倒数
- 人堆频率的感知是非线性的,例如100Hz与它的2倍频200Hz之间的距离和 200Hz与它的2倍频400Hz间的距离对人耳来说是一致的。
- 频率大则音高,频率小则音低。
- 音乐上倍频称为一个八度,西方的十二平均律把一个八度分为十二个半音。
- 标准音高A5((5表示该音符在第5个八度音程),):中央c上的A音符发音频率为440HZ,表示成A=440HZ,A440,<u>
国际通用标准音</u> - midi 与频率的换算
- 节拍
- 是音乐中规律地强拍和弱拍的反复,如果我们跟随音乐的进 行用脚一下一下地点地,每一下就是一拍,打击乐器在音乐中就是配合节拍演奏的。
- 在乐谱中表示节拍用小节,每一个乐谱前面都有拍号,中间改变节奏会改变拍号。分子代表每一小节有多少拍子,分母代表用什么音符代表一拍。
- 如2/4代表用四分音 符代表一拍,每-d,节有两拍。
- 速度tempo决定了一段音的快慢,是音乐的重要元素,影响音乐的情感和演奏难度。
- 速度一般以文字或数字标记的于乐谱的开端,习惯于每分钟多少拍,(beats per minute,BPM)作为度量单位。
- BPM的数值越大代表越快的速度。
- 音的长短:(最重要):音符的长短,由发声体震动的振幅决定的。
- b分之一音符
- 音的强弱:表征音乐的力度(强度),力度的变化是音乐作品中表达情感的常用方式之 一
- 音色
- 连音符:是一个音节包含一个或多个音符的情况
- 音程:两个音之间的距离,音程的单位是分贝(Cent)
- image
语音产生的机理
- 语音产生的过程包括三部分
-
声门下部分,声门部分。声门上部分
- >声门下部分由气管,支气管,肺等呼吸气管组成,它提供发声的动力。
- >声门在发声时作有节奏的开闭动作,把从肺呼出的气流调节成脉冲状声门 波。这种携带了能量的声门波成为说话和歌唱时的基本声源。
- >声门上部分是整个共鸣腔,包含口腔,鼻腔和咽腔。声门波经过共鸣腔的 调节以及辐射效应,产生出不同的音素,并发出声音。 - 歌声合成大致分为两类
- 基于人耳感知机制的频率参数模型和基于声音产生原理的物理模型。
歌声合成研究现状
基于波形拼接的方法
- 处理过程
- 音符标注、分割
- 缺点:过程复杂、需要人工介入,需要建立大量的规则
基于统计模型
- 实例:基于隐马尔科夫链的系统
- 基于源一滤波器模型对歌声进行信号分解,得到基频和频谱 参数,然后利用隐马尔科夫模型对这些参数进行建
- 优点:
- 通过一定数量的歌声可以达到一个不错的音质
- 对歌声进行参数分解,需要存储的的参数需要空间小
- 参数的改变和转换非常的便捷,因此改变歌声的音质,音高,时长等特征方面非常灵活
- 缺点
- 真实感差
汉语歌声合成:歌声转换
基于统计模型的汉语歌声 合成研究
基频模型
- 针对歌声基频存在的数据稀疏问题,提出了参考乐谱的基频引导方 法。该方法将乐谱中包含的基频信息引入到歌声基频的生成算法中, 避免了因数据稀疏而造成的合成音高在时间和频谱结构上出现偏差的 问题,可以合成出与乐谱相一致的具有精准音高的基频。
- 对真实基频和乐谱基频之间可能存在差异的问题进行了研究,提出了 在训练中也考虑乐谱基频因素从而准确得到两者之间的差值的方法。 利用该方法可以得到相比乐谱基频引导方法更加准确和真实的基频估 计。上述方法也可用于连音符的合成。
HMM
- 马尔科夫原理
-
隐马尔科夫链
- HMM三个基本问题
- 1.给定一个输出序列O和模型入,求模型输出此序列的概率。这个问题可通 过前向后向算法求解。
- 2.给定一个输出序列和模型,求最可能输出此序列的状态序列。这个问题可 通过Viterbi算法求解。
- 3.给定一个输出序列和模型结构,求模型参数使得概率最大。这是模型的训 练问题,Baum.Welch算法可以用来求解。
- image
- HMM三个基本问题
基于HMM的声音合成框架
建模尺度
- 声韵母作为建模单元
建模结构
- image
特征提取
- 从波形中提取基频和频谱特征。
- 频谱特征采用mel-cepstrum特征
- image
模型训练阶段
- image
合成阶段
- 生成的声学参数输入到梅尔对数频谱估计滤波器,合成出歌声。
歌声合成的关键
- 表达出正确的音乐信息,即音高和节奏要精准。这对歌声的时长模型和基 频模型提出了新要求,需要将音乐信息加入建模方可实现,
- 颤音的合成是歌声的关键。
歌声的市场与乐谱的关系
- 唱歌的时候的开始时间相对于乐谱有一个提前量。
- 提前量
歌声的基频与乐谱的关系
- 1.歌声的基频需要遵循乐谱,也就是说要”在调上”。一般的朗诵语音和歌声 的最大区别在于歌声必须遵循乐谱的音高。同时音高是影响歌声合成质量 的一个关键影响因素。
- 2.歌声的基频包含有颤音,颤音是音乐情绪的表达的重要组成部分,也是衡 量专业歌手的重要指标。
- 歌声的基频与乐谱的音高
时间模型
- 唱歌的起点与乐谱上的时间有一个提前差,
- 时间模型
基频稀疏问题
- 在矩阵中,若数值为0的元素数目远远多于非0元素的数目,并且非0元素分布没有规律时,则称该矩阵为稀疏矩阵
- 由于歌声中的基频中含有大量的上下文,比如音高、调、节奏等信息,
- 因此基频通 常是稀疏的,所以在训练数据中出现的很少的上下文情况将无法得到充分训练, 这会导致无法合成出符合乐谱音高的基频曲线。
实际基频与乐谱差值建模
- 对基频与乐谱的音高曲线进行建模,位归一化方法,这样虽然基频是稀疏的,但是基频与乐谱的差距在缩小。
- 数据 层次的归一化方法在训练前对基频数据进行处理,然后将基频差用于训练。 这样做会引起一个问题,基频在做归一化时需要先与乐谱做对齐,因此在后续 训练中都得按照这个对齐的结果进行。这样很容易出现问题,第一,对齐通常无 法保证准确,第二,只能使用Viterbi训练而无法使用嵌入的EM训练,EM.训 练通常比Viterbi训练要好。模型层次的归一化方法巧妙的运用了说话人适应[46]的技术,在模型层次对基频进行归一化,解决了以上问题。
颤音建模
- 有正确的音准和节奏。颤音也是语音和歌声的最大区别。
-
颤音幅度和速率
- 颤音幅度顾名思义指的是颤音上下抖动的幅度,颤音速率指 颤音的变化速率。
- 其中bt是第t个状态对应的基频值,转换矩阵Bt=[1,bt]由乐谱唯一决定, 因此氐是唯一需要估计的参数。
- image
- 歌声的基频= 语调部分 + 颤音部分
- 颤音参数提取用的最多的是基于希尔伯特变换的方法。希尔伯特变换可以计算信号瞬时速率,非常适合颤音的时变特点。
歌声合成的评价方式
- 定义了一个平均音符距离指标,它是以音符为基本单 位进行计算的,所以它能从更高的尺度上,同时也是从音乐的意义上来衡量两 个基频曲线间的距离。
歌声库的建立
曲目选择
- 不是的
- 饶舌类歌曲
- 自由节奏曲目
- 音高不标准的歌曲,如一些民族歌曲中出现的非标准音。
- 选择曲目:儿童歌曲以及传统流行歌曲中得到
- 所有拼音的覆盖
- 声韵母分布的平衡
- 调的覆盖和平衡
- 节奏的覆盖和平衡
- 曲目
- 传统流行歌曲
乐谱分析
- 将乐谱转换为用于训练与合成的上下文标注是歌声合成系统的前端部分。
标准的MIDI
- MIDI(music Instrument Digital interface),中文称为数字接口,是一个工业标准的电子通信协议。
- 编号为128的MIDI消息类型为 Note On,即开始演奏一个音符;编号为144的MIDI消息类型为Note Of!f,即停 止演奏一个音符。
- midi文件
- 读入 MIDI 格式文件,产生一个 n*7 的矩阵,其中矩阵的第三列表示通道标号,第四列表示音符音高,第五列表示按键的速度,然后提取代表主通道信息的子矩阵,子矩阵的每行代表乐谱中每个音符的基频等信 息。子矩阵的第四列对应于乐谱中每个音符的频率,利用子矩阵中第六列和第七列的数 值可求出乐谱中任一音符的时长。
- "度”就是音与音之间距离的衡量单位。而音与音之间音高的距离叫做音程, 半音(Semitone)是其计算的最小单位。有些音之间的距离仅差半音,此时我们称之为“半 音程”,而有些则差两个半音, 我们则称之为“全音程”
示例
- 保存为mid
import random
import sys
from mido import Message, MidiFile, MidiTrack, MAX_PITCHWHEEL
notes = [64, 64+7, 64+12]
outfile = MidiFile()
track = MidiTrack()
outfile.tracks.append(track)
track.append(Message('program_change', program=12))
delta = 300
ticks_per_expr = int(sys.argv[1]) if len(sys.argv) > 1 else 20
for i in range(4):
note = random.choice(notes)
track.append(Message('note_on', note=note, velocity=100, time=delta))
for j in range(delta // ticks_per_expr):
pitch = MAX_PITCHWHEEL * j * ticks_per_expr // delta
track.append(Message('pitchwheel', pitch=pitch, time=ticks_per_expr))
track.append(Message('note_off', note=note, velocity=100, time=0))
outfile.save('test.mid')
- mid2json
import sys
import json
import mido
def midifile_to_dict(mid):
tracks = []
for track in mid.tracks:
tracks.append([vars(msg).copy() for msg in track])
return {
'ticks_per_beat': mid.ticks_per_beat,
'tracks': tracks,
}
mid = mido.MidiFile(sys.argv[1])
print(json.dumps(midifile_to_dict(mid), indent=2))
print('mid',mid)
- mid 数据读取
from mido import Message,MidiFile
msg = Message('note_on', note=60,channel=2,velocity=112)
# velocity 速度
# channel 频道
# note 音符
print('msg',msg)
print(msg.note,msg.channel,msg.velocity,msg.time)
# 转化为字节
print(msg.bytes())
print(msg.bin())
print(msg.hex())
# 从字节加载数据
msg1 = Message.from_bytes([0x90, 0x40, 0x60])
print(msg1)
# 打开mid 文件
mid = MidiFile('../test.mid')
# 有多少个音轨
for i, track in enumerate(mid.tracks):
print('Track {}: {}'.format(i, track.name))
for msg in track:
if msg.is_meta:
print('meta')
else:
print(msg)
# 计算速度
print(msg.bpm2tempo())
'''mid 总时间'''
print('mid.length',mid.length)
'''
type 0 (single track): all messages are saved in one track
type 1 (synchronous): all tracks start at the same time
type 2 (asynchronous): each track is independent of the others
'''
print(mid.type)
MusicXML
- xml
xml解析
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "errrolyan"
# Date: 18-10-16
# Describe = "乐谱xml文件转化未为拼音”
import os,re,sys
import os.path
import xml.etree.ElementTree as ET
import pinyin
from collections import Counter
def coverFiles(sourceDir, targetDir):
for file in os.listdir(sourceDir):
sourceFile = os.path.join(sourceDir, file)
targetFile = os.path.join(targetDir, file)
if os.path.isfile(sourceFile):
open(targetFile, "wb").write(open(sourceFile, "rb").read())
def fileread(filepath):
pathDir = os.listdir(filepath)
for s in pathDir:
newDir = os.path.join(filepath, s)
if os.path.isfile(newDir):
if os.path.splitext(newDir)[1] == ".xml":
print(newDir)
name1 = newDir[13:39]
tree = ET.parse(newDir)
root = tree.getroot()
for text in root.iter('text'):
text.text = re.sub(u"[\s.。??!!;;\/_:,%^*(\"“”《》$\,']", '', text.text)
if u'\u4e00' <= text.text <= u'\u9fff':
text.text = pinyin.get(text.text, format="strip", delimiter=" ") # format="strip" "numerical"
print(text.text)
text.text = str(text.text)
text.set('updated', 'yes')
print("第" + newDir + "首歌完成!!!")
tree.write(newDir,encoding="utf-8",xml_declaration='xml version="1.0" encoding="utf-8"')
def xml_to_pinyin(xml_in_dir, xml_out_dir):
coverFiles(xml_in_dir, xml_out_dir)
fileread(xml_out_dir)
if __name__=="__main__":
usage = 'Usage: xml_to_Pinyin.py xml_in_dir xml_out_dir'
if len(sys.argv) != 3:
xmlInPath = 'xml_in_dir1'
xmlOutPath = 'xml_out_dir1'
else:
xmlInPath = sys.argv[1]
xmlOutPath = sys.argv[2]
xml_to_pinyin(xmlInPath,xmlOutPath)
print("完成转换拼音")
基线系统搭建
- 对歌声数据,使用25ms的汉明窗,5ms 的帧移进行MGC参数提取,MGC参数包含能量特征一共35维度。基频使 用Snack库中的get_f0提取snack,基频的上下限我们取值80Hz与700Hz,对应 MIDI音高约为E2与F5。颤音被检测出来并从基频中减去。所有的特征,包含对数域基频,颤音,MGC都求一阶及二阶动态特征。HMM状态数目为7个, MGC流使用单高斯,基频和颤音流使用多空间概率分布HMM。
歌声信号分析
- 时域分析和频域分析都有各自的局限性:时域分析对语音信号的频率特性没有直观的显示;频域分析中又缺乏语音信号随时间的变化关系。
时域分析
- image
- 时域波形 图中可以看出语音的时长、音节的起止位置;另外,通过波形是否具有周期性可以区分 清音和浊音;通过观察不同音素的波形区别,甚至还可以大概的估计出浊音的基音周期。
频域分析
- 音频信号的频率、功率谱、倒频谱、频谱包络。
- 处理方式:短时傅里叶变换。
语谱分析
-
语谱分析是动态的频谱,反应了语音频谱随时间的变化情况。
-
语谱图是语谱分析的外在表现形式,语谱 图也称频谱分析视图,采用二维平面来表示三维信息。它的横坐标是时间,纵坐标是频 率,坐标点值为语音数据能量。
- image
-
语谱图中还可以确定语音参数,如共振峰频率、基频。与时间轴平行的横杠带纹反映的就是共振峰的特点。
提取工具world
-
World 通过CheapTrick的方法获取频谱包络信息,音按照基频的周期为单位进行分段,以此 保证波形和频谱的平滑连续.对于加窗以后的时域信号进行傅里叶变换获得对应的频谱,然后在三角窗内 对信号进行平滑,再利用倒谱方法,求取频谱的包络信息:
歌声基频生成方法
- 歌声的基频乐谱上音符的高低密切相关,基频的大致走向可以 直接反映乐谱上音符的高低。一般来讲,基频可认为包含2部分,一部分对应 乐谱,一般成为语调曲线:另一部分为细微的颤音。颤音是一种近似正弦的持续 振荡,频率在5—8Hz.颤音可以体现歌唱者的细微情感。而颤音也是衡量一个 歌手专业素养的重要指标之一。
- 歌声基频生成
基于乐谱基频引导的基频生成方法
- 一个 音符的绝对唱名是指这个音符的真实音名(C、D、E、F、G、A、B等)对应的 唱名(do、re、mi、fa.、sol、la、si等):而一个音符的首调唱名是规定当前歌曲的调名主音为do时,该音符的唱名,表示该音符与调名主音的音高差值,是一 个“相对”的音高。
- 十二个调名对应主音与编号
乐谱基频引导
- image
DF0模型
- image
- image
- image
-
3.实际上现在实际基频与乐谱基频已经一一对应,因此可求得实际基频与乐 谱基频的差值(Dr0)。
-
建模DF0
- (A)因为已经得到了状态级别的对齐,因此每个DF0值都可对应到一个FO模型,所以我们可以让DF0共享F0的决策树,这里在每个叶子节 点对DF0使用单高斯进行建模。
- (B)第二种方法是,在得到DF0后,重新对DF0在HMM的框架下单独进 行训练。DF0有这单独的决策树以及时长模型。
音节层模型
离散余弦变换用于基频参数化
- 离散余弦变换DCT已经成功用于多种语言的基频建模。DCT是一种线性可逆的变换,它可以将基频曲线为一组余弦分量的加权和。
- [图片上传失败...(image-96c8ae-1590825742301)]
- MGC处理:论文:CELP coding based on reel-cepstral analysis.
歌声合成的关键
- 准确的音高和时长
- 颤音是歌声情感表达的关键,缺乏表现力的主要原因是因为基频过于平滑。
- 基频主要分为两个部分的曲线:形状曲线和颤音曲线
- 形状曲线又可以分为音高曲线和残差曲线
- 颤音的频率一般为4HZ-8HZ
- 将基频曲线通过一个截止频率为3.5HZ低通滤波器来得到低频部分作为形状曲线,高频部分作为颤音曲线。
- 乐谱的音高曲线可以直接由公式计算到
- f=440*2^(m-69)/2
- 残差曲线 = 形状曲线 - 乐谱曲线
- 形状曲线又可以分为音高曲线和残差曲线
残差特征
- image
- image
颤音特征(vibrato)
颤音的幅值和速率
- [图片上传失败...(image-a791b7-1590825742301)]
具备情感表达能力的歌者转换
- 语音转换关键是韵律特征和音质特征
- 基频特征转换
- image
- 基频特征转换
- 歌者转换技术使得利用己有的歌声合成系统附加少量的特定 人的歌声库可以合成出该特定人的歌声。由于录制歌声库的工作量非常庞大, 因此歌者转换技术非常有应用价值。
- 从一个歌唱人转换到另一个歌唱人的时候,如果有着不该变歌曲的调的要求,那么基频是不能改变的。
基于声学参数修改的语音转歌声的算法
- 主要修改3个声学参数:基频、语音时长和频谱包络
- image
-
歌声和语音的主要区别是基频的差距,不是频谱特征。
- 基频代表说话人声带每次开启和关闭的时间间隔,反映了声调的变化。基频的大小反映了声带的大小、厚薄、松紧程度以及声门上下之间的气压差的效应等。
- 目前提取基频的有利工具是world
- image
- 基频分为两个部分处理:基频的跳变区、基频的平稳区。
- 基频的拟合:
- image
- w叫是自然频率;是阻尼系数;k是系统的比例增益
- 歌唱声音的频谱包络在3kHz附近有一个叫做“歌唱共振峰”的显著峰值,
声韵母表
- image
网友评论