Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'kinoraw_tools/audio_tools.py')
-rw-r--r--kinoraw_tools/audio_tools.py360
1 files changed, 360 insertions, 0 deletions
diff --git a/kinoraw_tools/audio_tools.py b/kinoraw_tools/audio_tools.py
new file mode 100644
index 00000000..d0278de0
--- /dev/null
+++ b/kinoraw_tools/audio_tools.py
@@ -0,0 +1,360 @@
+import bpy, os
+from bpy.props import IntProperty, StringProperty, BoolProperty
+import subprocess
+
+from . import functions
+
+
+proxy_qualities = [ ( "1", "25%", "" ), ( "2", "50%", "" ),
+ ( "3", "75%", "" ), ( "4", "100%", "" )]
+
+#
+# ls *.sh | parallel -j 8 sh {}
+#
+
+# functions
+
+
+
+
+def createsyncfile(filename):
+ if not os.path.isfile(bpy.path.abspath(filename)):
+ f = open(bpy.path.abspath(filename), "w")
+ data = []
+
+ try:
+ f.writelines(data) # Write a sequence of strings to a file
+ finally:
+ f.close()
+
+
+def readsyncfile(filename):
+ try:
+ file = open(bpy.path.abspath(filename))
+ data = file.readlines()
+ file.close()
+
+ return data
+
+ except IOError:
+ pass
+
+
+def writesyncfile(filename, data):
+ try:
+ f = open(bpy.path.abspath(filename), "w")
+ try:
+ for line in data:
+ f.writelines(line) # Write a sequence of strings to a file
+ finally:
+ f.close()
+
+ except IOError:
+ pass
+
+
+# classes
+
+
+class ExtractWavOperator(bpy.types.Operator):
+ """ Use ffmpeg to extract audio from video and import it synced"""
+ bl_idname = "sequencer.extract_wav_operator"
+ bl_label = "Extract Wav from movie strip Operator"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(self, context):
+ strip = functions.act_strip(context)
+ scn = context.scene
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ return strip.type in ('MOVIE')
+ else:
+ return False
+
+
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ audio_dir = preferences.addons[__package__].preferences.audio_dir
+
+ functions.create_folder(bpy.path.abspath(audio_dir))
+
+ for strip in context.selected_editable_sequences:
+
+ # get filename
+ if strip.type == "MOVIE":
+ filename = bpy.path.abspath(strip.filepath)
+ newfilename = bpy.path.abspath(strip.filepath).rpartition(
+ "/")[2]
+ fileoutput = os.path.join(bpy.path.abspath(audio_dir),
+ newfilename)+".wav"
+
+ # check for wav existing file
+ if not os.path.isfile(fileoutput):
+ #if not, extract the file
+ extract_audio = "ffmpeg -i '{}' -acodec pcm_s16le -ac 2 {}".\
+ format(filename, fileoutput)
+ print(extract_audio)
+ os.system(extract_audio)
+ else:
+ print("ya existe")
+
+ if strip.type == "MOVIE":
+ # import the file and trim in the same way the original
+ bpy.ops.sequencer.sound_strip_add(filepath=fileoutput,\
+ frame_start=strip.frame_start, channel=strip.channel+1,\
+ replace_sel=True, overlap=False, cache=False)
+
+ # Update scene
+ context.scene.update()
+
+ newstrip = context.scene.sequence_editor.active_strip
+
+ # deselect all other strips
+ for i in context.selected_editable_sequences:
+ if i.name != newstrip.name:
+ i.select=False
+
+ # Update scene
+ context.scene.update()
+
+ #Match the original clip's length
+
+ newstrip.frame_start = strip.frame_start - strip.animation_offset_start
+
+ #print(newstrip.name)
+ functions.triminout(newstrip,
+ strip.frame_start + strip.frame_offset_start,
+ strip.frame_start + strip.frame_offset_start + \
+ strip.frame_final_duration)
+
+ return {'FINISHED'}
+
+
+
+class ExternalAudioSetSyncOperator(bpy.types.Operator):
+ """get sync info from selected audio and video strip and store it into a text file """
+ bl_idname = "sequencer.external_audio_set_sync"
+ bl_label = "set sync info"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(cls, context):
+ if cls.has_sequencer(context):
+ if len(context.selected_editable_sequences) == 2:
+ types = []
+ for i in context.selected_editable_sequences:
+ types.append(i.type)
+ if 'MOVIE' and 'SOUND' in types:
+ return True
+ else:
+ return False
+
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ filename = preferences.addons[__package__].preferences.audio_external_filename
+
+ for strip in context.selected_editable_sequences:
+ if strip.type == "MOVIE":
+ moviestrip = strip
+ elif strip.type == "SOUND":
+ soundstrip = strip
+
+ offset = str(moviestrip.frame_start - soundstrip.frame_start)
+
+ data1 = readsyncfile(filename)
+ data2 = []
+ newline = moviestrip.filepath + " " + soundstrip.filepath + " " + offset + "\n"
+
+ if data1 != None:
+ repeated = False
+ for line in data1:
+ if line.split()[0] == moviestrip.filepath and line.split()[1] == soundstrip.filepath:
+ data2.append(newline)
+ repeated = True
+ else:
+ data2.append(line)
+ if not repeated:
+ data2.append(newline)
+ else:
+ data2.append(newline)
+
+ createsyncfile(filename)
+ writesyncfile(filename, data2)
+
+ return {'FINISHED'}
+
+
+class ExternalAudioReloadOperator(bpy.types.Operator):
+ """ reload external audio synced to selected movie strip acording to info from a text file """
+ bl_idname = "sequencer.external_audio_reload"
+ bl_label = "reload external audio"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type\
+ in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(cls, context):
+ if cls.has_sequencer(context):
+ if len(context.selected_editable_sequences) == 1:
+ if context.selected_editable_sequences[0].type == 'MOVIE':
+ return True
+ else:
+ return False
+
+
+ def execute(self, context):
+
+ preferences = context.user_preferences
+ filename = preferences.addons[__package__].preferences.audio_external_filename
+
+ data = readsyncfile(filename)
+
+ for strip in context.selected_editable_sequences:
+
+ sounds = []
+ for line in data:
+ if line.split()[0] == strip.filepath:
+ moviefile = bpy.path.abspath(line.split()[0])
+ soundfile = bpy.path.abspath(line.split()[1])
+ offset = int(line.split()[2])
+ sounds.append((soundfile, offset))
+
+
+ for soundfile, offset in sounds:
+ print(soundfile,offset)
+ print(strip.filepath)
+ # find start frame for sound strip (using offset from file)
+ sound_frame_start = strip.frame_start - strip.animation_offset_start - offset
+
+ # import the file and trim in the same way the original
+ bpy.ops.sequencer.sound_strip_add(filepath=soundfile,\
+ frame_start=sound_frame_start, channel=strip.channel+1,\
+ replace_sel=True, overlap=False, cache=False)
+
+ # Update scene
+ context.scene.update()
+
+ newstrip = context.scene.sequence_editor.active_strip
+
+ # deselect all other strips
+ for i in context.selected_editable_sequences:
+ if i.name != newstrip.name:
+ i.select=False
+
+ # Update scene
+ context.scene.update()
+
+ # trim sound strip like original one
+ functions.triminout(newstrip,
+ strip.frame_start + strip.frame_offset_start,
+ strip.frame_start + strip.frame_offset_start + \
+ strip.frame_final_duration)
+
+ return {'FINISHED'}
+
+
+class AudioToolPanel(bpy.types.Panel):
+ """ """
+ bl_label = "Audio Tools"
+ bl_idname = "OBJECT_PT_AudioTool"
+ bl_space_type = 'SEQUENCE_EDITOR'
+ bl_region_type = 'UI'
+
+ @classmethod
+ def poll(self, context):
+ if context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
+ strip = functions.act_strip(context)
+ scn = context.scene
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+ if scn and scn.sequence_editor and scn.sequence_editor.active_strip:
+ if prefs.use_audio_tools:
+ return True
+ else:
+ return False
+
+ def draw_header(self, context):
+ layout = self.layout
+ layout.label(text="", icon="PLAY_AUDIO")
+
+ def draw(self, context):
+
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ strip = functions.act_strip(context)
+
+ if strip.type == "MOVIE":
+ layout = self.layout
+ layout.prop(prefs, "audio_dir", text="path for audio files")
+
+
+ layout.operator("sequencer.extract_wav_operator", text="Extract Wav")
+
+
+ layout = self.layout
+ layout.prop(prefs, "audio_scripts")
+
+ if prefs.audio_scripts:
+ layout = self.layout
+ layout.prop(prefs, "audio_scripts_path", text="path for scripts")
+
+ layout = self.layout
+ layout.prop(prefs, "audio_use_external_links", text="external audio sync")
+
+
+ if prefs.audio_use_external_links:
+ layout = self.layout
+ layout.prop(prefs, "audio_external_filename", text="sync data")
+
+ row = layout.row(align=True)
+ row.operator("sequencer.external_audio_set_sync", text="set sync")
+ row.operator("sequencer.external_audio_reload", text="reload audio")
+
+ layout = self.layout
+
+ row = layout.row()
+ row.prop(prefs, "metertype", text="")
+ row.operator("sequencer.openmeterbridge", text="Launch Audio Meter", icon = "SOUND")
+
+
+class OpenMeterbridgeOperator(bpy.types.Operator):
+ """ """
+ bl_idname = "sequencer.openmeterbridge"
+ bl_label = "open external vu meter to work with jack"
+
+ @staticmethod
+ def has_sequencer(context):
+ return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'})
+
+ @classmethod
+ def poll(cls, context):
+ if cls.has_sequencer(context):
+ if len(context.selected_editable_sequences) == 1:
+ return True
+
+
+ def execute(self, context):
+ preferences = context.user_preferences
+ prefs = preferences.addons[__package__].preferences
+
+ command = "meterbridge -t {} 'PulseAudio JACK Sink:front-left' 'PulseAudio JACK Sink:front-right' &".format(prefs.metertype.lower())
+ p = subprocess.Popen(command,stdout=subprocess.PIPE, shell = True)
+
+ return {'FINISHED'}
+
+