Skip to content
Snippets Groups Projects
io_import_sound_to_anim.py 44.84 KiB
#!/usr/bin/python3
# To change this template, choose Tools | Templates
# and open the template in the editor.
#  ***** GPL LICENSE BLOCK *****
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.
#  All rights reserved.
#  ***** GPL LICENSE BLOCK *****

bl_info = {
    "name": "Import: Sound to Animation",
    "author": "Vlassius",
    "version": (0, 22),
    "blender": (2, 57, 0),
    "api": 37023,
    "location": "Select a object -> go to the Object tab ->  Import Movement From Wav File",
    "description": "Extract movement from sound file. See the Object Panel at the end.",
    "warning": "",
    "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Import-Export/Import_Movement_Fom_Audio_File",
    "tracker_url": "",
    "category": "Import-Export"}

"""
-- Extract movement from sound file, to help in animation - import script --<br> 

- NOTES:
- This script takes a wav file and get sound "movement" to help you in sync the movement to words in the wave file. <br>
- Blender 2.5.7


-v 0.22Beta- 
    Included 
    Camera Rotation
    Empty Location-Rotation-Scale
    

-v 0.21Beta- 
    Changed just the meta informations like version and wiki page.
    

-v 0.20 Beta- 
    New Panel
    
    
-v 0.1.5 Beta- 
    Change in API-> Properties
    Included the button "Get FPS from Scene" due to a problem to get it in the start 
    Return to Frame 1 after import
    Filter .wav type file (by batFINGER)
    
-v 0.1.4 Beta- 
    If choose negative in rotation, auto check the rotation negative to Bones
    change in register()  unregister():

-v 0.1.3 Beta- 
    File Cleanup
    Change to bl_info. 
    Cosmetic Changes. 
    Minor change in API in how to define buttons. 
    Adjust in otimization.

-v 0.1.2 Beta
change in API- Update function bpy.ops.anim.keyframe_insert_menu 

-v 0.1.1 Beta
change in API- Update property of  window_manager.fileselect_add

-v 0.1.0 Beta
new - Added support to LAMP object         
new - improved flow to import
new - check the type of object before show options
bug - option max. and min. values
change- Updated scene properties for changes in property API.
        See http://lists.blender.org/pipermail/bf-committers/2010-September/028654.html

new flow:
          1) Process the sound file
          2) Show Button to import key frames 


- v0.0.4 ALPHA
new - Added destructive optimizer option - LostLestSignificativeDigit lost/total -> 10/255 default
new - Visual Graph to samples
new - Option to just process audio file and do not import - this is to help adjust the audio values
new - Get and show automatically the FPS (in proper field) information taking the information from scene
bug- Display sensitivity +1
bug - Corrected location of the script in description

- v0.0.3
Main change: Corrected to work INSIDE dir /install/linux2/2.53/scripts/addons
Corrected position of label "Rotation Negative"
Added correct way to deal with paths in Python os.path.join - os.path.normpath

- v0.0.2
Corrected initial error (Register() function)
Corrected some labels R. S. L.
Turned off "object field" for now
Changed target default to Z location 

- v0.0.1
Initial version



Credit to:
Vlassius

- http://vlassius.com.br
- vlassius@vlassius.com.br

"""

import bpy
from bpy.props import *
#from io_utils import ImportHelper
import wave

#TODO
#    Arrumar - não tem rotacao para objeto - so transformacao
#    alterar OBJETO NOMEADO

#    Colocar Escolha do Canal!!
#
#
#    colocar relatorio de samples min, max, 90%, colocar sugestao subir/descer audio sense 
#
#
#   colocar CANCELAR com ESC
#
#



#para deixar array global 
def _Interna_Globals(BytesDadosTotProcess, context):
    global array
    
    array= bytearray(BytesDadosTotProcess)  # cria array  
    context.scene.imp_sound_to_anim.bArrayCriado=True


def wavimport(context):
    #================================================================================================== 
    # Insert Key Frame
    #================================================================================================== 
    
#    print("Inside Wave Import...")   
    
    iDivScala= int(context.scene.imp_sound_to_anim.action_escale)     #scala do valor do movimento. [se =1 - 0 a 255 ] [se=255 - 0,00000 a 1,00000] [se=1000 - 0 a 0.255]

    bNaoValorIgual=True
    if context.scene.imp_sound_to_anim.action_valor_igual: bNaoValorIgual= False    # não deixa repetir valores
    
    iStartFrame= int(context.scene.imp_sound_to_anim.frames_initial)

    iMaxValue= context.scene.imp_sound_to_anim.action_max_value
    iMinValue= context.scene.imp_sound_to_anim.action_min_value
    
    bEscala=bRotacao=bEixo=False   
    if context.scene.imp_sound_to_anim.import_type=='imp_t_Scale':
        bEscala=True;
        
    if context.scene.imp_sound_to_anim.import_type=='imp_t_Rotation':
        bRotacao=True;

    if context.scene.imp_sound_to_anim.import_type=='imp_t_Location':
        bEixo=True;

    # atencao, nao é boolean
    iEixoXneg= iEixoYneg= iEixoZneg=1
    # atencao, nao é boolean
    iRotationNeg=1
    # atencao, nao é boolean
    iEscalaYneg= iEscalaZneg= iEscalaXneg=1
    bEixoX=bEixoY=bEixoZ=bEscalaX=bEscalaY=bEscalaZ=bRotationX=bRotationY=bRotationZ=False 

    # LOCAL 1
    if context.scene.imp_sound_to_anim.import_where1== 'imp_w_x':
        bEixoX=True
        bEscalaX=True
        bRotationX=True
        
    if context.scene.imp_sound_to_anim.import_where1== 'imp_w_y':
        bEixoY=True
        bEscalaY=True
        bRotationY=True

    if context.scene.imp_sound_to_anim.import_where1== 'imp_w_z':
        bEixoZ=True
        bEscalaZ=True
        bRotationZ=True

    if context.scene.imp_sound_to_anim.import_where1== 'imp_w_-x':
        bEixoX=True
        bEscalaX=True
        bRotationX=True
        iEixoXneg=-1
        iEscalaXneg=-1
        iRotationNeg=-1

    if context.scene.imp_sound_to_anim.import_where1== 'imp_w_-y':
        bEixoY=True
        bEscalaY=True
        bRotationY=True
        iEixoYneg=-1
        iRotationNeg=-1
        iEscalaYneg=-1

    if context.scene.imp_sound_to_anim.import_where1== 'imp_w_-z':
        bEixoZ=True
        bEscalaZ=True
        bRotationZ=True
        iEixoZneg=-1
        iRotationNeg=-1
        iEscalaZneg=-1


    # LOCAL 2
    if context.scene.imp_sound_to_anim.import_where2== 'imp_w_x':
        bEixoX=True
        bEscalaX=True
        bRotationX=True
        
    if context.scene.imp_sound_to_anim.import_where2== 'imp_w_y':
        bEixoY=True
        bEscalaY=True
        bRotationY=True

    if context.scene.imp_sound_to_anim.import_where2== 'imp_w_z':
        bEixoZ=True
        bEscalaZ=True
        bRotationZ=True

    if context.scene.imp_sound_to_anim.import_where2== 'imp_w_-x':
        bEixoX=True
        bEscalaX=True
        bRotationX=True
        iEixoXneg=-1
        iEscalaXneg=-1
        iRotationNeg=-1

    if context.scene.imp_sound_to_anim.import_where2== 'imp_w_-y':
        bEixoY=True
        bEscalaY=True
        bRotationY=True
        iEixoYneg=-1
        iRotationNeg=-1
        iEscalaYneg=-1

    if context.scene.imp_sound_to_anim.import_where2== 'imp_w_-z':
        bEixoZ=True
        bEscalaZ=True
        bRotationZ=True
        iEixoZneg=-1
        iRotationNeg=-1
        iEscalaZneg=-1


    # LOCAL 3
    if context.scene.imp_sound_to_anim.import_where3== 'imp_w_x':
        bEixoX=True
        bEscalaX=True
        bRotationX=True
        
    if context.scene.imp_sound_to_anim.import_where3== 'imp_w_y':
        bEixoY=True
        bEscalaY=True
        bRotationY=True

    if context.scene.imp_sound_to_anim.import_where3== 'imp_w_z':
        bEixoZ=True
        bEscalaZ=True
        bRotationZ=True

    if context.scene.imp_sound_to_anim.import_where3== 'imp_w_-x':
        bEixoX=True
        bEscalaX=True
        bRotationX=True
        iEixoXneg=-1
        iEscalaXneg=-1
        iRotationNeg=-1

    if context.scene.imp_sound_to_anim.import_where3== 'imp_w_-y':
        bEixoY=True
        bEscalaY=True
        bRotationY=True
        iEixoYneg=-1
        iRotationNeg=-1
        iEscalaYneg=-1

    if context.scene.imp_sound_to_anim.import_where3== 'imp_w_-z':
        bEixoZ=True
        bEscalaZ=True
        bRotationZ=True
        iEixoZneg=-1
        iRotationNeg=-1
        iEscalaZneg=-1

    iMinBaseX=iMinScaleBaseX=context.scene.imp_sound_to_anim.action_offset_x
    iMinBaseY=iMinScaleBaseY=context.scene.imp_sound_to_anim.action_offset_y
    iMinBaseZ=iMinScaleBaseZ=context.scene.imp_sound_to_anim.action_offset_z

    #escala inicia com 1 e não com zero
    iRotationAxisBaseX=context.scene.imp_sound_to_anim.action_offset_x  +1
    iRotationAxisBaseY=context.scene.imp_sound_to_anim.action_offset_y  +1
    iRotationAxisBaseZ=context.scene.imp_sound_to_anim.action_offset_z  +1

    #Added destructive optimizer option - LostLestSignificativeDigit lost/total
    iDestructiveOptimizer=context.scene.imp_sound_to_anim.optimization_destructive

    bLimitValue=False    #limita ou nao o valor - velocidade        

    if iMinValue<0: iMinValue=0
    if iMaxValue>255: iMaxValue=255
    if iMinValue>255: iMinValue=255
    if iMaxValue<0: iMaxValue=0    
    if iMinValue!= 0: bLimitValue= True
    if iMaxValue!= 255: bLimitValue= True


    print('')
    print("================================================================")   
    from time import strftime
    print(strftime("Start Import:  %H:%M:%S"))
    print("================================================================")   
    print('')

    ilocationXAnt=0
    ilocationYAnt=0
    ilocationZAnt=0
    iscaleXAnt=0
    iscaleYAnt=0
    iscaleZAnt=0
    iRotateValAnt=0
    iSumOptimizerP1=0
    iSumOptimizerP2=0
    iSumOptimizerP3=0
    iSumImportFrames=0

    # variavel global _Interna_Globals
    if context.scene.imp_sound_to_anim.bArrayCriado:
        for i in range(len(array)):

            ival=array[i]/iDivScala
            #valor pequeno demais, vai dar zero na hora de aplicar
            if ival < 0.001: 
                 array[i]=0
                 ival=0
                 #print ("Valor Pequeno Demais, Zerando")
               
            # opcao de NAO colocar valores iguais sequenciais
            if i>0 and bNaoValorIgual and array[i-1]== array[i]:
                print("Importing Blender Frame: "+str(i)+"\tof "+str(len(array)-1) + "\t(skipped by optimizer)")
                iSumOptimizerP3+=1
                
            else:                 

                # otimizacao - não preciso mais que 2 valores iguais. pular key frame intermediario - Ex b, a, -, -, -, a
                # tambem otimiza pelo otimizador com perda
                if i>0 and i< len(array)-1 and abs(array[i] - array[i-1])<=iDestructiveOptimizer and abs(array[i] - array[i+1])<=iDestructiveOptimizer:# valor atual == anterior e posterior -> pula 
                        print("Importing Blender Frame: "+str(i)+"\tof "+str(len(array)-1) + "\t(skipped by optimizer)")
                        if iDestructiveOptimizer>0 and array[i] != array[i-1] or array[i] != array[i+1]: 
                            iSumOptimizerP1+=1
                            #print(array[i], iSumOptimizerP1) 
                        else: iSumOptimizerP2+=1
                else:           
                        if bLimitValue:
                            if array[i] > iMaxValue: array[i]=iMaxValue
                            if array[i] < iMinValue: array[i]=iMinValue

                        ival=array[i]/iDivScala
                        #print("teste ival= " + str(ival))    ####
                        #passa para float com somente 3 digitos caso seja float
                        m_ival=ival*1000
                        if int(m_ival) != m_ival:
                            ival= int(m_ival)
                            ival = ival /1000
                            #print("Novo ival= " + str(ival))    ####

                        bpy.context.scene.frame_current = i+iStartFrame

                        if bpy.context.active_object.type=='MESH' or bpy.context.active_object.type=='CAMERA' or bpy.context.active_object.type=='EMPTY':   #precisa fazer objeto ativo
                            if bEixo:                   
                                if bEixoX: bpy.context.active_object.location.x = ival*iEixoXneg+iMinBaseX
                                if bEixoY: bpy.context.active_object.location.y = ival*iEixoYneg+iMinBaseY
                                if bEixoZ: bpy.context.active_object.location.z = ival*iEixoZneg+iMinBaseZ               
                                #print("mesh ou camera em eixo")  ####
                            
                            if bEscala:
                                if bEscalaX: bpy.context.active_object.scale.x = ival*iEscalaXneg+iMinScaleBaseX
                                if bEscalaY: bpy.context.active_object.scale.y = ival*iEscalaYneg+iMinScaleBaseY
                                if bEscalaZ: bpy.context.active_object.scale.z = ival*iEscalaZneg+iMinScaleBaseZ 

                        # 'ARMATURE' or ('MESH' and bRotacao) or ('CAMERA' and bRotacao) or 'LAMP' or 'EMPTY' and bRotacao)
                        if bpy.context.active_object.type=='ARMATURE' or (bpy.context.active_object.type=='MESH' and bRotacao) or (bpy.context.active_object.type=='CAMERA' and bRotacao) or bpy.context.active_object.type=='LAMP' or (bpy.context.active_object.type=='EMPTY' and bRotacao):

                                ###############  BONE ######################
                                if bpy.context.active_object.type=='ARMATURE':   #precisa ser objeto ativo. Nao achei como passar para editmode
                                    if bpy.context.mode!= 'POSE':    #posemode 
                                        bpy.ops.object.posemode_toggle()
                                        #print("bone pose mode")   ####
    
                                ###############  ALL ######################    
                                if bEixo:
                                    if ilocationXAnt!=0 or ilocationYAnt!=0 or ilocationZAnt!=0: 
                                        bpy.ops.transform.translate(value=(ilocationXAnt*-1, ilocationYAnt*-1, ilocationZAnt*-1), constraint_axis=(bEixoX, bEixoY,bEixoZ), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)  
                                    
                                    ilocationX=ilocationY=ilocationZ=0                
                                    if bEixoX: ilocationX = ival*iEixoXneg+iMinBaseX
                                    if bEixoY: ilocationY = ival*iEixoYneg+iMinBaseY
                                    if bEixoZ: ilocationZ = ival*iEixoZneg+iMinBaseZ  
                                    bpy.ops.transform.translate(value=(ilocationX, ilocationY, ilocationZ), constraint_axis=(bEixoX, bEixoY,bEixoZ), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
                                    ilocationXAnt= ilocationX
                                    ilocationYAnt= ilocationY
                                    ilocationZAnt= ilocationZ 
            
                                if bEscala:
                                    if iscaleXAnt!=0 or iscaleYAnt!=0 or iscaleZAnt!=0:
                                        tmpscaleXAnt=0
                                        tmpscaleYAnt=0
                                        tmpscaleZAnt=0
                                        if iscaleXAnt: tmpscaleXAnt=1/iscaleXAnt
                                        if iscaleYAnt: tmpscaleYAnt=1/iscaleYAnt
                                        if iscaleZAnt: tmpscaleZAnt=1/iscaleZAnt 
                                        bpy.ops.transform.resize(value=(tmpscaleXAnt, tmpscaleYAnt, tmpscaleZAnt ), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)  
                                    
                                    iscaleX=iscaleY=iscaleZ=0                
                                    if bEscalaX: iscaleX = ival*iEscalaXneg+iMinScaleBaseX
                                    if bEscalaY: iscaleY = ival*iEscalaYneg+iMinScaleBaseY
                                    if bEscalaZ: iscaleZ = ival*iEscalaZneg+iMinScaleBaseZ                        
                                    bpy.ops.transform.resize(value=(iscaleX, iscaleY, iscaleZ), constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
                                    iscaleXAnt= iscaleX
                                    iscaleYAnt= iscaleY
                                    iscaleZAnt= iscaleZ                       
                                    
                                if bRotacao:                        
                                    if iRotateValAnt!=0: 
                                        bpy.ops.transform.rotate(value= (iRotateValAnt*-1,), axis=(iRotationAxisBaseX, iRotationAxisBaseY, iRotationAxisBaseZ), constraint_axis=(bRotationX, bRotationY, bRotationZ), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)                                                                   
                                        
                                    bpy.ops.transform.rotate(value= (ival*iRotationNeg,), axis=(iRotationAxisBaseX, iRotationAxisBaseY, iRotationAxisBaseZ), constraint_axis=(bRotationX, bRotationY, bRotationZ), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False)
                                    iRotateValAnt= ival*iRotationNeg
                       
                        if bEixo and not bEscala and not bRotacao:
                             bpy.ops.anim.keyframe_insert_menu(type='Location')
                                                        
                        if bRotacao and not bEixo and not bEscala:
                            bpy.ops.anim.keyframe_insert_menu(type='Rotation')
                            
                        if bEscala and not bEixo and not bRotacao:
                            bpy.ops.anim.keyframe_insert_menu(type='Scaling')               
            
                        if bEixo and bRotacao:
                            bpy.ops.anim.keyframe_insert_menu(type='LocRot')
                           
                        if bEscala and bEixo:
                            bpy.ops.anim.keyframe_insert_menu(type='LocScale')
            
                        if bEixo and bRotacao and bEscala:
                            bpy.ops.anim.keyframe_insert_menu(type='LocRotScale')
            
                        if bEscala and bRotacao:
                            bpy.ops.anim.keyframe_insert_menu(type='RotScale')
                                     
                    
                        print("Importing Blender Frame: "+str(i)+"\tof "+str(len(array)-1) + "\tValue: "+ str(ival))
#                        context.scene.imp_sound_to_anim.Info_Import= "Importing Frame: "+str(i)+" of "+str(len(array)-1)
#                        bpy.context.scene.imp_sound_to_anim.update()   

                        iSumImportFrames+=1
                        
                # FIm do ELSE otimizador 
            # Fim bNaoValorIgual

    bpy.context.scene.frame_current = 1

    context.scene.imp_sound_to_anim.Info_Import="Done. Imported " + str(iSumImportFrames) + " Frames" 
#    context.scene.imp_sound_to_anim.bArrayCriado=False   # nao precisa importar novamente sem processar audio novamente
    
    print('')
    print("================================================================")
    print("Imported: " +str(iSumImportFrames) + " Key Frames")
    print("Optimizer Pass 1 prepared to optimize: " +str(iSumOptimizerP1) + " blocks of Frames")
    print("Optimizer Pass 2 has optimized: " +str(iSumOptimizerP2) + " Frames")
    print("Optimizer Pass 3 has optimized: " +str(iSumOptimizerP3) + " Frames")
    print("Optimizer has optimized: " +str(iSumOptimizerP1 + iSumOptimizerP2+iSumOptimizerP3) + " Frames")       
    print(strftime("End Import:  %H:%M:%S - by Vlassius"))
    print("================================================================")   
    print('')
   
    
#================================================================================================== 
# Sound Converter
#================================================================================================== 

def SoundConv(File, DivSens, Sensibil, Resol, context):

    try:
        Wave_read= wave.open(File, 'rb')
    except IOError as e:
        print("File Open Error: ", e)
        return False

    NumCh=      Wave_read.getnchannels()
    SampW=      Wave_read.getsampwidth() // NumCh # 8, 16, 24 32 bits
    FrameR=     Wave_read.getframerate() // NumCh
    NumFr=      Wave_read.getnframes()
    ChkCompr=   Wave_read.getcomptype()
    
    if ChkCompr != "NONE":
        print('Formato de Compressão Nao Suportado ', ChkCompr)
        return False
    
    # com 8 bits/S - razao Sample/s por resolucao
    # usado para achar contorno da onda - achando picos 
    BytesResol= int(FrameR/Resol)
    
    # com 8 bits/S - razao Sample/s por resolucao
    # tamanho do array
    BytesDadosTotProcess= NumFr // BytesResol 

    # inicia array
    _Interna_Globals(BytesDadosTotProcess, context)
    
    print('')
    print("================================================================")   
    from time import strftime
    print(strftime("Go!  %H:%M:%S"))
    print("================================================================")   
    print('')   
    print('Audio Time: \t ' + str(NumFr//FrameR) + 's (' + str(NumFr//FrameR//60) + 'min)')
    print('Interactions: \t', BytesDadosTotProcess)
    print('Audio Frames: \t', NumFr)
    print('Frame Rate: \t', FrameR)
    print('Chan in File: \t', NumCh)
    print('Bit/Samp/Chan: \t', SampW*8)
    print('Channel in use:\t 1')
    print('Sensitivity: \t', Sensibil+1)
    print('DivMovim: \t', DivSens)
    print(' ')

#    _array= bytearray(BytesDadosTotProcess)  # cria array
    j=0  # usado de indice
    
    print ("Sample->[value]\tAudio Frame # \t\t[Graph Value]")

    # laço total leitura bytes
    # armazena dado de pico
    looptot= int(BytesDadosTotProcess // DivSens)
    for jj in range(looptot):      
        
        # caso de 2 canais (esterio)
        # uso apenas 2 bytes em 16 bits
        # [0] e [1] para CH L
        # [2] e [3] para CH R      
        # uso 1 byte se em 8 bits              
        ValorPico=0
        for i in range(BytesResol):            
            frame = Wave_read.readframes(DivSens)            
            if len(frame)==0: break

            if SampW ==1:
                if frame[0]> ValorPico: 
                    ValorPico= frame[0]               

            if SampW ==2:                # frame[0] baixa       frame[1] ALTA BIT 1 TEM SINAL
                if Sensibil ==0:
                    if frame[1] <127:    # se bit1 =0, usa o valor
                        fr = frame[1] << 1
                        if fr > ValorPico: 
                            ValorPico= fr               
                        
                if Sensibil ==4:
                    if frame[1] < 127:     # se bit1 =0, usa o valor
                        frame0= ((frame[0] & 0b11111100) >> 2) | ((frame[1] & 0b00000011) << 6)                        
                        if frame0 > ValorPico: 
                                ValorPico= frame0               

                if Sensibil ==3:
                    if frame[1] < 127:    # se bit1 =0, usa o valor
                        frame0= ((frame[0] & 0b11110000) >> 4) | ((frame[1] & 0b00001111) << 4)                        
                        if frame0 > ValorPico: 
                                ValorPico= frame0               

                if Sensibil ==2:
                    if frame[1] < 127:    # se bit1 =0, usa o valor
                        frame0= ((frame[0] & 0b11100000) >> 5) | ((frame[1] & 0b00011111) << 3)                        
                        if frame0 > ValorPico: 
                                ValorPico= frame0               

                if Sensibil ==1:
                    if frame[1] < 127:    # se bit1 =0, usa o valor
                        frame0= ((frame[0] & 0b11000000) >> 6) | ((frame[1] & 0b00111111) << 2)                        
                        if frame0 > ValorPico: 
                                ValorPico= frame0               

                if Sensibil ==5:
                    if frame[0] > ValorPico: 
                        ValorPico= frame[0]               

        for ii in range(DivSens):           
            array[j]=ValorPico  # valor de pico encontrado
            j +=1;           # incrementa indice prox local
        
        igraph= ValorPico//10
        stgraph="["        
        for iii in range(igraph): 
            stgraph+="+" 

        for iiii in range(26-igraph): 
            stgraph+=" " 
        stgraph+="]"
        
        print ("Sample-> " + str(ValorPico) + "\tAudio Frame # " + str(jj) + " of " + str(looptot-1) + "\t"+ stgraph)
                
# fim
#    print(_array)
    context.scene.imp_sound_to_anim.Info_Import= "Click \"Import Key frames\" to begin import" #this set the initial text

    print("================================================================")   
    print(strftime("End Process:  %H:%M:%S"))
    print("================================================================")   

    try:
        Wave_read.close()
    except:
        print('File Close Error')
        
    return

#
#
#================================================================================================== 
#================================================================================================== 
#================================================================================================== 
#
#
# BLENDER Configuration - Blender Beta
#
#
#================================================================================================== 
#================================================================================================== 
#================================================================================================== 
#    
#

class VIEW3D_PT_CustomMenuPanel(bpy.types.Panel):
    bl_space_type = "PROPERTIES"
    bl_region_type = "WINDOW"
    bl_context = "object"
    bl_label = "Import Movement From Wav File"
    bl_options = {'DEFAULT_CLOSED'}   

    def draw(self, context):
        layout = self.layout

        b=bpy.context.active_object.type=='EMPTY' or bpy.context.active_object.type=='ARMATURE' or bpy.context.active_object.type=='MESH' or bpy.context.active_object.type=='CAMERA' or bpy.context.active_object.type=='LAMP' 
        if not b:
            row=layout.row()
            row.label(text="The Selected Object is: type \"" + bpy.context.active_object.type + "\", and it is not supported.") 
            row=layout.row()
            row.label(text="Supported Object are Type: Armature, Mesh, Camera and Lamp") 
            row=layout.row()
        else:

            #print(context.scene.imp_sound_to_anim.bTypeImport)
    
            if context.scene.imp_sound_to_anim.bTypeImport == 0:
                #To use Direct 
                #mount panel to Direct animation 
                row=layout.row()
                layout.operator("import.sound_animation_botao_udirect")           


            #-----------------------------
            #Direct Animation
            #-----------------------------
            if context.scene.imp_sound_to_anim.bTypeImport == 1:
                row=layout.row()
                row.label(text="Select a Object, choose where to import,") 
                row=layout.row()
                row.label(text="click button \"Process Wav\" and choose a wave file,")
                row=layout.row()
                row.label(text="Check the informations of processed wave file (in the terminal),")
                row=layout.row()
                row.label(text="Click button \"Import Key Frames\",")
                row=layout.row()        
                row.label(text="run the animation (alt A) and enjoy")        

                row=layout.row()        
                row.prop(context.scene.imp_sound_to_anim,"audio_sense")
                row=layout.row()
                row.prop(context.scene.imp_sound_to_anim,"frames_per_second")
                row=layout.row()     
                row.prop(context.scene.imp_sound_to_anim,"action_per_second")
                row=layout.row()
                row.prop(context.scene.imp_sound_to_anim,"action_escale")
    
                row=layout.row()
                row.prop(context.scene.imp_sound_to_anim,"import_type")

                #coluna
                column= layout.column()
                split=column.split(percentage=0.55)
                col=split.column()

                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"import_where1")

                col=split.column()          
                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"import_where2")

                col=split.column()          
                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"import_where3")

                row=layout.row()   
                row.label(text='Optional Configurations:')

                #coluna
                column= layout.column()
                split=column.split(percentage=0.5)
                col=split.column()

                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"frames_initial")
                
                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"action_min_value")

                col=split.column()          

                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"optimization_destructive")               

                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"action_max_value")
                
                row=layout.row()
                row.prop(context.scene.imp_sound_to_anim,"action_valor_igual")        

                column= layout.column()
                split=column.split(percentage=0.5)
                col=split.column()

                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"action_offset_x")

                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"action_offset_z")

                col=split.column()          
                
                row=col.row()
                row.prop(context.scene.imp_sound_to_anim,"action_offset_y")

                row=col.row()
                row.label(text='auto +1 to Scale')
                
                #operator button
                #OBJECT_OT_Botao_Go => Botao_GO
                row=layout.row()
                layout.operator(OBJECT_OT_Botao_Go.bl_idname)
    
                row=layout.row()
                if context.scene.imp_sound_to_anim.bArrayCriado:          
                    row.label(text=context.scene.imp_sound_to_anim.Info_Import)
                    layout.operator(OBJECT_OT_Botao_Import.bl_idname)
                    row=layout.row()
    



    
            #-----------------------------
            #Use Driver
            #-----------------------------            
            if context.scene.imp_sound_to_anim.bTypeImport == 2:            

                row=layout.row()        
                row.prop(context.scene.imp_sound_to_anim,"audio_sense")
                row=layout.row()
                row.prop(context.scene.imp_sound_to_anim,"frames_per_second")
                row=layout.row()     
                row.prop(context.scene.imp_sound_to_anim,"action_per_second")

                row=layout.row()
                layout.operator(ImportWavFile.bl_idname)
            



class ImpSoundtoAnim(bpy.types.PropertyGroup):
    
                    
        bArrayCriado = IntProperty(name="", 
            description="Avisa que rodou process de som",
            default=0)
 
        #Nome do objeto
        Info_Import = StringProperty(name="",        
            description="Info about Import",
            maxlen= 1024,
            default= "")#this set the initial text
        
        #    iAudioSensib=0    #sensibilidade volume do audio 0 a 5. Quanto maior, mais sensibilidade
        audio_sense = IntProperty(name="Audio Sens",         
            description="Audio Sensibility.",
            min=1,
            max=6,
            step=1,
            default= 1)
        
        #    iFramesPorSeg=15  #Frames por segundo para key frame
        #fps= (bpy.types.Scene) bpy.context.scene.render.fps
        frames_per_second = IntProperty(name="#Frames/s",        
            description="Frames you want per second. Better match your set up in Blender scene.",
            min=1,
            max=120,
            step=1)    
              
        #    iMovPorSeg=1      #Sensibilidade de movimento. 3= 3 movimentos por segundo
        action_per_second = IntProperty(name="Act/s",        
            description="Actions per second",
            min=1,
            max=120,
            step=1,                        
            default= 4)#this set the initial text
        
        #    iDivScala=200     #scala do valor do movimento. [se =1 - 0 a 255 ] [se=255 - 0,00000 a 1,00000] [se=1000 - 0 a 0.255]
        action_escale = IntProperty(name="Scale",        
            description="Scale the result values. (if 1, values from 0 to 255) (if 1000, values from 0 to 0.255)",
            min=1,
            max=99999,
            step=100,                                    
            default= 100)#this set the initial text
        
        #    iMaxValue=255
        action_max_value = IntProperty(name="Clip Max",        
            description="Set the max value (clip higher values).",
            min=1,
            max=255,
            step=1,                                    
            default= 255)#this set the initial text
        
        #    iMinValue=0
        action_min_value = IntProperty(name="Clip Min",        
            description="Set the min value. (clip lower values)",
            min=0,
            max=255,
            step=1,                                    
            default= 0)#this set the initial text
        
        #    iStartFrame=0#
        frames_initial = IntProperty(name="Frame Ini",        
            description="Where to start to put the computed values.",
            min=0,
            max=999999999,
            step=1,                             
            default= 0)
        
        
        #########  ADICIONAIS ################
        
        action_offset_x = FloatProperty(name="OffsetX",        
            description="Offset X Values",
            min=-999999,
            max=999999,
            step=1,                       
            default= 0)

        action_offset_y = FloatProperty(name="OffsetY",        
            description="Offset Y Values",
            min=-999999,
            max=999999,
            step=1,                       
            default= 0)

        action_offset_z = FloatProperty(name="OffsetZ",        
            description="Offset Z Values",
            min=-999999,
            max=999999,
            step=1,                       
            default= 0)


        import_type= EnumProperty(items=(('imp_t_Scale', "Scale", "Apply to Scale"),
                                         ('imp_t_Rotation', "Rotation", "Apply to Rotation"),
                                         ('imp_t_Location', "Location", "Apply to Location")                                                                                 
                                        ),
                                 name="Property",
                                 description= "Property to Import",
                                 default='imp_t_Location')

        import_where1= EnumProperty(items=(('imp_w_-z', "-z", "Apply to -z"),
                                          ('imp_w_-y', "-y", "Apply to -y"),
                                          ('imp_w_-x', "-x", "Apply to -x"),
                                          ('imp_w_z', "z", "Apply to z"),
                                          ('imp_w_y', "y", "Apply to y"),
                                          ('imp_w_x', "x", "Apply to x")
                                        ),
                                 name=" ",
                                 description= "Where to Import",
                                 default='imp_w_z')

        import_where2= EnumProperty(items=(('imp_w_none', "None", ""),
                                          ('imp_w_-z', "-z", "Apply to -z"),
                                          ('imp_w_-y', "-y", "Apply to -y"),
                                          ('imp_w_-x', "-x", "Apply to -x"),
                                          ('imp_w_z', "z", "Apply to z"),
                                          ('imp_w_y', "y", "Apply to y"),
                                          ('imp_w_x', "x", "Apply to x")
                                        ),
                                 name="",
                                 description= "Where to Import",
                                 default='imp_w_none')

        import_where3= EnumProperty(items=(('imp_w_none', "None", ""),
                                          ('imp_w_-z', "-z", "Apply to -z"),
                                          ('imp_w_-y', "-y", "Apply to -y"),
                                          ('imp_w_-x', "-x", "Apply to -x"),
                                          ('imp_w_z', "z", "Apply to z"),
                                          ('imp_w_y', "y", "Apply to y"),
                                          ('imp_w_x', "x", "Apply to x")
                                        ),
                                 name="",
                                 description= "Where to Import",
                                 default='imp_w_none')

        
        ############## Propriedades boolean  ###################

        #  INVERTIDO!!!  bNaoValorIgual=True    # não deixa repetir valores     INVERTIDO!!!   
        action_valor_igual = BoolProperty(name="Hard Transition",
            description="Use to movements like a mouth, to a arm movement, maybe you will not use this.",
            default=1)
        
        #
        #  Optimization
        #                 
        optimization_destructive = IntProperty(name="Optimization",        
            description="Hi value, Hi optimization, Hi loss of information.",
            min=0,
            max=255,
            step=10,
            default= 10)

        # import as driver or direct
        # not defined
        # Direct=1
        # Driver=2
        bTypeImport = IntProperty(name="",        
            description="Import Direct or Driver",
            default=0)
        

           
from bpy.props import *

def WavFileImport(self, context):     
    self.layout.operator(ImportWavFile.bl_idname, text="Import a wav file", icon='PLUGIN') 




####################################################################################
#
#     USE DIRECT 
#
####################################################################################

class OBJECT_OT_Botao_uDirect(bpy.types.Operator):
    '''Import as Direct Animation'''
    bl_idname = "import.sound_animation_botao_udirect"
    bl_label = "Direct to a Property"

    def execute(self, context):
        context.scene.imp_sound_to_anim.bTypeImport= 1
        if context.scene.imp_sound_to_anim.frames_per_second == 0:
             context.scene.imp_sound_to_anim.frames_per_second= bpy.context.scene.render.fps
    
    def invoke(self, context, event):
        self.execute(context)
        return {'RUNNING_MODAL'}



####################################################################################
#
#     bptao iniciar process som
#
####################################################################################

class OBJECT_OT_Botao_Import(bpy.types.Operator):
    '''Import Key Frames to Blender'''
    bl_idname = "import.sound_animation_botao_import"
    bl_label = "Import Key Frames"

    def execute(self, context):
#        print("Running Wave Import...")
        #context.scene.imp_sound_to_anim.Info_Import= "Working. See the terminal window." #this set the initial text
        wavimport(context)
      
    def invoke(self, context, event):
        self.execute(context)
        return {'RUNNING_MODAL'}

####################################################################################
#
#     bptao iniciar process som
#
####################################################################################
class OBJECT_OT_Botao_Go(bpy.types.Operator):
    ''''''
    bl_idname = "import.sound_animation_botao_go"
    # change in API
    bl_description = "Process a .wav file, take movement from the sound and import to the scene as Key"
    bl_label = "Process Wav"

# change sugested batFINGER
    filter_glob = StringProperty(default="*.wav", options={'HIDDEN'})

    path = StringProperty(name="File Path", description="Filepath used for importing the WAV file", maxlen= 1024, default= "")

    filename = StringProperty(name="File Name",
                              description="Name of the file")
                              
    directory = StringProperty(name="Directory",
                               description="Directory of the file")

    def execute(self, context):   
        # nao funciona self.properties.path
#        print("File Selected: ", self.properties.path)#display the file name and current path
#        print("Filename: ", self.properties.filename)#display the file name and current path
#        print("Directory Selected: ", self.properties.directory)#display the file name and current path        
        import os 
        f= os.path.join(self.properties.directory, self.properties.filename)
        f= os.path.normpath(f)
                            
        print (" ")
        print (" ")
        print ("Selected file = ",f)
        checktype = f.split('\\')[-1].split('.')[1]
        if checktype.upper() != 'WAV':
            print ("ERROR!! Selected file = ", f)
            print ("ERROR!! Its not a .wav file")
            return

        iAudioSensib= int(context.scene.imp_sound_to_anim.audio_sense)-1    #sensibilidade volume do audio 0 a 5. Quanto maior, mais sensibilidade
        if iAudioSensib <0: iAudioSensib=0
        elif iAudioSensib>5: iAudioSensib=5
    
        iFramesPorSeg= int(context.scene.imp_sound_to_anim.frames_per_second)  #Frames por segundo para key frame
        
        iMovPorSeg= int(context.scene.imp_sound_to_anim.action_per_second)      #Sensibilidade de movimento. 3= 3 movimentos por segundo
    
        #iDivMovPorSeg Padrao - taxa 4/s ou a cada 0,25s  => iFramesPorSeg/iDivMovPorSeg= ~0.25
        for i in range(iFramesPorSeg):
            iDivMovPorSeg=iFramesPorSeg/(i+1)
            if iFramesPorSeg/iDivMovPorSeg >=iMovPorSeg:
                break    

        # chama funcao de converter som, retorna preenchendo _Interna_Globals.array
        SoundConv(f, int(iDivMovPorSeg), iAudioSensib, iFramesPorSeg, context)
        return {'FINISHED'}

      
    def invoke(self, context, event):
        #need to set a path so so we can get the file name and path
        wm = context.window_manager
        wm.fileselect_add(self)

        return {'RUNNING_MODAL'}  

        
def register():
    bpy.utils.register_module(__name__)
    bpy.types.Scene.imp_sound_to_anim = PointerProperty(type=ImpSoundtoAnim, name="Import: Sound to Animation", description="Extract movement from sound file. See the Object Panel at the end.")
    bpy.types.INFO_MT_file_import.append(WavFileImport) 

#
def unregister():
    bpy.utils.unregister_module(__name__)
    bpy.types.INFO_MT_file_import.remove(WavFileImport) 

#
if __name__ == "__main__":
    register()