@@ -2157,6 +2157,166 @@ def randomize_device(self, track_index, device_index):
21572157 except Exception as e :
21582158 return {"ok" : False , "error" : str (e )}
21592159
2160+ # ========================================================================
2161+ # MAX FOR LIVE (M4L) DEVICE OPERATIONS
2162+ # ========================================================================
2163+
2164+ def is_max_device (self , track_index , device_index ):
2165+ """Check if device is a Max for Live device"""
2166+ try :
2167+ if track_index < 0 or track_index >= len (self .song .tracks ):
2168+ return {"ok" : False , "error" : "Invalid track index" }
2169+
2170+ track = self .song .tracks [track_index ]
2171+ if device_index < 0 or device_index >= len (track .devices ):
2172+ return {"ok" : False , "error" : "Invalid device index" }
2173+
2174+ device = track .devices [device_index ]
2175+
2176+ # M4L devices have specific class names
2177+ m4l_classes = ['MxDeviceAudioEffect' , 'MxDeviceMidiEffect' , 'MxDeviceInstrument' ]
2178+ is_m4l = device .class_name in m4l_classes
2179+
2180+ return {
2181+ "ok" : True ,
2182+ "is_m4l" : is_m4l ,
2183+ "class_name" : str (device .class_name ),
2184+ "class_display_name" : str (device .class_display_name ) if hasattr (device , 'class_display_name' ) else str (device .class_name ),
2185+ "device_name" : str (device .name )
2186+ }
2187+ except Exception as e :
2188+ return {"ok" : False , "error" : str (e )}
2189+
2190+ def get_m4l_devices (self , track_index ):
2191+ """Get all Max for Live devices on track"""
2192+ try :
2193+ if track_index < 0 or track_index >= len (self .song .tracks ):
2194+ return {"ok" : False , "error" : "Invalid track index" }
2195+
2196+ track = self .song .tracks [track_index ]
2197+ m4l_devices = []
2198+ m4l_classes = ['MxDeviceAudioEffect' , 'MxDeviceMidiEffect' , 'MxDeviceInstrument' ]
2199+
2200+ for i , device in enumerate (track .devices ):
2201+ if device .class_name in m4l_classes :
2202+ device_type = self ._get_m4l_type (device .class_name )
2203+ m4l_devices .append ({
2204+ "index" : i ,
2205+ "name" : str (device .name ),
2206+ "class_name" : str (device .class_name ),
2207+ "type" : device_type ,
2208+ "is_active" : device .is_active ,
2209+ "num_parameters" : len (device .parameters )
2210+ })
2211+
2212+ return {
2213+ "ok" : True ,
2214+ "track_index" : track_index ,
2215+ "track_name" : str (track .name ),
2216+ "devices" : m4l_devices ,
2217+ "count" : len (m4l_devices )
2218+ }
2219+ except Exception as e :
2220+ return {"ok" : False , "error" : str (e )}
2221+
2222+ def _get_m4l_type (self , class_name ):
2223+ """Get M4L device type from class name"""
2224+ type_map = {
2225+ 'MxDeviceAudioEffect' : 'audio_effect' ,
2226+ 'MxDeviceMidiEffect' : 'midi_effect' ,
2227+ 'MxDeviceInstrument' : 'instrument'
2228+ }
2229+ return type_map .get (class_name , 'unknown' )
2230+
2231+ def set_device_param_by_name (self , track_index , device_index , param_name , value ):
2232+ """Set device parameter by name (useful for M4L devices with custom parameter names)"""
2233+ try :
2234+ if track_index < 0 or track_index >= len (self .song .tracks ):
2235+ return {"ok" : False , "error" : "Invalid track index" }
2236+
2237+ track = self .song .tracks [track_index ]
2238+ if device_index < 0 or device_index >= len (track .devices ):
2239+ return {"ok" : False , "error" : "Invalid device index" }
2240+
2241+ device = track .devices [device_index ]
2242+
2243+ # Find parameter by name
2244+ for i , param in enumerate (device .parameters ):
2245+ if str (param .name ) == param_name :
2246+ param .value = float (value )
2247+ return {
2248+ "ok" : True ,
2249+ "track_index" : track_index ,
2250+ "device_index" : device_index ,
2251+ "param_name" : param_name ,
2252+ "param_index" : i ,
2253+ "value" : float (param .value )
2254+ }
2255+
2256+ return {"ok" : False , "error" : "Parameter '{}' not found" .format (param_name )}
2257+ except Exception as e :
2258+ return {"ok" : False , "error" : str (e )}
2259+
2260+ def get_m4l_param_by_name (self , track_index , device_index , param_name ):
2261+ """Get M4L device parameter value by name"""
2262+ try :
2263+ if track_index < 0 or track_index >= len (self .song .tracks ):
2264+ return {"ok" : False , "error" : "Invalid track index" }
2265+
2266+ track = self .song .tracks [track_index ]
2267+ if device_index < 0 or device_index >= len (track .devices ):
2268+ return {"ok" : False , "error" : "Invalid device index" }
2269+
2270+ device = track .devices [device_index ]
2271+
2272+ # Find parameter by name
2273+ for i , param in enumerate (device .parameters ):
2274+ if str (param .name ) == param_name :
2275+ return {
2276+ "ok" : True ,
2277+ "param_index" : i ,
2278+ "name" : str (param .name ),
2279+ "value" : float (param .value ),
2280+ "min" : float (param .min ),
2281+ "max" : float (param .max ),
2282+ "is_enabled" : param .is_enabled if hasattr (param , 'is_enabled' ) else True
2283+ }
2284+
2285+ return {"ok" : False , "error" : "Parameter '{}' not found" .format (param_name )}
2286+ except Exception as e :
2287+ return {"ok" : False , "error" : str (e )}
2288+
2289+ def get_cv_tools_devices (self , track_index ):
2290+ """Get all CV Tools devices on track (subset of M4L devices)"""
2291+ try :
2292+ if track_index < 0 or track_index >= len (self .song .tracks ):
2293+ return {"ok" : False , "error" : "Invalid track index" }
2294+
2295+ track = self .song .tracks [track_index ]
2296+ cv_devices = []
2297+
2298+ for i , device in enumerate (track .devices ):
2299+ device_name = str (device .name )
2300+ # Check if device name contains "CV" (common in CV Tools)
2301+ if 'CV' in device_name or 'cv' in device_name .lower ():
2302+ cv_devices .append ({
2303+ "index" : i ,
2304+ "name" : device_name ,
2305+ "class_name" : str (device .class_name ),
2306+ "is_active" : device .is_active ,
2307+ "num_parameters" : len (device .parameters )
2308+ })
2309+
2310+ return {
2311+ "ok" : True ,
2312+ "track_index" : track_index ,
2313+ "track_name" : str (track .name ),
2314+ "cv_devices" : cv_devices ,
2315+ "count" : len (cv_devices )
2316+ }
2317+ except Exception as e :
2318+ return {"ok" : False , "error" : str (e )}
2319+
21602320 # ========================================================================
21612321 # UTILITY
21622322 # ========================================================================
@@ -2225,5 +2385,8 @@ def get_available_tools(self):
22252385 # Track routing extras (3 tools)
22262386 "get_track_output_routing" , "set_track_input_sub_routing" , "set_track_output_sub_routing" ,
22272387 # Device extras - missing tool (1 tool)
2228- "randomize_device"
2388+ "randomize_device" ,
2389+ # Max for Live (M4L) operations (6 tools)
2390+ "is_max_device" , "get_m4l_devices" , "set_device_param_by_name" ,
2391+ "get_m4l_param_by_name" , "get_cv_tools_devices"
22292392 ]
0 commit comments