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=
- Wwolaud @wolaud
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!
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!
- Wwolaud @wolaud
Thanks Chad! I'll keep an eye out for your updates and reach out about getting a Zoom scheduled.
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() } });
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:
- 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();
- Wwolaud @wolaud
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.
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.
- Wwolaud @wolaud
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!
- Wwolaud @wolaud
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?
- Wwolaud @wolaud
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(); }
- 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') } }