Midi Velocity Up & Down and pre determined values
Hey @Kitch!
I've got another idea that I've been exploring but can't really find an elegant solution to. Maybe there's already something that can help with this? I've tried a couple of ideas but have kept hitting roadblocks.
I'd love to be able to select a note in the piano roll with my mouse while simultaneously clicking a stream deck button to either raise or lower the velocity. Going further, it would be great to have pre-determined level values of increse and decrease (like +10, -10).
As far as I can tell, there might be a way to do this with the step editor via midi control, but that defeats the whole idea of having a quick way to punch up the velocity while editing in the piano roll without having to manual slide it with the mouse or opening the velocity automation lane.
Thanks!
Linked from:
- Kitch Membery @Kitch2024-09-09 17:10:56.872Z
Hi @Justin_Krol,
I'll take a look this week and see if it's something that we can achieve, I can see the benefits!
Thanks for the request.
- SIn reply toJustin_Krol⬆:SoundFlow Bot @soundflowbot
This issue is now tracked internally by SoundFlow as SF-1441
- In reply toJustin_Krol⬆:Kitch Membery @Kitch2024-11-14 19:08:05.666Z
Hi @Justin_Krol,
I did some research into this and unfortunately hit a roadblock.
When multiple notes of different velocities are selected, the velocity slider value only displays the lowest velocity of all selected notes.
However, if your use case is for single MIDI notes or MIDI notes with the same velocity values, this script will do the trick.
const logic = sf.ui.logic; // Bail out if Logic Pro is not running if (!logic.isRunning) throw `Logic Pro is not running`; logic.invalidate(); logic.appActivate(); const pianoRollGroup = logic.mainWindow.groups.whoseDescription.is("Piano Roll").first; const pianoRoll = pianoRollGroup.groups.whoseDescription.is("Piano Roll").allItems[1]; const pianoRollInspector = pianoRoll.splitGroups.first.splitGroups.allItems[1].children.whoseRole.is("AXUnknown").whoseDescription.is("Inspector").first; const velocityGroup = pianoRollInspector.scrollAreas.first.groups.find(g => g.children.whoseRole.is("AXStaticText").whoseValue.is("Velocity").first.exists); const [_, velocityValueText, velocitySliderElement] = velocityGroup.children.map(e => e); const velocitySlider = velocitySliderElement.children.whoseRole.is("AXValueIndicator").first; if (!velocitySlider) throw `Could not find the Velocity slider`; const getCurrentVelocity = () => Number(velocityValueText.value.invalidate().value); // Increments the Slider by 1 const incrementSmall = () => velocitySlider.value.value = "1"; // Increments the Slider by 5 const incrementLarge = () => velocitySliderElement.elementClick({ actionName: "AXIncrement" }); // Decrements the Slider by 1 const decrementSmall = () => velocitySlider.value.value = "-1"; // Decrements the Slider by 5 const decrementLarge = () => velocitySliderElement.elementClick({ actionName: "AXDecrement" }); /** * Adjusts the velocity in the Piano Roll. * * @param {Object} options - The adjustment options. * @param {number} [options.targetVelocity] - The specific velocity to set (must be between 1 and 127). * @param {number} [options.adjustBy] - The amount to increase or decrease the current velocity. * @throws Will throw an error if targetVelocity is provided and is out of range (1-127). */ function adjustVelocity({ targetVelocity, adjustBy } = {}) { if (targetVelocity != null) { if (targetVelocity < 1 || targetVelocity > 127) { log("Set/Adjust Velocity", `Target Velocity must be between 1 and 127`); throw 0 } } else if (adjustBy != null) { // Calculate the target based on current velocity and adjustBy targetVelocity = Math.max(1, Math.min(127, getCurrentVelocity() + adjustBy)); } else if(targetVelocity === undefined && adjustBy === undefined){ log("Set/Adjust Velocity", `This preset must have either a "Target Velocity" or "Increment By" property value`); throw 0 } // Adjust to reach target velocity while (getCurrentVelocity() !== targetVelocity) { const currentVelocity = getCurrentVelocity(); const delta = Math.abs(currentVelocity - targetVelocity); if (currentVelocity > targetVelocity) { // Decrement if (delta >= 5) { decrementLarge(); } else { for (let i = 0; i < delta; i++) decrementSmall(); } } else { // Increment if (delta >= 5) { incrementLarge(); } else { for (let i = 0; i < delta; i++) incrementSmall(); } } sf.engine.checkForCancellation(); } }
Here are some example use cases for the function call.
// Set a target velocity value between 1-127 adjustVelocity({ targetVelocity:60 });
// Increment the note velocity by 10 adjustVelocity({ adjustBy:10 });
// Decrement the note velocity by 10 adjustVelocity({ adjustBy:-10 });
- In reply toJustin_Krol⬆:Kitch Membery @Kitch2024-11-14 19:13:37.392Z
You could also turn this into a Command Template...
const { targetVelocity, adjustBy } = event.props; const logic = sf.ui.logic; // Bail out if Logic Pro is not running if (!logic.isRunning) throw `Logic Pro is not running`; logic.invalidate(); logic.appActivate(); const pianoRollGroup = logic.mainWindow.groups.whoseDescription.is("Piano Roll").first; const pianoRoll = pianoRollGroup.groups.whoseDescription.is("Piano Roll").allItems[1]; const pianoRollInspector = pianoRoll.splitGroups.first.splitGroups.allItems[1].children.whoseRole.is("AXUnknown").whoseDescription.is("Inspector").first; const velocityGroup = pianoRollInspector.scrollAreas.first.groups.find(g => g.children.whoseRole.is("AXStaticText").whoseValue.is("Velocity").first.exists); const [_, velocityValueText, velocitySliderElement] = velocityGroup.children.map(e => e); const velocitySlider = velocitySliderElement.children.whoseRole.is("AXValueIndicator").first; if (!velocitySlider) throw `Could not find the Velocity slider`; const getCurrentVelocity = () => Number(velocityValueText.value.invalidate().value); // Increments the Slider by 1 const incrementSmall = () => velocitySlider.value.value = "1"; // Increments the Slider by 5 const incrementLarge = () => velocitySliderElement.elementClick({ actionName: "AXIncrement" }); // Decrements the Slider by 1 const decrementSmall = () => velocitySlider.value.value = "-1"; // Decrements the Slider by 5 const decrementLarge = () => velocitySliderElement.elementClick({ actionName: "AXDecrement" }); /** * Adjusts the velocity in the Piano Roll. * * @param {Object} options - The adjustment options. * @param {number} [options.targetVelocity] - The specific velocity to set (must be between 1 and 127). * @param {number} [options.adjustBy] - The amount to increase or decrease the current velocity. * @throws Will throw an error if targetVelocity is provided and is out of range (1-127). */ function adjustVelocity({ targetVelocity, adjustBy } = {}) { if (targetVelocity != null) { if (targetVelocity < 1 || targetVelocity > 127) { log("Set/Adjust Velocity", `Target Velocity must be between 1 and 127`); throw 0 } } else if (adjustBy != null) { // Calculate the target based on current velocity and adjustBy targetVelocity = Math.max(1, Math.min(127, getCurrentVelocity() + adjustBy)); } else if(targetVelocity === undefined && adjustBy === undefined){ log("Set/Adjust Velocity", `This preset must have either a "Target Velocity" or "Increment By" property value`); throw 0 } // Adjust to reach target velocity while (getCurrentVelocity() !== targetVelocity) { const currentVelocity = getCurrentVelocity(); const delta = Math.abs(currentVelocity - targetVelocity); if (currentVelocity > targetVelocity) { // Decrement if (delta >= 5) { decrementLarge(); } else { for (let i = 0; i < delta; i++) decrementSmall(); } } else { // Increment if (delta >= 5) { incrementLarge(); } else { for (let i = 0; i < delta; i++) incrementSmall(); } } sf.engine.checkForCancellation(); } } adjustVelocity({ targetVelocity, adjustBy });
With template properties set up like this...
With presets like this...
- SIn reply toJustin_Krol⬆:SoundFlow Bot @soundflowbot
The linked internal issue SF-1441 has been marked as Done