From e0e45e1d03c04ffb277dcd35e0e327d1172c081c Mon Sep 17 00:00:00 2001 From: RyanOnTheInside <7623207+ryanontheinside@users.noreply.github.com> Date: Sat, 3 May 2025 13:03:34 -0400 Subject: [PATCH] adds batch support for sequence controls --- .../controls/sequence_controls.py | 86 ++++++++++++++++--- 1 file changed, 75 insertions(+), 11 deletions(-) diff --git a/node_wrappers/realtimenodes/controls/sequence_controls.py b/node_wrappers/realtimenodes/controls/sequence_controls.py index 3fe22bf..f07da36 100644 --- a/node_wrappers/realtimenodes/controls/sequence_controls.py +++ b/node_wrappers/realtimenodes/controls/sequence_controls.py @@ -17,11 +17,21 @@ class SequenceControlBase(ControlNodeBase): ["forward", "reverse", "pingpong", "random"], {"default": "forward", "tooltip": "How to move through the sequence"}, ), + "batch_size": ( + "INT", + { + "default": 1, + "min": 1, + "max": 1000, + "step": 1, + "tooltip": "Number of values to generate in batch", + }, + ), } ) return inputs - def update_sequence_base(self, values: list, steps_per_item: int, sequence_mode: str, always_execute: bool = True): + def update_sequence_base(self, values: list, steps_per_item: int, sequence_mode: str, batch_size: int = 1, always_execute: bool = True): # Initialize or get state state = self.get_state( { @@ -32,8 +42,41 @@ class SequenceControlBase(ControlNodeBase): ) if not values: - return (values[0] if values else None,) + if batch_size > 1: + return ([None] * batch_size,) + return (None,) + # Fast path for batch_size=1 (real-time case) + if batch_size == 1: + # Update step counter + state["step_counter"] += 1 + + # Check if we should move to next item + if state["step_counter"] >= steps_per_item: + state["step_counter"] = 0 + + # Update index based on sequence mode + if sequence_mode == "forward": + state["current_index"] = (state["current_index"] + 1) % len(values) + elif sequence_mode == "reverse": + state["current_index"] = (state["current_index"] - 1) % len(values) + elif sequence_mode == "pingpong": + next_index = state["current_index"] + state["direction"] + if next_index >= len(values) or next_index < 0: + state["direction"] *= -1 # Reverse direction + next_index = state["current_index"] + state["direction"] + state["current_index"] = next_index + elif sequence_mode == "random": + import random + state["current_index"] = random.randint(0, len(values) - 1) + + # Save state + self.set_state(state) + + # Return current value + return (values[state["current_index"]],) + + # Batch processing path # Update step counter state["step_counter"] += 1 @@ -54,14 +97,35 @@ class SequenceControlBase(ControlNodeBase): state["current_index"] = next_index elif sequence_mode == "random": import random - state["current_index"] = random.randint(0, len(values) - 1) # Save state self.set_state(state) - # Return current value - return (values[state["current_index"]],) + # Generate batch of values from current position + batch_values = [] + for i in range(batch_size): + if sequence_mode == "forward": + idx = (state["current_index"] + i) % len(values) + elif sequence_mode == "reverse": + idx = (state["current_index"] - i) % len(values) + elif sequence_mode == "pingpong": + # For pingpong, we need to bounce when we hit the ends + direction = state["direction"] + idx = state["current_index"] + for j in range(i): + idx += direction + if idx >= len(values) or idx < 0: + direction *= -1 + idx += 2 * direction # Move two steps to correct direction + idx = max(0, min(idx, len(values) - 1)) # Safety bounds check + elif sequence_mode == "random": + import random + idx = random.randint(0, len(values) - 1) + + batch_values.append(values[idx]) + + return (batch_values,) class FloatSequence(SequenceControlBase): @@ -77,10 +141,10 @@ class FloatSequence(SequenceControlBase): inputs["required"].update({"values": ("STRING", {"default": "0.0, 0.5, 1.0", "tooltip": "List of float values (comma separated)"})}) return inputs - def update(self, values: str, steps_per_item: int, sequence_mode: str, always_execute: bool = True): + def update(self, values: str, steps_per_item: int, sequence_mode: str, batch_size: int = 1, always_execute: bool = True): # Parse comma-separated float values float_values = [float(v.strip()) for v in values.split(",") if v.strip()] - return self.update_sequence_base(float_values, steps_per_item, sequence_mode, always_execute) + return self.update_sequence_base(float_values, steps_per_item, sequence_mode, batch_size, always_execute) class IntSequence(SequenceControlBase): @@ -96,10 +160,10 @@ class IntSequence(SequenceControlBase): inputs["required"].update({"values": ("STRING", {"default": "0, 5, 10", "tooltip": "List of integer values (comma separated)"})}) return inputs - def update(self, values: str, steps_per_item: int, sequence_mode: str, always_execute: bool = True): + def update(self, values: str, steps_per_item: int, sequence_mode: str, batch_size: int = 1, always_execute: bool = True): # Parse comma-separated integer values int_values = [int(v.strip()) for v in values.split(",") if v.strip()] - return self.update_sequence_base(int_values, steps_per_item, sequence_mode, always_execute) + return self.update_sequence_base(int_values, steps_per_item, sequence_mode, batch_size, always_execute) class StringSequence(SequenceControlBase): @@ -122,7 +186,7 @@ class StringSequence(SequenceControlBase): ) return inputs - def update(self, values: str, steps_per_item: int, sequence_mode: str, always_execute: bool = True): + def update(self, values: str, steps_per_item: int, sequence_mode: str, batch_size: int = 1, always_execute: bool = True): # Split into string values string_values = [v.strip() for v in values.split("\n") if v.strip()] - return self.update_sequence_base(string_values, steps_per_item, sequence_mode, always_execute) + return self.update_sequence_base(string_values, steps_per_item, sequence_mode, batch_size, always_execute)