Live2D Mouth-sync artifact
Project description
pymouth
pymouth 是基于Python的Live2D口型同步库. 你可以用音频文件, 甚至是AI模型输出的ndarray, 就能轻松的让你的Live2D形象开口
唱跳RAP v.
效果演示视频.
Demo video
Quick Start
Environment
- Python>=3.10
- VTubeStudio>=1.28.0 (可选)
Installation
pip install pymouth
Get Started
-
在开始前你需要打开
VTubeStudio的 Server 开关. 端口一般默认是8001.
-
你需要确定自己Live2D口型同步的支持参数.
请注意:下面提供一种简单的判断方式,但这种方式会修改(重置)Live2D模型口型部分参数,使用前请备份好自己的模型。
如果你对自己的模型了如指掌,可以跳过这步。
- 确认重置参数后,如果出现以下信息,则说明你的模型仅支持
基于分贝的口型同步 - 确认重置参数后,如果出现以下信息,则说明你的模型仅支持
基于元音的口型同步 - 如果VTubeStudio找到了所有参数,并且重置成功,说明两种方式都支持。只需要在接下来的代码中选择一种方式即可.
- 确认重置参数后,如果出现以下信息,则说明你的模型仅支持
-
下面是两种基于不同方式的Demo.
你可以找一个音频文件替换some.wav.
samplerate:音频数据的采样率.
output_device:输出设备Index. 这里很重要,如果不告诉插件播放设备是哪个,那么插件不会正常工作。 可以参考audio_devices_utils.py-
基于分贝的口型同步import time from pymouth import VTSAdapter, DBAnalyser def main(): with VTSAdapter(DBAnalyser()) as a: a.action(audio='some.wav', samplerate=44100, output_device=2) time.sleep(100000) # do something if __name__ == "__main__": main()
-
基于元音的口型同步import time from pymouth import VTSAdapter, VowelAnalyser def main(): with VTSAdapter(VowelAnalyser()) as a: a.action(audio='some.wav', samplerate=44100, output_device=2) time.sleep(100000) # do something if __name__ == "__main__": main()
第一次运行程序时,
VTubeStudio会弹出插件授权界面, 通过授权后, 插件会在runtime路径下生成pymouth_vts_token.txt文件, 之后运行不会重复授权, 除非token文件丢失或在VTubeStudio移除授权.
-
API变化
1.3.0版本之后,分析器的对象由用户创建, VowelAnalyser 元音分析仪支持 temperature 参数,这个参数用来控制各个元音的置信度,值越大置信度越低,口型越平滑,反之亦然。temperature 不能为 0
1.3.0对两种 Analyser 进行了算法改进,使口型同步的效果更好。
import asyncio
from pymouth import VTSAdapter, VowelAnalyser
async def main():
# with VTSAdapter(VowelAnalyser) as a: # 不能再用这种方式创建 Analyser
with VTSAdapter(VowelAnalyser()) as a: # 需要由用户new出这个对象,temperature 默认为10,可以不填
a.action(audio='aiueo.wav', samplerate=44100, output_device=2) # no-block
await asyncio.sleep(100000)
if __name__ == "__main__":
asyncio.run(main())
1.2.0版本之后,移除了所有函数的协程调用方式(async/await),协程调用具有传染性,不利于用户维护。
目前只提供阻塞与非阻塞调用方式,非阻塞方式由内部线程池单线程实现,即无论a.action
被调用多少次,都会按照调用的现后顺序播放音频。
- 如果你仍使用协程启动,可以参考下面的示例
import asyncio from pymouth import VTSAdapter, VowelAnalyser async def main(): with VTSAdapter(VowelAnalyser()) as a: a.action(audio='aiueo.wav', samplerate=44100, output_device=2) # no-block # a.action_block(audio='aiueo.wav', samplerate=44100, output_device=2) # block await asyncio.sleep(100000) if __name__ == "__main__": asyncio.run(main())
About AI
下面是一个比较完整的使用pymouth作为AI TTS消费者的例子。
import queue
import threading
import time
from fish_speech import tts
from pymouth import VTSAdapter, DBAnalyser, VTSPluginInfo
class SpeakMsg:
def __init__(self, msg: str, required: bool):
self.msg = msg
self.required = required
self.create_timestamp = time.time()
self.create_datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.create_timestamp))
class Speaker:
def __init__(self):
self.queue = queue.Queue(1)
def start(self):
plugin_info = VTSPluginInfo(plugin_name='kanojyo2',
developer='organics',
authentication_token_path='./pymouth_vts_token.txt',
plugin_icon=None)
with VTSAdapter(DBAnalyser(), plugin_info=plugin_info) as a:
while True:
msg: SpeakMsg = self.queue.get()
t0 = time.time()
audio, rate = tts.tts_ndarray(msg.msg)
print(f'speak time:{time.time() - t0:.02f}')
a.action(audio=audio, samplerate=rate, output_device=2)
def speak(self, msg: str, required=True):
if required:
self.queue.put(SpeakMsg(msg, required))
else:
try:
self.queue.put_nowait(SpeakMsg(msg, required))
except queue.Full:
print("Queue Full")
if __name__ == "__main__":
speakers = Speaker()
# 这里的实现只作为参考而不是建议。对于AI等CPU密集型场景,使用线程而不是协程可能会更好。
threading.Thread(target=speakers.start).start()
More Details
High Level
关键的代码只有两行:
with VTSAdapter(DBAnalyser()) as a:
a.action(audio='some.wav', samplerate=44100, output_device=2) # no-block
# a.action_block(audio='aiueo.wav', samplerate=44100, output_device=2) # block
a.action()非阻塞,会立即返回,由程序内部维护线程池和队列。
a.action_block()阻塞,直到音频播放和处理完毕才会返回,纯同步代码无线程,线程由调用者维护。
VTSAdapter以下是详细的参数说明:
| param | required | default | describe |
|---|---|---|---|
analyser |
Y | 分析仪,必须是 Analyser 的子类,目前支持DBAnalyser和VowelAnalyser |
|
db_vts_mouth_param |
'MouthOpen' |
仅作用于DBAnalyser, VTS中控制mouth_input的参数, 如果不是默认值请自行修改. |
|
vowel_vts_mouth_param |
dict[str,str] |
仅作用于VowelAnalyser, VTS中控制mouth_input的参数, 如果不是默认值请自行修改. |
|
ws_uri |
str |
websocket uri 默认:ws://localhost:8001 | |
plugin_info |
VTSPluginInfo |
插件信息,可以自定义 |
a.action() 会开始处理音频数据. 以下是详细的参数说明:
| param | required | default | describe |
|---|---|---|---|
audio |
Y | 音频数据, 可以是文件path, 可以是SoundFile对象, 也可以是ndarray | |
samplerate |
Y | 采样率, 这取决与音频数据的采样率, 如果你无法获取到音频数据的采样率, 可以尝试输出设备的采样率. | |
output_device |
Y | 输出设备Index, 这取决与硬件或虚拟设备. 可用 audio_devices_utils.py 打印当前系统音频设备信息. | |
finished_callback |
None |
音频处理完成会回调这个方法. | |
auto_play |
True |
是否自动播放音频,默认为True,会播放音频(自动将audio写入指定output_device) |
Low Level
Get Started 演示了一种High Level API 如果你不使用 VTubeStudio 或者想更加灵活的使用, 可以尝试Low Level API. 下面是一个Demo.
import time
from pymouth import DBAnalyser
def callback(y: float, data):
# Y is the Y coordinate of the model's mouth.
# Like is 0.4212883452
print(y) # do something
with DBAnalyser() as a:
a.action_noblock('zh.wav', 44100, output_device=2, callback=callback) # no block
# a.action_block() # block
print("end")
time.sleep(1000000)
import time
from pymouth import VowelAnalyser
def callback(md: dict[str, float], data):
"""
md like is:
{
'VoiceSilence': 0,
'VoiceA': 0.6547555255,
'VoiceI': 0.2872873444,
'VoiceU': 0.1034789232,
'VoiceE': 0.3927834533,
'VoiceO': 0.1927834548,
}
"""
print(md) # do something
with VowelAnalyser() as a:
a.action_noblock('zh.wav', 44100, output_device=2, callback=callback) # no block
# a.action_block() # block
print("end")
time.sleep(1000000)
TODO
- 文档补全
- Test case
Special Thanks
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pymouth-1.3.0.tar.gz.
File metadata
- Download URL: pymouth-1.3.0.tar.gz
- Upload date:
- Size: 12.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.9.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3323c5ecee39fae8fbf7a266516d5bbeb1c1dac6e80f461d4df96fa83707916f
|
|
| MD5 |
29c016914270de98c9beb930c53341b3
|
|
| BLAKE2b-256 |
f3fe4f07d8ee74e1c84878db2636802aa332a318520d54deabb6f21973cb81e9
|
File details
Details for the file pymouth-1.3.0-py3-none-any.whl.
File metadata
- Download URL: pymouth-1.3.0-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.9.21
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d823442cfa955f6e72b7eff82d2a38ff6c5d603feb488ed0d148ab07fae27049
|
|
| MD5 |
64f5cc663a0215d216b789361deb9c8e
|
|
| BLAKE2b-256 |
d82cbff0a3e76d809a3c1f6baa79d05fbe0300a4606128fe6388d4d014b28768
|