animation_animall: remove from contrib: T68332 T63750 e6a1dfbe53be
[blender-addons-contrib.git] / io_import_lipSync_Importer.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 bl_info = {
20     "name": "LipSync Importer & Blinker",
21     "author": "Yousef Harfoush - bat3a)",
22     "version": (0, 5, 2),
23     "blender": (2, 70, 0),
24     "location": "3D window > Tool Shelf",
25     "description": "Plots Moho (Papagayo, Jlipsync, Yolo) file "
26                    "to frames and adds automatic blinking",
27     "warning": "",
28     "wiki_url": "https://wiki.blender.org/index.php?title=Extensions:2.6/Py/"
29                 "Scripts/Import-Export/Lipsync Importer",
30     "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
31     "category": "Import-Export"}
32
33
34 import bpy
35 import re
36 from random import random
37 from bpy.props import (
38         EnumProperty,
39         IntProperty,
40         FloatProperty,
41         StringProperty,
42         PointerProperty,
43         )
44
45 global lastPhoneme
46 lastPhoneme = "nothing"
47
48
49 # add blinking
50 def blinker():
51
52     scn = bpy.context.scene
53
54     if scn.regMenuTypes.enumBlinkTypes == '0':
55         modifier = 0
56     elif scn.regMenuTypes.enumBlinkTypes == '1':
57         modifier = scn.regMenuTypes.blinkMod
58
59     # creating keys with blinkNm count
60     for y in range(scn.regMenuTypes.blinkNm):
61         frame = y * scn.regMenuTypes.blinkSp + int(random() * modifier)
62         createShapekey('blink', frame)
63
64
65 # -- code contributed by dalai felinto adds armature support modified by me --
66
67 bone_keys = {
68     "AI": ('location', 0),
69     "E": ('location', 1),
70     "FV": ('location', 2),
71     "L": ('rotation_euler', 0),
72     "MBP": ('rotation_euler', 1),
73     "O": ('rotation_euler', 2),
74     "U": ('scale', 0),
75     "WQ": ('scale', 1),
76     "etc": ('scale', 2),
77     "rest": ('ik_stretch', -1)
78 }
79
80
81 def lipsyncerBone():
82     # reading imported file & creating keys
83     scene = bpy.context.scene
84     bone = bpy.context.active_pose_bone
85
86     resetBoneScale(bone)
87
88     f = open(scene.regMenuTypes.fpath)  # importing file
89     f.readline()           # reading the 1st line that we don"t need
90
91     for line in f:
92         # removing new lines
93         lsta = re.split("\n+", line)
94
95         # building a list of frames & shapes indexes
96         lst = re.split(":? ", lsta[0])  # making a list of a frame & number
97         frame = int(lst[0])
98
99         for key, attribute in bone_keys.items():
100             if lst[1] == key:
101                 createBoneKeys(key, bone, attribute, frame)
102
103
104 def resetBoneScale(bone):
105     # set the attributes used by papagayo to 0.0
106     for attribute, index in bone_keys.values():
107         if index != -1:
108             # bone.location[0] = 0.0
109             exec("bone.%s[%d] = %f" % (attribute, index, 0.0))
110         else:
111             exec("bone.%s = %f" % (attribute, 0.0))
112
113
114 def addBoneKey(bone, data_path, index=-1, value=None, frame=0, group=""):
115     # set a value and keyframe for the bone
116     # it assumes the 'bone' variable was defined before
117     # and it's the current selected bone
118     frame = bpy.context.scene.frame_current
119     if value is not None:
120         if index != -1:
121             # bone.location[0] = 0.0
122             exec("bone.%s[%d] = %f" % (data_path, index, value))
123         else:
124             exec("bone.%s = %f" % (data_path, value))
125
126     # bone.keyframe_insert("location", 0, 10.0, "Lipsync")
127     exec('bone.keyframe_insert("%s", %d, %f, "%s")' % (data_path, index, frame, group))
128
129
130 # creating keys with offset and eases for a phonem @ the Skframe
131 def createBoneKeys(phoneme, bone, attribute, frame):
132     global lastPhoneme
133
134     scene = bpy.context.scene
135
136     offst = scene.regMenuTypes.offset     # offset value
137     skVlu = scene.regMenuTypes.skscale    # shape key value
138
139     # in case of Papagayo format
140     if scene.regMenuTypes.enumFileTypes == '0':
141         frmIn = scene.regMenuTypes.easeIn     # ease in value
142         frmOut = scene.regMenuTypes.easeOut   # ease out value
143         hldIn = scene.regMenuTypes.holdGap    # holding time value
144
145     # in case of Jlipsync format or Yolo
146     elif scene.regMenuTypes.enumFileTypes == '1':
147         frmIn = 1
148         frmOut = 1
149         hldIn = 0
150
151     # inserting the In key only when phonem change or when blinking
152     if lastPhoneme != phoneme or eval(scene.regMenuTypes.enumModeTypes) == 1:
153         addBoneKey(bone, attribute[0], attribute[1], 0.0, offst + frame - frmIn, "Lipsync")
154
155     addBoneKey(bone, attribute[0], attribute[1], skVlu, offst + frame, "Lipsync")
156     addBoneKey(bone, attribute[0], attribute[1], skVlu, offst + frame + hldIn, "Lipsync")
157     addBoneKey(bone, attribute[0], attribute[1], 0.0, offst + frame + hldIn + frmOut, "Lipsync")
158
159     lastPhoneme = phoneme
160
161
162 # -------------------------------------------------------------------------------
163
164 # reading imported file & creating keys
165 def lipsyncer():
166
167     obj = bpy.context.object
168     scn = bpy.context.scene
169
170     f = open(bpy.path.abspath(scn.regMenuTypes.fpath))  # importing file
171     f.readline()         # reading the 1st line that we don"t need
172
173     for line in f:
174
175         # removing new lines
176         lsta = re.split("\n+", line)
177
178         # building a list of frames & shapes indexes
179         lst = re.split(":? ", lsta[0])  # making a list of a frame & number
180         frame = int(lst[0])
181
182         for key in obj.data.shape_keys.key_blocks:
183             if lst[1] == key.name:
184                 createShapekey(key.name, frame)
185
186
187 # creating keys with offset and eases for a phonem @ the frame
188 def createShapekey(phoneme, frame):
189
190     global lastPhoneme
191
192     scn = bpy.context.scene
193     obj = bpy.context.object
194     objSK = obj.data.shape_keys
195
196     offst = scn.regMenuTypes.offset     # offset value
197     skVlu = scn.regMenuTypes.skscale    # shape key value
198
199     # in case of Papagayo format
200     if scn.regMenuTypes.enumFileTypes == '0':
201         frmIn = scn.regMenuTypes.easeIn     # ease in value
202         frmOut = scn.regMenuTypes.easeOut   # ease out value
203         hldIn = scn.regMenuTypes.holdGap    # holding time value
204
205     # in case of Jlipsync format or Yolo
206     elif scn.regMenuTypes.enumFileTypes == '1':
207         frmIn = 1
208         frmOut = 1
209         hldIn = 0
210
211     # inserting the In key only when phonem change or when blinking
212     if lastPhoneme != phoneme or eval(scn.regMenuTypes.enumModeTypes) == 1:
213         objSK.key_blocks[phoneme].value = 0.0
214         objSK.key_blocks[phoneme].keyframe_insert(
215             "value", -1, offst + frame - frmIn, "Lipsync"
216             )
217
218     objSK.key_blocks[phoneme].value = skVlu
219     objSK.key_blocks[phoneme].keyframe_insert(
220             "value", -1, offst + frame, "Lipsync"
221             )
222     objSK.key_blocks[phoneme].value = skVlu
223     objSK.key_blocks[phoneme].keyframe_insert(
224             "value", -1, offst + frame + hldIn, "Lipsync"
225             )
226     objSK.key_blocks[phoneme].value = 0.0
227     objSK.key_blocks[phoneme].keyframe_insert(
228             "value", -1, offst + frame + hldIn + frmOut, "Lipsync"
229             )
230     lastPhoneme = phoneme
231
232
233 # lipsyncer operation start
234 class btn_lipsyncer(bpy.types.Operator):
235     bl_idname = 'lipsync.go'
236     bl_label = 'Start Processing'
237     bl_description = 'Plots the voice file keys to timeline'
238
239     def execute(self, context):
240
241         scn = context.scene
242         obj = context.active_object
243
244         # testing if object is valid
245         if obj is not None:
246             if obj.type == "MESH":
247                 if obj.data.shape_keys is not None:
248                     if scn.regMenuTypes.fpath != '':
249                         lipsyncer()
250                     else:
251                         print("select a Moho file")
252                 else:
253                     print("No shape keys")
254
255             elif obj.type == "ARMATURE":
256                 if 1:  # XXX add prop test
257                     if scn.regMenuTypes.fpath != '':
258                         lipsyncerBone()
259                     else:
260                         print("select a Moho file")
261                 else:
262                     print("Create Pose properties")
263             else:
264                 print("Object is not a mesh ot bone")
265         else:
266             print("Select object")
267
268         return {'FINISHED'}
269
270
271 # blinker operation start
272 class btn_blinker(bpy.types.Operator):
273     bl_idname = 'blink.go'
274     bl_label = 'Start Processing'
275     bl_description = 'Add blinks at random or specifice frames'
276
277     def execute(self, context):
278
279         obj = context.object
280
281         # testing if object is valid
282         if obj is not None:
283             if obj.type == "MESH":
284                 if obj.data.shape_keys is not None:
285                     for key in obj.data.shape_keys.key_blocks:
286                         if key.name == 'blink':
287                             blinker()
288                             # return
289                 else:
290                     print("No shape keys")
291             else:
292                 print("Object is not a mesh ot bone")
293         else:
294             print("Select object")
295
296         return {'FINISHED'}
297
298
299 # defining custom enumeratos
300 class menuTypes(bpy.types.PropertyGroup):
301
302     enumFileTypes: EnumProperty(
303             items=(('0', 'Papagayo', ''),
304                     ('1', 'Jlipsync Or Yolo', '')
305                     # ('2', 'Retarget', '')
306                     ),
307             name='Choose FileType',
308             default='0'
309             )
310     enumBlinkTypes: EnumProperty(
311             items=(('0', 'Specific', ''),
312                     ('1', 'Random', '')),
313             name='Choose BlinkType',
314             default='0'
315             )
316     enumModeTypes: EnumProperty(
317             items=(('0', 'Lipsyncer', ''),
318                    ('1', 'Blinker', '')),
319             name='Choose Mode',
320             default='0'
321             )
322
323     fpath: StringProperty(
324             name="Import File ",
325             description="Select your voice file",
326             subtype="FILE_PATH"
327             )
328     skscale: FloatProperty(
329             description="Smoothing shape key values",
330             min=0.1, max=1.0,
331             default=0.8
332             )
333     offset: IntProperty(
334             description="Offset your frames",
335             default=0
336             )
337     easeIn: IntProperty(
338             description="Smoothing In curve",
339             min=1, default=3
340             )
341     easeOut: IntProperty(
342             description="Smoothing Out curve",
343             min=1,
344             default=3
345             )
346     holdGap: IntProperty(
347             description="Holding for slow keys",
348             min=0,
349             default=0
350             )
351     blinkSp: IntProperty(
352             description="Space between blinks",
353             min=1,
354             default=100
355             )
356     blinkNm: IntProperty(
357             description="Number of blinks",
358             min=1,
359             default=10
360             )
361     blinkMod: IntProperty(
362             description="Randomzing keyframe placment",
363             min=1,
364             default=10
365             )
366
367
368 # drawing the user interface
369 class LipSyncBoneUI(bpy.types.Panel):
370     bl_space_type = "VIEW_3D"
371     bl_region_type = "UI"
372     bl_label = "Phonemes"
373     bl_category = 'Animation'
374
375     def draw(self, context):
376         layout = self.layout
377         col = layout.column()
378
379         bone = bpy.context.active_pose_bone
380
381         # showing the current object type
382         if bone:  # and if scn.regMenuTypes.enumModeTypes == '0':
383             col.prop(bone, "location", index=0, text="AI")
384             col.prop(bone, "location", index=1, text="E")
385             col.prop(bone, "location", index=2, text="FV")
386
387             if bpy.context.scene.unit_settings.system_rotation == 'RADIANS':
388                 col.prop(bone, "rotation_euler", index=0, text="L")
389                 col.prop(bone, "rotation_euler", index=1, text="MBP")
390                 col.prop(bone, "rotation_euler", index=2, text="O")
391             else:
392                 row = col.row()
393                 row.prop(bone, "rotation_euler", index=0, text="L")
394                 row.label(text=str("%4.2f" % (bone.rotation_euler.x)))
395
396                 row = col.row()
397                 row.prop(bone, "rotation_euler", index=1, text="MBP")
398                 row.label(text=str("%4.2f" % (bone.rotation_euler.y)))
399
400                 row = col.row()
401                 row.prop(bone, "rotation_euler", index=2, text="O")
402                 row.label(text=str("%4.2f" % (bone.rotation_euler.z)))
403
404             col.prop(bone, "scale", index=0, text="U")
405             col.prop(bone, "scale", index=1, text="WQ")
406             col.prop(bone, "scale", index=2, text="etc")
407         else:
408             layout.label(text="No good bone is selected")
409
410
411 # drawing the user interface
412 class LipSyncUI(bpy.types.Panel):
413     bl_space_type = "VIEW_3D"
414     bl_region_type = "TOOL_PROPS"
415     bl_label = "LipSync Importer & Blinker"
416
417     def draw(self, context):
418         obj = bpy.context.active_object
419         scn = bpy.context.scene
420
421         layout = self.layout
422         col = layout.column()
423
424         # showing the current object type
425         if obj is not None:
426             if obj.type == "MESH":
427                 split = col.split(align=True)
428                 split.label(text="The active object is: ", icon="OBJECT_DATA")
429                 split.label(text=obj.name, icon="EDITMODE_HLT")
430
431             elif obj.type == "ARMATURE":  # bone needs to be selected
432                 if obj.mode == "POSE":    # mode needs to be pose
433                     split = col.split(align=True)
434                     split.label(text="The active object is: ", icon="ARMATURE_DATA")
435                     split.label(text=obj.name, icon="EDITMODE_HLT")
436                 else:
437                     col.label(text="You need to select Pose mode!", icon="OBJECT_DATA")
438             else:
439                 col.label(text="The active object is not a Mesh or Armature!", icon="OBJECT_DATA")
440         else:
441             layout.label(text="No object is selected", icon="OBJECT_DATA")
442
443         col.row().prop(scn.regMenuTypes, 'enumModeTypes')
444         col.separator()
445
446         # the lipsyncer panel
447         if scn.regMenuTypes.enumModeTypes == '0':
448             # choose the file format
449             col.row().prop(scn.regMenuTypes, 'enumFileTypes', text=' ', expand=True)
450
451             # Papagayo panel
452             if scn.regMenuTypes.enumFileTypes == '0':
453                 col.prop(scn.regMenuTypes, "fpath")
454
455                 split = col.split(align=True)
456                 split.label(text="Key Value :")
457                 split.prop(scn.regMenuTypes, "skscale")
458
459                 split = col.split(align=True)
460                 split.label(text="Frame Offset :")
461                 split.prop(scn.regMenuTypes, "offset")
462
463                 split = col.split(align=True)
464                 split.prop(scn.regMenuTypes, "easeIn", "Ease In")
465                 split.prop(scn.regMenuTypes, "holdGap", "Hold Gap")
466                 split.prop(scn.regMenuTypes, "easeOut", "Ease Out")
467
468                 col.operator('lipsync.go', text='Plot Keys to the Timeline')
469
470             # Jlipsync & Yolo panel
471             elif scn.regMenuTypes.enumFileTypes == '1':
472                 col.prop(scn.regMenuTypes, "fpath")
473                 split = col.split(align=True)
474                 split.label(text="Key Value :")
475                 split.prop(scn.regMenuTypes, "skscale")
476
477                 split = col.split(align=True)
478                 split.label(text="Frame Offset :")
479                 split.prop(scn.regMenuTypes, "offset")
480
481                 col.operator('lipsync.go', text='Plot Keys to the Timeline')
482
483         # the blinker panel
484         elif scn.regMenuTypes.enumModeTypes == '1':
485             # choose blink type
486             col.row().prop(scn.regMenuTypes, 'enumBlinkTypes', text=' ', expand=True)
487
488             # specific panel
489             if scn.regMenuTypes.enumBlinkTypes == '0':
490                 split = col.split(align=True)
491                 split.label(text="Key Value :")
492                 split.prop(scn.regMenuTypes, "skscale")
493
494                 split = col.split(align=True)
495                 split.label(text="Frame Offset :")
496                 split.prop(scn.regMenuTypes, "offset")
497
498                 split = col.split(align=True)
499                 split.prop(scn.regMenuTypes, "easeIn", "Ease In")
500                 split.prop(scn.regMenuTypes, "holdGap", "Hold Gap")
501                 split.prop(scn.regMenuTypes, "easeOut", "Ease Out")
502                 col.prop(scn.regMenuTypes, "blinkSp", "Spacing")
503                 col.prop(scn.regMenuTypes, "blinkNm", "Times")
504                 col.operator('blink.go', text='Add Keys to the Timeline')
505
506             # Random panel
507             elif scn.regMenuTypes.enumBlinkTypes == '1':
508                 split = col.split(align=True)
509                 split.label(text="Key Value :")
510                 split.prop(scn.regMenuTypes, "skscale")
511
512                 split = col.split(align=True)
513                 split.label(text="Frame Start :")
514                 split.prop(scn.regMenuTypes, "offset")
515
516                 split = col.split(align=True)
517                 split.prop(scn.regMenuTypes, "easeIn", "Ease In")
518                 split.prop(scn.regMenuTypes, "holdGap", "Hold Gap")
519                 split.prop(scn.regMenuTypes, "easeOut", "Ease Out")
520
521                 split = col.split(align=True)
522                 split.prop(scn.regMenuTypes, "blinkSp", "Spacing")
523                 split.prop(scn.regMenuTypes, "blinkMod", "Random Modifier")
524                 col.prop(scn.regMenuTypes, "blinkNm", "Times")
525                 col.operator('blink.go', text='Add Keys to the Timeline')
526
527
528 # registering the script
529 def register():
530     bpy.utils.register_module(__name__)
531     bpy.types.Scene.regMenuTypes = PointerProperty(type=menuTypes)
532
533
534 def unregister():
535     bpy.utils.unregister_module(__name__)
536     del bpy.types.Scene.regMenuTypes
537
538
539 if __name__ == "__main__":
540     register()