Scale marker regions to match tempo marker?

I have a music template that I’d very much like to not have to replicate every time I recreate the event, as it has approx 10 markers and twice as many regions.

Is it possible to resize all regions that are after a tempo marker to fit the new tempo? so they fit to the new grid?

I don’t think it’s directly possible. But instead of saving your template, you could script it, which would allow you to recreate everything at the right place. Here’s a code exemple, which you can try in the console, that places a loop in a new event, between bar 5 and 7, whatever tempo you choose for the “tempo” variable.

myEvent = studio.project.create("Event");
logictrack = myEvent.markerTracks[0];
tempo = 80;
start = 5;
stop = 7;
logictrack.addRegion((start-1)*60/tempo*4 , (stop-start)*60/tempo*4 , "loop" , 1);
1 Like

As of the time of writing (February of 2023), the only way to do this is to create or edit an event by using a script, as Alcibiade suggests.

That being said, a task to add a dedicated feature for this is already in our feature and improvement tracker, so I’ll add you to the list of people interested in it.

1 Like

The Following Script works perfectly to solve my problem, for those of you wanting to achieve the same thing before it gets officially implemented. Just save it as a .js and out it in the sscripts folder of your specific install.
Thanks guys.

studio.menu.addMenuItem({
  name: "Change Tempo While Preserving Beat Alignments",
  isEnabled: function() {
    var current = studio.window.editorCurrent();
    return current != null && current.entity == "TempoMarker";
  },
  execute: function() {
    var tempoMarker = studio.window.editorCurrent();

    var newTempo = studio.system.getNumber("Enter the new tempo:", tempoMarker.tempo);

    if (newTempo == null || newTempo <= 0) {
      return;
    }

    repositionModules(tempoMarker, newTempo);
    repositionMarkers(tempoMarker, newTempo);
    repositionAutomationCurvePoints(tempoMarker, newTempo);

    tempoMarker.tempo = newTempo;
  },
});

function positionToBeats(position, basePosition, tempo) {
  var offset = position - basePosition;
  return offset / 60 * tempo;

}

function beatsToPosition(beats, basePosition, tempo) {
  var offset = beats * 60 / tempo;
  return basePosition + offset;
}

function repositionModules(tempoMarker, newTempo) {
  var modules = tempoMarker.timeline.modules;

  for (var i = 0; i < modules.length; ++i) {
    var module = modules[i];

    if (module.start >= tempoMarker.position) {
      var startBeat = positionToBeats(module.start, tempoMarker.position, tempoMarker.tempo);
      var endBeat = positionToBeats(module.start + module.length, tempoMarker.position, tempoMarker.tempo);

      module.start = beatsToPosition(startBeat, tempoMarker.position, newTempo);
      module.length = beatsToPosition(endBeat, tempoMarker.position, newTempo) - module.start;

      repositionFadeCurve(module.fadeInCurve, tempoMarker, newTempo);
      repositionFadeCurve(module.fadeOutCurve, tempoMarker, newTempo);
    }
  }
}

function repositionFadeCurve(fadeCurve, tempoMarker, newTempo) {
  if (fadeCurve != null) {
    repositionAutomationPoint(fadeCurve.startPoint, tempoMarker, newTempo);
    repositionAutomationPoint(fadeCurve.endPoint, tempoMarker, newTempo);
  }
}

function repositionMarkers(tempoMarker, newTempo) {
  var markers = tempoMarker.timeline.markers;

  for (var i = 0; i < markers.length; ++i) {
    var marker = markers[i];

    if (marker != tempoMarker && marker.position >= tempoMarker.position) {
      var startBeat = positionToBeats(marker.position, tempoMarker.position, tempoMarker.tempo);
      var endBeat = null;

      if (marker.length != null) {
        endBeat = positionToBeats(marker.position + marker.length, tempoMarker.position, tempoMarker.tempo);
      }

      marker.position = beatsToPosition(startBeat, tempoMarker.position, newTempo);

      if (marker.length != null) {
        marker.length = beatsToPosition(endBeat, tempoMarker.position, newTempo) - marker.position;
      }
    }
  }
}

function repositionAutomationCurvePoints(tempoMarker, newTempo) {
  var curves = tempoMarker.timeline.automationCurves;

  for (var i = 0; i < curves.length; ++i) {
    var points = curves[i].automationPoints;

    for (var j = 0; j < points.length; ++j) {
      var point = points[j];

      if (point.position >= tempoMarker.position) {
        repositionAutomationPoint(point, tempoMarker, newTempo);
      }
    }
  }
}

function repositionAutomationPoint(point, tempoMarker, newTempo) {
  var beat = positionToBeats(point.position, tempoMarker.position, tempoMarker.tempo);
  point.position = beatsToPosition(beat, tempoMarker.position, newTempo);
}
2 Likes

Hey Joseph!
Is this feature in the development?
I’m also interested in this functionality.

As of the time of writing (February of 2025), this feature is in our tracker of planned improvements, but has not yet been scheduled for development.

I’ll add you to the list of people interested in this feature.