许愿成功之克隆声音

记得在很早很早以前,有个可以用各种动漫角色的语调说话的网站叫15.ai[1]。不过网站上线没几天就因为爆火没了,估计是算力不够或者引起了某些资本大佬的注意。

作者应该是利用这些角色的音色训练了一个或多个模型,然后再做TTS(text-to-speech)。我体验了一下,效果还不错,但那时clone任意的音色还是存在一些挑战的,而且这里面也没有说中文的角色。

当然我并没有坐以待毙,而是直接许愿要是有一个网站可以上传任意的声音文件,然后生成模仿此人说话的音频文件就好了。数年后,愿望成真,来自智谱的GLM-TTS-Clone出现了,并且提供了在线测试的网站:

Free AI powered by CogTTS & CogTTS-Voice Cloning

由于网站最好要纯净无BGM的音频,我选择B站UP主聒碎的视频[2],并使用Edge的扩展Smart Audio Capture截取了30秒的音频开始clone,然后输入一段文字开始生成。效果好像还不错。

生成音频1(肯德基疯狂星期四V我50吧)

生成音频2(不中啊周大师,你的友人度+1了)

AI时代果然是一个许愿的时代,你想要的东西,不要着急自己做,等等就会发现有人给你做好了嘿:)

[1] 网站已迁移到:https://15.dev

[2] 语料视频链接:https://www.bilibili.com/video/BV1zYrrYoEKf/

一个优雅的存档删除方案

考虑到以下问题:

无论是游戏进度还是系统还原点,随着时间增长,都会产生N个备份。实际生产生活中,距离时间最近的备份,往往更重要,比如每天一次备份,而太长时间以前的备份,可能每月一次备份就足够了。

尝试找到一个特定的删除函数D(X),表示第X个备份,会在D(X)的步骤被删除,使得无论在哪个步骤Y,移除掉所有的Xi满足Y>=D(Xi),剩下的数据,始终满足“越小的数据间距越大”的特点。

为此,我们考察以下数列:

[1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128]

这是一个间距逐渐增大的数列,其中1,2,3间距为1,4,6,8间距为2,8,12,16间距为4,…,64,96,128间距为32。只可惜这个数列的间距随着数据的增加而增加。用128减去这个数列,得到:

[127, 126, 125, 124, 122, 120, 116, 112, 104, 96, 80, 64, 32, 0]

看到结尾是32,从这里我们得到一点启发,是否可以让2的幂次(如2,4,8,16,32,…)存活时间更长,更难被删除?

到这里算法思路就很明确了:如果第X的存档如果在第Y步被保留,则X含2的因子的数量p需满足p>log2(Y-X)。

这里给出以下Python代码,实现此算法:

def calc_delete_step(value, scale):
    if value == 0:
        return 0

    order = 0
    x = value
    while x % 2 == 0:
        x //= 2
        order += 1
    return value + scale * (2 ** (order + 1) - 1)

def generate_remains(value, scale):
    data = [value]
    step = 1

    while value > 0:
        for _ in range(scale):
            value -= step
            if value > 0:
                data.append(value)
        if value % (step * 2) != 0:
            value += step
        step *= 2
    return data

这里calc_delete_step的实现略有改变,这是为了使generate_remains的结果能从大到小分为长度为scale,间隔为1,2,4,…的多个组。

接下来考察结果,默认取scale=1,考察不同的N:

NRemains
10[10, 9, 8, 4]
30[30, 29, 28, 24, 16]
100[100, 99, 98, 96, 88, 80, 64]
200[200, 199, 198, 196, 192, 176, 160, 128]
300[300, 299, 298, 296, 288, 272, 256, 192, 128]
500[500, 499, 498, 496, 488, 480, 448, 384, 256]
500 (scale=3)[500, 499, 498, 497, 496, 494, 492, 488, 484, 480, 472, 464, 456, 448, 432, 416, 384, 352, 320, 256, 192, 128]

计算Remains从N倒数的差,明显发现存档的间隔是从1到2到4的逐渐增长。

NN – r for r in Remains[::-1]
10[0, 1, 2, 6]
30[0, 1, 2, 6, 14]
100[0, 1, 2, 4, 12, 20, 36]
200[0, 1, 2, 4, 8, 24, 40, 72]
300[0, 1, 2, 4, 12, 28, 44, 108, 172]
500[0, 1, 2, 4, 12, 20, 52, 116, 244]
500 (scale=3)[0, 1, 2, 3, 4, 6, 8, 12, 16, 20, 28, 36, 44, 52, 68, 84, 116, 148, 180, 244, 308, 372]

效果还是很不错的,并且还有2点需要提出:

  1. 这个算法是o(logN)的算法,可以在对数时间计算出第N步需要保留的存档。
  2. N较大时,Remains的数量与N趋近于对数关系,与scale是线性关系。

博客复活!

博客于2025年11月1日正式复活,会记录一些日常,也可能记录一些工作上的灵感。考虑到有的工作内容比较敏感,可能还是着重在业余爱好上。

终究还是自己的地盘比较放心,不用担心账号管理这些的。

别问为什么不搞https,问就是懒。