Re-implement grouping
Speakers are now grouped closest-first so lower tracks are always more distant, needs testin
This commit is contained in:
@@ -14,7 +14,6 @@ from ear.fileio.adm.elements.geom import ObjectCartesianPosition
|
|||||||
from ear.fileio.adm.builder import (ADMBuilder, TypeDefinition)
|
from ear.fileio.adm.builder import (ADMBuilder, TypeDefinition)
|
||||||
from ear.fileio.adm.generate_ids import generate_ids
|
from ear.fileio.adm.generate_ids import generate_ids
|
||||||
|
|
||||||
|
|
||||||
import lxml
|
import lxml
|
||||||
import uuid
|
import uuid
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
@@ -27,6 +26,9 @@ from mathutils import Quaternion, Vector
|
|||||||
from time import strftime
|
from time import strftime
|
||||||
from math import sqrt
|
from math import sqrt
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
bl_info = {
|
bl_info = {
|
||||||
"name": "Export ADM Broadcast-WAV File",
|
"name": "Export ADM Broadcast-WAV File",
|
||||||
"description": "Export a Broadcast-WAV with each speaker as an ADM object",
|
"description": "Export a Broadcast-WAV with each speaker as an ADM object",
|
||||||
@@ -37,6 +39,16 @@ bl_info = {
|
|||||||
"category": "Import-Export",
|
"category": "Import-Export",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FrameInterval:
|
||||||
|
def __init__(self, start_frame, end_frame):
|
||||||
|
self.start_frame = int(start_frame)
|
||||||
|
self.end_frame = int(end_frame)
|
||||||
|
|
||||||
|
def overlaps(self, other : 'FrameInterval') -> bool:
|
||||||
|
return self.start_frame <= other.start_frame <= self.end_frame or \
|
||||||
|
other.start_frame <= self.start_frame <= other.end_frame
|
||||||
|
|
||||||
|
|
||||||
def compute_relative_vector(camera: bpy.types.Camera, target: bpy.types.Object):
|
def compute_relative_vector(camera: bpy.types.Camera, target: bpy.types.Object):
|
||||||
"""
|
"""
|
||||||
Return a vector from `camera` to `target` in the camera's coordinate space.
|
Return a vector from `camera` to `target` in the camera's coordinate space.
|
||||||
@@ -58,7 +70,7 @@ def compute_relative_vector(camera: bpy.types.Camera, target: bpy.types.Object):
|
|||||||
return relative_vector
|
return relative_vector
|
||||||
|
|
||||||
|
|
||||||
def room_norm_vector(vec, room_size=1.):
|
def room_norm_vector(vec, room_size=1.) -> Vector:
|
||||||
"""
|
"""
|
||||||
The Room is tearing me apart, Lisa.
|
The Room is tearing me apart, Lisa.
|
||||||
|
|
||||||
@@ -77,7 +89,7 @@ def room_norm_vector(vec, room_size=1.):
|
|||||||
return vec / chebyshev
|
return vec / chebyshev
|
||||||
|
|
||||||
|
|
||||||
def closest_approach_to_camera(scene, speaker_object):
|
def closest_approach_to_camera(scene, speaker_object) -> (float, int):
|
||||||
max_dist = sys.float_info.max
|
max_dist = sys.float_info.max
|
||||||
at_time = scene.frame_start
|
at_time = scene.frame_start
|
||||||
for frame in range(scene.frame_start, scene.frame_end + 1):
|
for frame in range(scene.frame_start, scene.frame_end + 1):
|
||||||
@@ -92,7 +104,7 @@ def closest_approach_to_camera(scene, speaker_object):
|
|||||||
return (max_dist, at_time)
|
return (max_dist, at_time)
|
||||||
|
|
||||||
|
|
||||||
def speaker_active_time_range(speaker):
|
def speaker_active_time_range(speaker) -> FrameInterval:
|
||||||
"""
|
"""
|
||||||
The time range this speaker must control in order to sound right.
|
The time range this speaker must control in order to sound right.
|
||||||
|
|
||||||
@@ -107,7 +119,7 @@ def speaker_active_time_range(speaker):
|
|||||||
if strip.frame_end > end:
|
if strip.frame_end > end:
|
||||||
end = strip.frame_end
|
end = strip.frame_end
|
||||||
|
|
||||||
return int(start), int(end)
|
return FrameInterval(start_frame=start, end_frame=end)
|
||||||
|
|
||||||
|
|
||||||
def speakers_by_min_distance(scene, speakers):
|
def speakers_by_min_distance(scene, speakers):
|
||||||
@@ -118,34 +130,37 @@ def speakers_by_min_distance(scene, speakers):
|
|||||||
|
|
||||||
|
|
||||||
def speakers_by_start_time(speaker_objs):
|
def speakers_by_start_time(speaker_objs):
|
||||||
return sorted(speaker_objs, key=(lambda spk: speaker_active_time_range(spk)[0]))
|
return sorted(speaker_objs, key=(lambda spk: speaker_active_time_range(spk).start_frame))
|
||||||
|
|
||||||
|
|
||||||
def group_speakers(speaker_objs):
|
def group_speakers(speakers, scene) -> List[List[bpy.types.Object]]:
|
||||||
def group_speakers_impl1(bag):
|
def list_can_accept_speaker(speaker_list, speaker_to_test):
|
||||||
"Returns a useable group and the remainder"
|
"""
|
||||||
leftover = []
|
returns True if speaker_list contains no speakers active in
|
||||||
this_group = []
|
the range speaker_to_test is active in
|
||||||
boundary = -0xffffffff
|
"""
|
||||||
for speaker in bag:
|
test_range = speaker_active_time_range(speaker_to_test)
|
||||||
start, end = speaker_active_time_range(speaker)
|
for spk in speaker_list:
|
||||||
if start > boundary:
|
spk_range = speaker_active_time_range(spk)
|
||||||
this_group.append(speaker)
|
if spk_range.overlaps(test_range):
|
||||||
boundary = end
|
return False
|
||||||
else:
|
|
||||||
leftover.append(speaker)
|
|
||||||
|
|
||||||
return (this_group, leftover)
|
return True
|
||||||
|
|
||||||
groups = []
|
by_priority = speakers_by_min_distance(scene, speakers)
|
||||||
remaining = speaker_objs
|
|
||||||
while len(remaining) > 0:
|
|
||||||
results = group_speakers_impl1(remaining)
|
|
||||||
groups.append(results[0])
|
|
||||||
remaining = results[1]
|
|
||||||
|
|
||||||
print("Will group {} sources into {} objects".format(len(speaker_objs), len(groups)))
|
ret_val = [[]]
|
||||||
return groups
|
for spk in by_priority:
|
||||||
|
for elem in ret_val:
|
||||||
|
if list_can_accept_speaker(elem, spk):
|
||||||
|
elem.append(spk)
|
||||||
|
break
|
||||||
|
ret_val.append([spk])
|
||||||
|
|
||||||
|
for i in range(len(ret_val)):
|
||||||
|
ret_val[i] = speakers_by_start_time(ret_val[i])
|
||||||
|
|
||||||
|
return ret_val
|
||||||
|
|
||||||
|
|
||||||
def adm_block_formats_for_speakers(scene, speaker_objs, room_size=1.):
|
def adm_block_formats_for_speakers(scene, speaker_objs, room_size=1.):
|
||||||
@@ -154,8 +169,8 @@ def adm_block_formats_for_speakers(scene, speaker_objs, room_size=1.):
|
|||||||
block_formats = []
|
block_formats = []
|
||||||
|
|
||||||
for speaker_obj in speakers_by_start_time(speaker_objs):
|
for speaker_obj in speakers_by_start_time(speaker_objs):
|
||||||
speaker_start, speaker_end = speaker_active_time_range(speaker_obj)
|
speaker_interval = speaker_active_time_range(speaker_obj)
|
||||||
for frame in range(speaker_start, speaker_end + 1):
|
for frame in range(speaker_interval.start_frame, speaker_interval.end_frame + 1):
|
||||||
scene.frame_set(frame)
|
scene.frame_set(frame)
|
||||||
relative_vector = compute_relative_vector(camera=scene.camera, target=speaker_obj)
|
relative_vector = compute_relative_vector(camera=scene.camera, target=speaker_obj)
|
||||||
|
|
||||||
@@ -251,14 +266,15 @@ def load_infiles_for_muxing(mixdowns):
|
|||||||
|
|
||||||
|
|
||||||
def rm_object_mixes(mixdowns):
|
def rm_object_mixes(mixdowns):
|
||||||
|
print("rm_object_mixes entered")
|
||||||
for elem in mixdowns:
|
for elem in mixdowns:
|
||||||
os.unlink(elem[0])
|
os.unlink(elem[0])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def write_muxed_wav(mixdowns, scene, out_format, room_size, outfile, shortest_file, object_count, infiles):
|
def write_muxed_wav(mixdowns, scene, out_format, room_size, outfile, shortest_file, object_count, infiles):
|
||||||
|
print("write_muxed_wav entered")
|
||||||
READ_BLOCK=1024
|
READ_BLOC=1024
|
||||||
speaker_groups = list(map(lambda x: x[1], mixdowns))
|
speaker_groups = list(map(lambda x: x[1], mixdowns))
|
||||||
|
|
||||||
adm, chna = adm_for_scene(scene, speaker_groups, out_format, room_size=room_size)
|
adm, chna = adm_for_scene(scene, speaker_groups, out_format, room_size=room_size)
|
||||||
@@ -373,22 +389,14 @@ def write_some_data(context, filepath, room_size, max_objects):
|
|||||||
|
|
||||||
sound_sources = all_speakers(scene)
|
sound_sources = all_speakers(scene)
|
||||||
|
|
||||||
|
object_groups = group_speakers(sound_sources, scene)
|
||||||
sorted_speakers = speakers_by_start_time(sound_sources)
|
|
||||||
|
|
||||||
object_groups = group_speakers(sorted_speakers)
|
|
||||||
|
|
||||||
closest_speakers = speakers_by_min_distance(scene, sound_sources)
|
|
||||||
too_far_speakers = []
|
too_far_speakers = []
|
||||||
n = len(closest_speakers) - 1
|
if len(object_groups) > max_objects:
|
||||||
while len(object_groups) > max_objects:
|
too_far_speakers = object_groups[max_objects:]
|
||||||
sorted_speakers = speakers_by_start_time(closest_speakers[0:n])
|
object_groups =object_groups[0:max_objects]
|
||||||
too_far_speakers = closest_speakers[n:]
|
|
||||||
object_groups = group_speakers(sorted_speakers)
|
|
||||||
n = n - 1
|
|
||||||
|
|
||||||
print("Will create {} objects for {} sources, ignoring {} sources".format(
|
print("Will create {} objects for {} sources, ignoring {} sources".format(
|
||||||
len(object_groups), len(sorted_speakers), len(too_far_speakers)))
|
len(object_groups), len(sound_sources), len(too_far_speakers)))
|
||||||
|
|
||||||
mixdowns_spk_list_tuple = list(generate_speaker_mixdowns(scene, object_groups, filepath))
|
mixdowns_spk_list_tuple = list(generate_speaker_mixdowns(scene, object_groups, filepath))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user