No internet connection
  1. Home
  2. How to
  3. Ableton Live

Script To Arm and Select a given track is buggy / inconsistent

By wolaud @wolaud
    2025-06-07 02:57:58.791Z

    Title

    Script To Arm and Select a given track is buggy / inconsistent

    What do you expect to happen when you run the script/macro?

    This is a script worked on previously on the forum to select and arm a named track.

    Are you seeing an error?

    AbletonLiveSetObjectValueAction: Object reference not set to an instance of an object.

    What happens when you run this script?

    It spits out the given error ("AbletonLiveSetObjectValueAction: Object reference not set to an instance of an object.")

    I am using the same script across a number of other named tracks and it seems to work perfectly for 90% of them. This and 1 or 2 others exhibit odd behavior.

    How were you running this script?

    I clicked the "Run Script" or "Run Macro" button in SoundFlow

    How important is this issue to you?

    5

    Details

    {
        "inputExpected": "This is a script worked on previously on the forum to select and arm a named track. ",
        "inputIsError": true,
        "inputError": "AbletonLiveSetObjectValueAction: Object reference not set to an instance of an object.",
        "inputWhatHappens": "It spits out the given error (\"AbletonLiveSetObjectValueAction: Object reference not set to an instance of an object.\")\n\nI am using the same script across a number of other named tracks and it seems to work perfectly for 90% of them. This and 1 or 2 others exhibit odd behavior.",
        "inputHowRun": {
            "key": "-MpfwYA4I6GGlXgvp5j1",
            "title": "I clicked the \"Run Script\" or \"Run Macro\" button in SoundFlow"
        },
        "inputImportance": 5,
        "inputTitle": "Script To Arm and Select a given track is buggy / inconsistent"
    }

    Source

    // Bail out if Ableton is not running
    if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
    
    // Track Names
    let tracks = ['VST 1'];
    
    // Toggle arm state for each track
    tracks.forEach((trackName) => {
        let track = sf.app.abletonLive.song.tracks.find(tr => tr.name.stringValue === trackName);
    
        if (track && track.arm) {
            let currentArmState = track.arm.value;
            track.arm.setValue({ value: !currentArmState });
        }
        // Track to Select
    let trackToSelect = 'VST 1';
    
    // Filter for Track to Select
    let track = sf.app.abletonLive.song.visibleTracks.find(t => t.name.value === trackToSelect);
    
    // Set Selected Track
    sf.app.abletonLive.song.view.selectedTrack.setValue({ value: track });
    });
    

    Links

    User UID: YwZWhQkNk9YC9IGtGtlgw4vfPEt1

    Feedback Key: sffeedback:YwZWhQkNk9YC9IGtGtlgw4vfPEt1:-OS7JhYMFM9FekTOOxdj

    Feedback ZIP: n4+IiHQQ6aoSIM7WU9Chd8u1PJcvhKUL7/FgTBFS+VMFwoICbV+8IeI40RhsCGnS4ttTB4MDAFy9ScmqI1KC4JyjgF+JvCAJkqGtmnrLARtHKnYWcc6uEHZLd/+CpuDpjzCgSkZdXlJ76WVBXsHurAsQdSWX5fR6YE2LXaPMezOewXLgTxmvONSOdrvdAMd7GDvD56x3vSoPGHx5BuOGMZCaiNQU+lqSJJXX3xbhfUAxFJQwMzdRAQGHyNTXQ2GKKvKb1FqCtLRUehgTXAzYs+wbhHxgQf1cNaS8Gv0FS5JlbEv0+eHKwm0ZAOsT3EUd3bK2EWGyl5PaIjjuvL4FCKsiTZchVub0/Hv3nVxcbB4=

    Solved in post #8, click to view
    • 13 replies

    There are 13 replies. Estimated reading time: 25 minutes

    1. W
      wolaud @wolaud
        2025-06-07 02:59:29.195Z

        An added function I would love to add to this script (once it's working) would be to have it also search the clip slots and select the first empty one. Currently it defaults to whatever scene happens to be selected.

        But it would be great to get it working more consistently for now of course!

        1. Chad Wahlbrink @Chad2025-06-08 02:01:55.466Z

          Thanks, @wolaud! I am planning to look into this more on Monday and also follow up with any other thoughts from the previous post:

          If you're up for it, it could be nice to hop on a quick Zoom call and discuss how to approach these scenarios with Ableton more generally (working with track selection, checking clip slots for empty slots, etc.). We can do a lot by bouncing between UI automation and the Ableton Control Surface API with SoundFlow. In this instance, I think we'll need to use some UI automation to record to the next empty slot, but it should be possible!

          If a Zoom call interests you, contact me at support@soundflow.org, and I'll set it up.

          Regardless, I'll have more for you early this coming week!

          1. Wwolaud @wolaud
              2025-06-10 19:08:11.631Z

              Thanks Chad! I'll keep an eye out for your updates and reach out about getting a Zoom scheduled.

              1. Chad Wahlbrink @Chad2025-06-11 19:30:02.358Z

                Hi @wolaud,

                I tried your script and couldn't reproduce inconsistencies on my machine. I'd be curious if the session has multiple tracks named "VST 1" - that could potentially lead to issues with finding the correct track.

                Here's a slightly refactored version of the script you shared above:

                // Bail out if Ableton is not running
                if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
                
                // Track Names
                let tracks = ['VST 1'];
                
                // Toggle arm state for each track
                tracks.forEach((trackName) => {
                    // Find the Track
                    let track = sf.app.abletonLive.song.tracks.find(tr => tr.name.stringValue === trackName);
                
                    // Toggle Arm The Track
                    if (track && track.arm) {
                        let currentArmState = track.arm.value;
                        track.arm.setValue({ value: !currentArmState });
                    }
                
                    // Set Selected Track
                    sf.app.abletonLive.song.view.selectedTrack.setValue({ value: track });
                });
                

                There were some syntax errors in the version you shared, because let track was repeated twice in the same scope. It's possible that this could have caused issues in rare instances.

                As for arming the next free slot, I think this should do it for you. Although, I'm just always setting the arm state to true instead of toggling, if you do want it toggle and then record to the next empty slot, just let me know:

                // Bail out if Ableton is not running
                if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
                
                // Track Names
                let tracks = ['VST 1'];
                
                // Toggle arm state for each track
                tracks.forEach((trackName) => {
                    // Find the Track
                    let track = sf.app.abletonLive.song.tracks.find(tr => tr.name.stringValue === trackName);
                
                    // Set Selected Track
                    sf.app.abletonLive.song.view.selectedTrack.setValue({ value: track });
                
                    // Toggle Arm The Track
                    if (track && track.arm) {
                        track.arm.setValue({ value: true });
                        // Record to next free slot
                        track.clipSlots.invalidate().find(clipslot => clipslot.hasClip.boolValue === false).fire()
                
                    }
                });
                
                1. Chad Wahlbrink @Chad2025-06-11 19:33:02.692Z

                  Also, here's a quick video of me playing with your script if it's helpful.

                  I mention invalidation, which could be why you see some inconsistency with this script and potentially the one used to read the input level for arming/disarming. Here's a forum post on invalidation as well:

          2. In reply towolaud:
            Chad Wahlbrink @Chad2025-06-13 16:42:28.108Z

            Hi @wolaud,

            I made some progress on the script to select the next free clip slot.

            To select the next free clip slot on a named track like "VST 1" you can use this script:

            // Bail out if Ableton is not running
            if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
            
            // Track Name
            let trackName = 'VST 1';
            
            // Find the Track
            let trackToArm = sf.app.abletonLive.song.tracks.find(tr => tr.name.stringValue === trackName);
            
            // Toggle Arm The Track
            trackToArm.arm.setValue({ value: true });
            
            // Find the next Empty Clip Slot on the Track
            let uiClipSlots = sf.ui.abletonLive.mainWindow.session.slots.groups.filter(group => group.title.value.includes(`in track ${trackName},`));
            let firstEmptyClipSlot = uiClipSlots.findIndex(group => group.title.value.startsWith(`in track ${trackName}`));
            
            // Highlight Clip Slot
            sf.app.abletonLive.song.view.highlightedClipSlot.setValue({ value: trackToArm.clipSlots[firstEmptyClipSlot] });
            

            To do this for the selected Track, it would be this. Note that using sf.app.abletonLive.song.view.selectedTrack.invalidate(); in a session as big as the one you are working from may be a bit slow, but it is accurate - we may be able to work out a quicker method.

            // Bail out if Ableton is not running
            if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
            
            // Arm the Selected Track 
            let trackToArm = sf.app.abletonLive.song.view.selectedTrack.invalidate();
            
            let trackName = trackToArm.name.stringValue;
            
            // Toggle Arm The Track
            trackToArm.arm.setValue({ value: true });
            
            // Find the next Empty Clip Slot on the Track
            let uiClipSlots = sf.ui.abletonLive.mainWindow.session.slots.groups.filter(group => group.title.value.includes(`in track ${trackName},`));
            let firstEmptyClipSlot = uiClipSlots.findIndex(group => group.title.value.startsWith(`in track ${trackName}`));
            
            // Highlight Clip Slot
            sf.app.abletonLive.song.view.highlightedClipSlot.setValue({ value: trackToArm.clipSlots[firstEmptyClipSlot] });
            

            Then to record to the selected clip slot, you can use a script like this:

            // Bail out if Ableton is not running
            if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
            
            sf.app.abletonLive.song.view.highlightedClipSlot.fire();
            
            1. Wwolaud @wolaud
                2025-06-13 23:10:35.383Z

                Thanks Chad. Unfortunately the first one (select next free clip slot) does not seem to be working for me and gives the following error:

                13.06.2025 16:03:35.91 [Backend]: Logging unknown error in action (02) AbletonLiveSetObjectValueAction: Object reference not set to an instance of an object.

                When triggered it will arm the track if it isn't armed already and throw that error.

                The ideal version of this one for me would arm the track if it's not already armed and select the empty clip slot, and disarm the track if it's already armed.

                1. Chad Wahlbrink @Chad2025-06-14 01:01:38.178Z

                  That should be doable! I’ll do some sleuthing on the error you saw when I am back at my computer again.

                  It was working for me this morning on your template live set, it would need to be in session view for it to work currently.

                  1. Wwolaud @wolaud
                      2025-06-14 01:33:38.454Z

                      Okay so after restarting my machine it does in fact appear to work! Not sure why that would be but I'll take it as a win!

                      1. Wwolaud @wolaud
                          2025-06-14 01:40:17.937Z

                          So the next thing that would be great would be to make it toggle the state of the track arm instead of always set it to on. If that is possible.

                          Another thing I'm wondering is if this could be modified to select a range or group of multiple tracks?

                          1. Wwolaud @wolaud
                              2025-06-18 15:36:06.589Z

                              Hey Chad, wanted to follow up on this as I'm hoping to modify a script above and having a bit of trouble. Building off of this:

                              // Bail out if Ableton is not running
                              if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
                              
                              // Track Name
                              let trackName = 'VST 1';
                              
                              // Find the Track
                              let trackToArm = sf.app.abletonLive.song.tracks.find(tr => tr.name.stringValue === trackName);
                              
                              // Toggle Arm The Track
                              trackToArm.arm.setValue({ value: true });
                              
                              // Find the next Empty Clip Slot on the Track
                              let uiClipSlots = sf.ui.abletonLive.mainWindow.session.slots.groups.filter(group => group.title.value.includes(`in track ${trackName},`));
                              let firstEmptyClipSlot = uiClipSlots.findIndex(group => group.title.value.startsWith(`in track ${trackName}`));
                              
                              // Highlight Clip Slot
                              sf.app.abletonLive.song.view.highlightedClipSlot.setValue({ value: trackToArm.clipSlots[firstEmptyClipSlot] });
                              

                              This works perfectly - but I can't figure out a way to make it Arm multiple tracks (say VST1 and VST2) but then highlight only ONE clip slot — let's say VST1.

                              I was also wondering if it's possible to trigger a cycling list of macros with each button press? Say for example:

                              Button Press 1: Arm (or toggle track arm) for VST1 and VST2 and selects the first empty clip on VST1
                              Button Press 2: Switches selection to the first empty clip slot on VST2
                              Button Press 3: Disarm (or toggle) track arm VST1 and VST2.

                    • In reply towolaud:
                      Chad Wahlbrink @Chad2025-06-13 16:54:36.437Z

                      Hi @wolaud,

                      To toggle exclusive arm, I would use this script:

                      
                      // Bail out if Ableton is not running
                      if (!sf.ui.abletonLive.isRunning) throw `Ableton Live is not running`;
                      
                      // Define the Settings Window
                      let settingsWin = sf.ui.abletonLive.invalidate().windows.whoseTitle.is("Settings").first;
                      let settingsWinExisted = settingsWin.exists;
                      
                      // If the Settings Window Isn't On Screen, then Open it
                      if (!settingsWinExisted) {
                          sf.ui.abletonLive.menuClick({ menuPath: ['Live', 'Settings...'] });
                          settingsWin.elementWaitFor();
                      }
                      
                      // Select the 'Record, Warp & Launch' tab
                      sf.ui.abletonLive.mainWindow.groups.first.radioGroups.whoseTitle.is("Settings Page Chooser").first.radioButtons.whoseDescription.is("Record, Warp & Launch").first.elementClick();
                      
                      // Toggle Exclusive Arm
                      sf.ui.abletonLive.mainWindow.groups.first.groups.whoseTitle.is("Record, Warp and Launch Settings").first.checkBoxes.whoseDescription.is("Exclusive Arm").first.checkboxSet({ targetValue: "Toggle" });
                      
                      // Close the Settings Window if it wasn't already open
                      if (!settingsWinExisted) {
                          sf.ui.abletonLive.mainWindow.closeButton.elementClick();
                      }
                      
                      ReplySolution
                      1. In reply towolaud:
                        Chad Wahlbrink @Chad2025-06-13 17:12:49.533Z2025-06-13 17:46:16.185Z

                        To set the fade mode for an audio clip, you can use this.

                        This will be included in the next version of the official package as well!

                        
                        let clipViewTab = "Tool Tabs"
                        let horizontalOrientationName = "Audio Utilities"
                        let verticalOrientationName = "SampleTools"
                        let checkBox = "Clip Fade-In/Fade-Out"
                        let targetValue = "Toggle"
                        
                        // Click the Audio Clip View Button
                        setAudioClipViewCheckBox({
                            clipViewTab,
                            horizontalOrientationName,
                            verticalOrientationName,
                            checkBox,
                            targetValue
                        })
                        
                        /**
                         * Navigate to Clip View Tab
                         * @param {Object} props 
                         * @param {Object} props.radioGroup
                         * @param {string} props.targetRadioButtonName
                         */
                        function navigateToClipViewTab({ radioGroup, targetRadioButtonName }) {
                        
                            const radioButtons = radioGroup.radioButtons;
                        
                            // Find current radio button (by intValue === 1)
                            const currentRadioButton = radioButtons.find(rb => rb.value.intValue === 1);
                            if (!currentRadioButton) throw new Error("Current radio button not found");
                        
                            const currentName = currentRadioButton.getString("AXDescription");
                        
                            // Find indices of current and target radio buttons
                            const currentIndex = radioButtons.findIndex(rb => rb.getString("AXDescription") === currentName);
                            const targetIndex = radioButtons.findIndex(rb => rb.getString("AXDescription") === targetRadioButtonName);
                        
                            if (targetIndex === -1) throw new Error(`Target button '${targetRadioButtonName}' not found in radio group`);
                        
                            // 
                            const delta = targetIndex - currentIndex;
                        
                            if (delta === 0) return; // Already on the target
                        
                            // Focus the current button so that it accepts arrow key presses
                            currentRadioButton.elementSetAttributeValue({
                                attributeName: "AXFocused",
                                attributeValue: true,
                            });
                        
                            const directionKey = delta > 0 ? "kVK_RightArrow" : "kVK_LeftArrow";
                        
                            for (let i = 0; i < Math.abs(delta); i++) {
                                sf.keyboard.internalPress({ virtualKey: directionKey });
                        
                                // Maybe add a waitFor to make sure the intValue has changed for the target radio button.
                            }
                        }
                        
                        /**
                         * Set Audio Clip View checkBox
                         */
                        function setAudioClipViewCheckBox({ clipViewTab, horizontalOrientationName, verticalOrientationName, checkBox, targetValue }) {
                        
                            // Activate Ableton
                            sf.ui.abletonLive.appActivateMainWindow();
                        
                            const mainWindow = sf.ui.abletonLive.mainWindow;
                        
                            const clipDetailGroup = mainWindow.groups.first.groups.whoseTitle.is("Clip Detail").first;
                            const clipTitleBar = clipDetailGroup.groups.whoseTitle.is("Clip Title Bar").first;
                            const radioGroup = clipDetailGroup.radioGroups.whoseTitle.is(clipViewTab).first;
                        
                            // If Clip View is Not Shown, Then Show It
                            if (!sf.ui.abletonLive.getMenuItem('View', 'Clip View').isMenuChecked) {
                                sf.ui.abletonLive.menuClick({ menuPath: ['View', 'Clip View'] })
                            }
                        
                            // If no clip is selected then bail
                            if (clipDetailGroup.children.whoseRole.is("AXStaticText").whoseValue.is("No clip selected.").first.exists) {
                                log(`No Clip Selected`)
                                throw 0;
                            }
                        
                            // If this is not a Audio clip then, we should bail! 
                            if (!sf.app.abletonLive.song.view.detailClip.invalidate().isAudioClip.boolValue) {
                                log(`This Is Not An Audio Clip`);
                                throw 0;
                            }
                        
                            function showHideClipProperties(targetValue) {
                                if (clipTitleBar.exists) {
                                    clipTitleBar.elementClick({ actionName: 'AXShowMenu' })
                        
                                    // Explicitly define the context menu
                                    let contextMenuWin = sf.ui.abletonLive.windows.whoseTitle.is('').allItems.find(window => window.groups.first.groups.whoseTitle.is('Context').exists)
                                        .groups.first.groups.whoseTitle.is("Context").first;
                        
                                    let foldMenuItem = contextMenuWin.menuItems.whoseTitle.endsWith('Fold').first;
                        
                                    // Wait for the context menu to show up
                                    contextMenuWin.elementWaitFor({}, `Failed Waiting for the context menu`);
                        
                                    if (foldMenuItem.exists && foldMenuItem.title.invalidate().value == 'Fold' && (targetValue == "Disable" || targetValue == "Toggle")) {
                                        foldMenuItem.elementClick({ actionName: "AXPick" });
                                    } else if (foldMenuItem.title.invalidate().value == '✔ Fold' && (targetValue == "Enable" || targetValue == "Toggle")) {
                                        contextMenuWin.menuItems.whoseTitle.is('✔ Fold').first.elementClick({ actionName: "AXPick" })
                                    } else {
                                        //dismiss menu
                                        sf.keyboard.internalPress({ virtualKey: `kVK_Escape` });
                                    }
                                }
                            }
                        
                            let werePropsHid = (!radioGroup.invalidate().radioButtons.exists)
                                && (!clipDetailGroup.checkBoxes.whoseDescription.is(verticalOrientationName).first.exists);
                        
                            // Make sure the clip view panel is shown in some form - it's going to default to vertical
                            if (werePropsHid) {
                                showHideClipProperties('Enable')
                            }
                        
                            // In horizontal mode, make sure we are on the right tool tab
                            if (radioGroup.radioButtons.whoseDescription.is(horizontalOrientationName).first.exists
                                && radioGroup.radioButtons.whoseDescription.is(horizontalOrientationName).first.value.intValue == 0) {
                        
                                navigateToClipViewTab({
                                    radioGroup,
                                    targetRadioButtonName: horizontalOrientationName
                                });
                        
                            }
                            // If we are in vertical mode, ensure the tab is open.
                            else if (clipDetailGroup.checkBoxes.whoseDescription.is(verticalOrientationName).first.exists) {
                                clipDetailGroup.checkBoxes.whoseDescription.is(verticalOrientationName).first.checkboxSet({ targetValue: "Enable" })
                            }
                            // If clip details are shown and neither version of the Sample Tools clip view exists, this is not an audio clip. 
                            else if (!radioGroup.radioButtons.whoseDescription.is(horizontalOrientationName).first.exists
                                && !clipDetailGroup.checkBoxes.whoseDescription.is(verticalOrientationName).first.exists
                                && !sf.app.abletonLive.song.view.detailClip.invalidate().isAudioClip.boolValue) {
                                log(`This Is Not An Audio Clip`);
                                throw 0;
                            }
                        
                            if (checkBox == 'Clip Fade-In/Fade-Out' && !clipDetailGroup.checkBoxes.whoseDescription.is(checkBox).first.exists) {
                                throw `Clip Fade-In/Fade-Out is only available in Session View.`
                            }
                        
                            // Set The CheckBox
                            clipDetailGroup.checkBoxes.whoseDescription.is(checkBox).first.checkboxSet({ targetValue: targetValue })
                        
                        
                            if (werePropsHid) {
                                showHideClipProperties('Disable')
                            }
                        }