blender-addons/storypencil/sound.py
Campbell Barton e8da6131fd License headers: use SPDX-FileCopyrightText for all addons
Move copyright text to SPDX-FileCopyrightText or set to the
Blender Foundation so "make check_licenses" now runs without warnings.
2023-06-15 16:54:05 +10:00

138 lines
3.9 KiB
Python

# SPDX-FileCopyrightText: 2023 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from typing import List, Sequence, Tuple
from bpy.types import (
Operator,
)
def send_sound_strip(s, dest_scn):
'''recreate sound strip in another scene
:dest_scn: scene destination
:return: newly create sound strip
'''
if s.type != 'SOUND':
return
vse = dest_scn.sequence_editor
ns = vse.sequences.new_sound(name=s.name, filepath=s.sound.filepath, channel=s.channel, frame_start=int(s.frame_start))
ns.sound = s.sound # reget the same sound source
for attr in ('pitch',
'pan',
'show_waveform',
'speed_factor',
'volume',
'mute'):
if hasattr(s, attr):
setattr(ns, attr, getattr(s, attr))
if ns.volume == 0:
ns.volume = 1
return ns
def get_all_overlapping_sound_strip(scn_strip, skip_mute=True):
"""return array of all sound strips for this strip"""
if scn_strip.type != 'SCENE':
return
src_scn = scn_strip.id_data
vse = src_scn.sequence_editor
overlapping_sounds = []
for s in vse.sequences:
if s.type != 'SOUND':
continue
if skip_mute and s.mute:
continue
if (s.frame_final_end - 1 < scn_strip.frame_final_start)\
or (s.frame_final_start - 1 > scn_strip.frame_final_end):
continue
overlapping_sounds.append(s)
return overlapping_sounds
def delete_sounds(scene):
for st in reversed(scene.sequence_editor.sequences):
if st.type == 'SOUND':
scene.sequence_editor.sequences.remove(st)
def get_scene_frame_from_sequencer_frame(scn_strip, sound) -> float:
"""return frame in scene referential"""
return sound.frame_start - scn_strip.frame_start + scn_strip.scene.frame_start
def send_sound_to_strip_scene(scn_strip, clear_sequencer=True, skip_mute=True):
"""Add sounds to strip scene"""
if scn_strip.type != 'SCENE':
return
tgt_scene = scn_strip.scene
sounds = get_all_overlapping_sound_strip(scn_strip, skip_mute=skip_mute)
if not sounds:
return
# Clear sounds if exists in scene vse already
if clear_sequencer:
delete_sounds(tgt_scene)
print(f'Duplicating sounds in {tgt_scene.name}:')
for s in sounds:
new_start = get_scene_frame_from_sequencer_frame(scn_strip, s)
ns = send_sound_strip(s, tgt_scene)
if ns:
ns.frame_start = new_start
ns.frame_offset_start = s.frame_offset_start
ns.frame_final_duration = s.frame_final_duration
return sounds
def dispatch_sounds_in_scenes(selected_scn_only=True, skip_mute=True):
"""Main function to duplicate sounds in strip scenes"""
edit_scene = bpy.context.scene
edit = edit_scene.sequence_editor
ct = 0
for strip in edit.sequences:
if strip.type != 'SCENE':
continue
if selected_scn_only and not strip.select:
continue
sounds = send_sound_to_strip_scene(strip, skip_mute=skip_mute)
if sounds:
ct += 1
if ct:
print('INFO', f'Sound duplicated in {ct} scenes')
else:
print('ERROR', f'No duplication occured')
class STORYPENCIL_OT_duplicate_sound_in_edit_scene(Operator):
bl_idname = "storypencil.duplicate_sound_in_edit_scene"
bl_label = "Sounds To Scenes"
bl_description = "Copy sound strips from VSE to the source scenes"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
if context.space_data.type != 'SEQUENCE_EDITOR':
return False
return True
def execute(self, context):
scene = context.scene
dispatch_sounds_in_scenes(
selected_scn_only=scene.storypencil_selected_scn_only,
skip_mute=scene.storypencil_skip_sound_mute)
bpy.ops.sequencer.reload()
return {'FINISHED'}