From b506fd83de147556943c4d005c58e716fa99d938 Mon Sep 17 00:00:00 2001 From: Jamie Hardt Date: Thu, 6 Nov 2025 16:09:03 -0800 Subject: [PATCH] Implementing for ptsl in progress --- __init__.py | 15 +++--- blender_manifest.toml | 13 ++++++ intern/pro_tools.py | 22 +++++++++ operator_add_speakers_to_objects.py | 72 ----------------------------- operator_protools_export.py | 64 +++++++++++-------------- 5 files changed, 69 insertions(+), 117 deletions(-) create mode 100644 intern/pro_tools.py delete mode 100644 operator_add_speakers_to_objects.py diff --git a/__init__.py b/__init__.py index b08938e..c2a8663 100644 --- a/__init__.py +++ b/__init__.py @@ -1,19 +1,20 @@ import bpy -from .operator_protools_export import ProToolsExport +from .operator_protools_export import SendToProTools def export_pt_menu_callback(self, _): - self.layout.operator(ProToolsExport.bl_idname, text="Send to Pro Tools") + layout = self.layout + layout.separator() + layout.operator(SendToProTools.bl_idname, text="Send to Pro Tools") def register(): - bpy.utils.register_class(ProToolsExport) - bpy.types.TOPBAR_MT_file_export.append(export_pt_menu_callback) + bpy.utils.register_class(SendToProTools) + bpy.types.VIEW3D_MT_object.append(export_pt_menu_callback) def unregister(): - bpy.utils.unregister_class(ProToolsExport) - - bpy.types.TOPBAR_MT_file_export.remove(export_pt_menu_callback) + bpy.utils.unregister_class(SendToProTools) + bpy.types.VIEW3D_MT_object.remove(export_pt_menu_callback) diff --git a/blender_manifest.toml b/blender_manifest.toml index 9b502fd..ae5fec8 100644 --- a/blender_manifest.toml +++ b/blender_manifest.toml @@ -14,10 +14,23 @@ license = [ ] website = "https://git.squad51.us/jamie/soundobjects_blender_addon" platforms = ["macos-arm64", "windows-x64"] +wheels = [ + './wheels/grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl', + './wheels/grpcio-1.74.0-cp311-cp311-win_amd64.whl', + './wheels/iniconfig-2.3.0-py3-none-any.whl', + './wheels/packaging-25.0-py3-none-any.whl', + './wheels/pluggy-1.6.0-py3-none-any.whl', + './wheels/protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl', + './wheels/protobuf-6.31.1-cp310-abi3-win_amd64.whl', + './wheels/py_ptsl-600.1.0-py3-none-any.whl', + './wheels/pygments-2.19.2-py3-none-any.whl', + './wheels/pytest-8.4.2-py3-none-any.whl' +] [permissions] network = "Communicates with Pro Tools via RPC, only localhost" + [build] paths_exclude_pattern = [ "__pycache__/", diff --git a/intern/pro_tools.py b/intern/pro_tools.py new file mode 100644 index 0000000..573a03d --- /dev/null +++ b/intern/pro_tools.py @@ -0,0 +1,22 @@ +import bpy +import ptsl +from ptsl import PTSL_pb2 as pt + +def send_to_pro_tools(speakers: list[bpy.types.Object], + room_size: float) -> bool: + try: + client = ptsl.Client(company_name="Squad 51", + application_name="Send to Pro Tools Blender Extension") + + for speaker in speakers: + assert speaker.data + result = client.run_command(pt.CId_CreateNewTracks, { + {'number_of_tracks': 1, + 'track_name': speaker.data.name} + }) + + client.close() + return True + + except: + return False diff --git a/operator_add_speakers_to_objects.py b/operator_add_speakers_to_objects.py deleted file mode 100644 index 65993cc..0000000 --- a/operator_add_speakers_to_objects.py +++ /dev/null @@ -1,72 +0,0 @@ -import bpy - -from bpy.types import Operator -from bpy.props import BoolProperty, StringProperty, EnumProperty, FloatProperty - -from .intern.add_sound_to_meshes import add_speakers_to_meshes, TriggerMode - -class AddSoundToMeshOperator(Operator): - """Add a speaker to each selected object""" - bl_idname = "object.add_speakers_to_obj" - bl_label = "Add Sounds to Meshes" - - TRIGGER_OPTIONS = ( - (TriggerMode.START_FRAME, - "Start Frame", - "Sound will play on the first frame of the animation"), - (TriggerMode.MIN_DISTANCE, - "Minimum Distance", - "Sound will play when the object is closest to the camera"), - (TriggerMode.RANDOM, - "Random", - "Sound will play exactly once, at a random time"), - (TriggerMode.RANDOM_GAUSSIAN, - "Random (Gaussian)", - "Sound will play exactly once, at a guassian random time with " + - "stdev of 1 and mean in the middle of the animation") - ) - - @classmethod - def poll(cls, context): - sounds_avail = bpy.data.sounds - return len(context.selected_objects) > 0 and len(sounds_avail) > 0 - - use_sounds: StringProperty( - name="Sound Prefix", - description="Sounds having names starting with thie field will be assigned randomly to each speaker" - ) - - sync_audio_peak: BoolProperty( - name="Sync Audio Peak", - default=True, - description="Synchronize speaker audio to loudest peak instead of beginning of file" - ) - - trigger_mode: EnumProperty( - items=TRIGGER_OPTIONS, - name="Trigger", - description="Select when each sound will play", - default=TriggerMode.MIN_DISTANCE, - - ) - - gaussian_stddev: FloatProperty( - name="Gaussian StDev", - description="Standard Deviation of Gaussian random time", - default=1., - min=0.001, - max=6., - ) - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self) - - def execute(self, context): - - add_speakers_to_meshes(bpy.context.selected_objects, bpy.context, - sound=None, - sound_name_prefix=self.use_sounds, - trigger_mode=self.trigger_mode, - sync_peak=self.sync_audio_peak, - gaussian_stddev=self.gaussian_stddev) - return {'FINISHED'} diff --git a/operator_protools_export.py b/operator_protools_export.py index 6d14add..c411dcf 100644 --- a/operator_protools_export.py +++ b/operator_protools_export.py @@ -1,52 +1,40 @@ -from bpy_extras.io_utils import ExportHelper -from bpy.props import StringProperty, BoolProperty, FloatProperty, IntProperty -from bpy.types import Operator +import bpy -from .intern.generate_adm import generate_adm +from .intern import pro_tools +def filter_speakers(objs: list[bpy.types.Object]) -> list[bpy.types.Object]: + return [s for s in objs if s.type == 'SPEAKER'] -class ProToolsExport(Operator, ExportHelper): - """ - Export audio objects to a Pro Tools session - """ - bl_idname = "export.pro_tools_live" # important since its how +class SendToProTools(bpy.types.Operator): + "Send Audio Objects to Pro Tools" + + bl_idname = "object.pro_tools_live" bl_label = "Send Audio Objects to Pro Tools" - filepath: str - filename_ext = ".wav" - filter_glob = StringProperty( - default="*.wav", - options={'HIDDEN'}, - maxlen=255, # Max internal buffer length, longer would be clamped. - ) - - room_size = FloatProperty( - default=1.0, + room_size: bpy.props.FloatProperty( name="Room Size", description="Distance from the lens to the front room boundary", + default=1.0, min=0.001, step=1, unit='LENGTH' - ) + ) - max_objects = IntProperty( - name="Max Objects", - description="Maximum number of object tracks to create", - default=24, - min=0, - max=118 - ) + @classmethod + def poll(cls, context): + return len(filter_speakers(context.selected_objects)) > 0 and \ + bpy.app.online_access - create_bed = BoolProperty( - name="Create 7.1 Bed", - description="Create a bed for all sounds not included on object " - "tracks", - default=False, - options={'HIDDEN'} - ) + def invoke(self, context, event): + wm = context.window_manager + return wm.invoke_props_dialog(self, confirm_text="Send to Pro Tools") def execute(self, context) -> set: - assert self.create_bed is False, "Create Bed is not supported" - - return generate_adm(context, self.filepath, self.room_size, - self.max_objects) + if pro_tools.send_to_pro_tools( + filter_speakers(context.selected_objects), + self.room_size): + self.report({'INFO'}, "Speaker objects sent to Pro Tools") + return {'FINISHED'} + else: + self.report({'ERROR'}, "Could not connect to Pro Tools") + return {'FINISHED'}