1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
#
# Copyright (C) 2016-2019 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
#
# This file is part of Power Sequencer.
#
# Power Sequencer 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.
#
# Power Sequencer 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 Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
import bpy
from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
class POWER_SEQUENCER_OT_align_audios(bpy.types.Operator):
"""*brief* Align two audio strips
Tries to synchronize the selected audio strip to the active audio strip by comparing the sound.
Useful to synchronize audio of the same event recorded with different microphones.
To use this feature, you must have [ffmpeg](https://www.ffmpeg.org/download.html) and
[scipy](https://www.scipy.org/install.html) installed on your computer and available on the PATH (command line) to work.
The longer the audio files, the longer the tool can take to run, as it has to convert, analyze,
and compare the audio sources to work.
"""
doc = {
"name": doc_name(__qualname__),
"demo": "https://i.imgur.com/xkBUzDj.gif",
"description": doc_description(__doc__),
"shortcuts": [],
"keymap": "Sequencer",
}
bl_idname = doc_idname(__qualname__)
bl_label = doc["name"]
bl_description = doc_brief(doc["description"])
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
if not context.scene:
return False
active = context.scene.sequence_editor.active_strip
selected = context.selected_sequences
ok = (
len(selected) == 2
and active in selected
and all(map(lambda s: s.type == "SOUND", selected))
)
return ok
def execute(self, context):
try:
import scipy
except ImportError:
self.report({"ERROR"}, "Scipy must be installed to align audios")
return {"FINISHED"}
if not is_ffmpeg_available():
self.report({"ERROR"}, "ffmpeg must be installed to align audios")
return {"FINISHED"}
# This import is here because otherwise, it slows down blender startup
from .audiosync import find_offset
scene = context.scene
active = scene.sequence_editor.active_strip
active_filepath = bpy.path.abspath(active.sound.filepath)
selected = context.selected_sequences
selected.pop(selected.index(active))
align_strip = selected[0]
align_strip_filepath = bpy.path.abspath(align_strip.sound.filepath)
offset, score = find_offset(align_strip_filepath, active_filepath)
initial_offset = active.frame_start - align_strip.frame_start
fps = scene.render.fps / scene.render.fps_base
frames = int(offset * fps)
align_strip.frame_start -= frames - initial_offset
self.report({"INFO"}, "Alignment score: " + str(round(score, 1)))
return {"FINISHED"}
def is_ffmpeg_available():
"""
Returns true if ffmpeg is installed and available from the PATH
"""
try:
subprocess.call(["ffmpeg", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return True
except OSError:
return False
|