This repository has no description
1# SPDX-License-Identifier: MIT
2# SPDX-FileCopyrightText: 2023 MizunagiKB <mizukb@live.jp>
3extends Node2D
4
5
6
7const DEFAULT_ASSET: String = "res://addons/gd_cubism/example/res/live2d/mao_pro_jp/runtime/mao_pro.model3.json";
8
9var cubism_model: GDCubismUserModel
10var cubism_efx: GDCubismEffectCustom
11
12@export var param_mouth_name: String = "ParamMouthA"
13@export var min_db: float = 60.0
14@export var min_voice_freq: int = 0
15@export var max_voice_freq: int = 3200
16@export var history_size: int = 6
17
18@export_category("Audio")
19@export var audio_bus_name: String = "Voice"
20@export var audio_efx_index: int = 0
21
22var spectrum: AudioEffectSpectrumAnalyzerInstance
23var param_mouth: GDCubismParameter
24var ary_volume_history: Array
25var history_position: int = 0
26var lipsync_ready: bool = false
27
28
29func array_rebuild():
30 if history_size != ary_volume_history.size():
31 ary_volume_history.resize(history_size)
32 ary_volume_history.fill(0.0)
33
34
35func array_avg() -> float:
36 var sum_v: float = 0.0
37 for v in ary_volume_history:
38 sum_v += v
39 return sum_v / ary_volume_history.size()
40
41
42func recalc_model_position(model: GDCubismUserModel) -> void:
43 if model.assets == "":
44 return
45
46 var canvas_info: Dictionary = model.get_canvas_info()
47
48 if canvas_info.is_empty() != true:
49 var vct_viewport_size = Vector2(get_viewport_rect().size)
50 var scale: float = vct_viewport_size.y / max(canvas_info.size_in_pixels.x, canvas_info.size_in_pixels.y)
51 model.position = vct_viewport_size / 2.0
52 model.scale = Vector2(scale, scale)
53
54
55func _ready() -> void:
56 self.cubism_efx = $GDCubismUserModel/GDCubismEffectCustom
57 # self.cubism_efx.connect("cubism_prologue", self._on_cubism_prologue)
58 # self.cubism_efx.connect("cubism_epilogue", self._on_cubism_epilogue)
59 # self.cubism_efx.connect("cubism_init", self._on_cubism_init)
60 # self.cubism_efx.connect("cubism_term", self._on_cubism_term)
61 self.cubism_efx.connect("cubism_process", self._on_cubism_process)
62
63 self.cubism_model = $GDCubismUserModel
64 if self.cubism_model.assets == "":
65 self.cubism_model.assets = DEFAULT_ASSET
66
67
68func _process(delta: float) -> void:
69 self.recalc_model_position(self.cubism_model)
70
71
72func _on_cubism_init(model: GDCubismUserModel):
73 param_mouth = null
74 ary_volume_history.clear()
75 history_position = 0
76
77 var ary_param = model.get_parameters()
78
79 for param in ary_param:
80 if param.id == param_mouth_name:
81 param_mouth = param
82
83 var bus_index = AudioServer.get_bus_index(audio_bus_name)
84 spectrum = AudioServer.get_bus_effect_instance(bus_index, audio_efx_index)
85
86 if spectrum == null:
87 lipsync_ready = false
88 else:
89 lipsync_ready = true
90
91
92func _on_cubism_term(model: GDCubismUserModel):
93 lipsync_ready = false
94 param_mouth = null
95 ary_volume_history.clear()
96 history_position = 0
97
98
99func _on_cubism_process(model: GDCubismUserModel, delta: float):
100 if lipsync_ready == false:
101 return
102
103 array_rebuild()
104
105 var m: float = spectrum.get_magnitude_for_frequency_range(
106 min_voice_freq,
107 max_voice_freq
108 ).length()
109 var v = clamp((min_db + linear_to_db(m)) / min_db, 0.0, 1.0)
110
111 ary_volume_history[history_position] = v
112 history_position += 1
113
114 if history_position >= ary_volume_history.size():
115 history_position = 0
116
117 var avg_v: float = array_avg()
118
119 if param_mouth != null:
120 param_mouth.value = max(param_mouth.value - 0.2, 0.0)
121 if avg_v > 0.1:
122 param_mouth.value = 1.0