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