mixing_tools.py•11.5 kB
import reapy
from reapy import reascript_api as RPR
class MixingTools:
    """Tools for mixing and automation in REAPER."""
    
    def __init__(self, config):
        """
        Initialize MixingTools with configuration.
        
        Args:
            config (dict): Configuration dictionary
        """
        self.config = config
    
    def add_automation_point(self, track_id, parameter, position, value):
        """
        Add an automation point for the specified parameter.
        
        Args:
            track_id (int): Track ID
            parameter (str): Parameter name (volume, pan, mute, etc.)
            position (float): Position in seconds
            value (float): Parameter value (normalized 0.0-1.0)
            
        Returns:
            dict: Result of the operation
        """
        try:
            # Get track by ID
            track = reapy.Track.from_id(track_id)
            
            # Map parameter name to envelope
            param_map = {
                "volume": 0,
                "pan": 1,
                "mute": 2,
                "solo": 3
            }
            
            if parameter.lower() not in param_map:
                return {
                    "success": False,
                    "error": f"Unsupported parameter: {parameter}"
                }
            
            # Get envelope
            env_idx = param_map[parameter.lower()]
            envelope = track.get_envelope(env_idx)
            
            # Add point
            point_idx = envelope.add_point(position, value)
            
            return {
                "success": True,
                "track_id": track_id,
                "parameter": parameter,
                "position": position,
                "value": value,
                "point_index": point_idx
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def add_fx_parameter_automation(self, track_id, fx_id, param_index, position, value):
        """
        Add an automation point for an FX parameter.
        
        Args:
            track_id (int): Track ID
            fx_id (int): FX ID
            param_index (int): Parameter index
            position (float): Position in seconds
            value (float): Parameter value (normalized 0.0-1.0)
            
        Returns:
            dict: Result of the operation
        """
        try:
            # Get track by ID
            track = reapy.Track.from_id(track_id)
            
            # Find FX by ID
            fx = None
            fx_index = -1
            for i in range(track.n_fxs):
                current_fx = track.fxs[i]
                if current_fx.id == fx_id:
                    fx = current_fx
                    fx_index = i
                    break
            
            if fx is None:
                return {
                    "success": False,
                    "error": f"FX with ID {fx_id} not found on track {track_id}"
                }
            
            # Get parameter envelope
            param = fx.params[param_index]
            envelope = param.envelope
            
            # Enable automation for this parameter if not already enabled
            if not envelope:
                # This is a simplification - in practice, you'd need to use ReaScript API
                # to show the parameter envelope
                return {
                    "success": False,
                    "error": "Parameter automation not enabled. Enable it in REAPER first."
                }
            
            # Add point
            point_idx = envelope.add_point(position, value)
            
            return {
                "success": True,
                "track_id": track_id,
                "fx_id": fx_id,
                "param_index": param_index,
                "param_name": param.name,
                "position": position,
                "value": value,
                "point_index": point_idx
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def create_send(self, source_track_id, destination_track_id, volume=0.0):
        """
        Create a send between the specified tracks.
        
        Args:
            source_track_id (int): Source track ID
            destination_track_id (int): Destination track ID
            volume (float): Send volume in dB
            
        Returns:
            dict: Result of the operation
        """
        try:
            # Get tracks by ID
            source_track = reapy.Track.from_id(source_track_id)
            destination_track = reapy.Track.from_id(destination_track_id)
            
            # Get destination track index
            dest_idx = destination_track.index
            
            # Create send
            send_idx = source_track.add_send(dest_idx)
            
            # Set send volume
            send = source_track.sends[send_idx]
            send.volume = volume
            
            return {
                "success": True,
                "source_track_id": source_track_id,
                "destination_track_id": destination_track_id,
                "send_index": send_idx,
                "volume": send.volume
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def set_send_parameters(self, source_track_id, send_idx, volume=None, pan=None, mute=None):
        """
        Set parameters for the specified send.
        
        Args:
            source_track_id (int): Source track ID
            send_idx (int): Send index
            volume (float, optional): Send volume in dB
            pan (float, optional): Send pan position (-1.0 to 1.0)
            mute (bool, optional): Send mute state
            
        Returns:
            dict: Result of the operation
        """
        try:
            # Get track by ID
            source_track = reapy.Track.from_id(source_track_id)
            
            # Get send
            send = source_track.sends[send_idx]
            
            # Set parameters if provided
            if volume is not None:
                send.volume = volume
            
            if pan is not None:
                send.pan = pan
            
            if mute is not None:
                send.mute = mute
            
            return {
                "success": True,
                "source_track_id": source_track_id,
                "send_index": send_idx,
                "volume": send.volume,
                "pan": send.pan,
                "mute": send.mute
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def create_bus(self, name, tracks):
        """
        Create a bus track and route the specified tracks to it.
        
        Args:
            name (str): Bus track name
            tracks (list): List of track IDs to route to the bus
            
        Returns:
            dict: Result of the operation
        """
        try:
            project = reapy.Project()
            
            # Create bus track
            track_index = project.n_tracks
            project.add_track(track_index, name)
            bus_track = project.tracks[track_index]
            
            # Create sends from each track to the bus
            sends = []
            for track_id in tracks:
                try:
                    source_track = reapy.Track.from_id(track_id)
                    send_idx = source_track.add_send(track_index)
                    sends.append({
                        "source_track_id": track_id,
                        "send_index": send_idx
                    })
                except Exception as e:
                    print(f"Error creating send from track {track_id}: {e}")
            
            return {
                "success": True,
                "bus_track_id": bus_track.id,
                "bus_track_index": track_index,
                "name": name,
                "sends": sends
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }
    
    def apply_eq_preset(self, track_id, preset_name):
        """
        Apply an EQ preset to the specified track.
        
        Args:
            track_id (int): Track ID
            preset_name (str): Preset name
            
        Returns:
            dict: Result of the operation
        """
        try:
            # Get track by ID
            track = reapy.Track.from_id(track_id)
            
            # Define EQ presets
            presets = {
                "vocal": [
                    {"freq": 100, "gain": -3, "q": 0.7, "type": "highpass"},
                    {"freq": 250, "gain": -2, "q": 1.0, "type": "peak"},
                    {"freq": 3000, "gain": 2, "q": 1.0, "type": "peak"},
                    {"freq": 10000, "gain": 1, "q": 0.7, "type": "highshelf"}
                ],
                "kick": [
                    {"freq": 30, "gain": -3, "q": 0.7, "type": "highpass"},
                    {"freq": 60, "gain": 3, "q": 1.0, "type": "peak"},
                    {"freq": 400, "gain": -4, "q": 1.0, "type": "peak"}
                ],
                "snare": [
                    {"freq": 100, "gain": -3, "q": 0.7, "type": "highpass"},
                    {"freq": 200, "gain": -2, "q": 1.0, "type": "peak"},
                    {"freq": 3500, "gain": 3, "q": 1.0, "type": "peak"}
                ],
                "guitar": [
                    {"freq": 80, "gain": -3, "q": 0.7, "type": "highpass"},
                    {"freq": 800, "gain": -2, "q": 1.0, "type": "peak"},
                    {"freq": 3000, "gain": 2, "q": 1.0, "type": "peak"}
                ],
                "bass": [
                    {"freq": 40, "gain": -3, "q": 0.7, "type": "highpass"},
                    {"freq": 80, "gain": 3, "q": 1.0, "type": "peak"},
                    {"freq": 800, "gain": -2, "q": 1.0, "type": "peak"}
                ]
            }
            
            if preset_name not in presets:
                return {
                    "success": False,
                    "error": f"Preset not found: {preset_name}"
                }
            
            # Add ReaEQ
            fx_index = track.add_fx("ReaEQ")
            
            if fx_index < 0:
                return {
                    "success": False,
                    "error": "Failed to add ReaEQ"
                }
            
            # Get the FX
            fx = track.fxs[fx_index]
            
            # Apply preset
            # Note: This is a simplification - in practice, you'd need to map
            # the preset parameters to ReaEQ's specific parameter indices
            
            return {
                "success": True,
                "track_id": track_id,
                "fx_id": fx.id,
                "preset": preset_name,
                "message": f"EQ preset '{preset_name}' applied. Note: Parameter mapping is simplified."
            }
        except Exception as e:
            return {
                "success": False,
                "error": str(e)
            }