Script for specified change of Pl.back Rate and length of sel. Events

Julian-M wrote on 9/24/2024, 4:38 PM

Hello! 👋
From time to time in my projects I need to speed up by exactly 1.2x (or 1.5x) source videos with associated audio events of the same length. ↔️

To do this for one video, I go to the Properties of the video event, specify the Playback Rate value as 1.200 instead of 1.000. Then I enable "Ignore Event Grouping" and manually pull the right edge of the video event to the resulting loop point, then manually stretch the audio event until its edge is aligned with the edge of the video event.
Then I select both events and group them again. ♻️

In principle, for one video this process will take about half a minute.
But if you have to do the same thing many times, it becomes a very long and tedious task... 😵

I tried to work with chatGPT 🤖, because unfortunately I don't have programming skills =(
Chat offered me code after code, and I received error after error, and asked him for corrections.
After a long time, he was able to fix the errors and presented me with a working code, which I will attach to this post.

https://drive.google.com/file/d/1_5O3EElEfUUU2BSWvkWiP6y96jhOhQws/view?usp=sharing

I am using Vegas Pro Version 20.0 (Build 403)

But this script has a bug 🐞 that Chat could not fix even after a long time of my feedback.


The thing is that it tries to calculate a new length of the selected events, to which the Playback Rate change was applied, by dividing their original length by the Playback Rate coefficient 🕑.
As a result, the right border of the events remains a little further than the loop point.

If you have the desire, time and opportunity, could you try this script and also look at its code?
Perhaps such a function will be useful to someone else besides me.

Is it possible to fix the shortcoming with the event length?
Or maybe there are already ready-made working scripts for the same purpose?
Is it possible to use scripting to stretch video and audio events with a given coefficient?

Because if you do it manually using "CTRL" and holding the left mouse button on the event border, then both the event length and the Playback Rate change occur simultaneously, and not separately, as in the GPT script!

I will be grateful for any help!🙌

 

Comments

zzzzzz9125 wrote on 9/25/2024, 4:33 AM

But this script has a bug 🐞 that Chat could not fix even after a long time of my feedback.


The thing is that it tries to calculate a new length of the selected events, to which the Playback Rate change was applied, by dividing their original length by the Playback Rate coefficient 🕑.
As a result, the right border of the events remains a little further than the loop point.

@Julian-M Changing the rate only does NOT change the start of the event. You can use TrackEvent.AdjustPlaybackRate(double rate, bool adjustStart) in your code, such as using:

videoEvent.AdjustPlaybackRate(playbackRate, true);
audioEvent.AdjustPlaybackRate(playbackRate, true);

instead of:

videoEvent.PlaybackRate = playbackRate;
audioEvent.PlaybackRate = playbackRate;

 

 

Last changed by zzzzzz9125 on 9/25/2024, 4:34 AM, changed a total of 1 times.

Using VEGAS Pro 22 build 248 & VEGAS Pro 21 build 208.

Information about my PC:
Brand Name: HP VICTUS Laptop
System: Windows 11.0 (64-bit) 10.00.22631
CPU: 12th Gen Intel(R) Core(TM) i7-12700H
GPU: NVIDIA GeForce RTX 3050 Laptop GPU
GPU Driver: NVIDIA Studio Driver 560.70

Julian-M wrote on 9/25/2024, 5:40 AM

@Julian-M Changing the rate only does NOT change the start of the event. You can use TrackEvent.AdjustPlaybackRate(double rate, bool adjustStart) in your code, such as using:

videoEvent.AdjustPlaybackRate(playbackRate, true);
audioEvent.AdjustPlaybackRate(playbackRate, true);

instead of:

videoEvent.PlaybackRate = playbackRate;
audioEvent.PlaybackRate = playbackRate;

Thanks You! 🙏
I tried to make this change to the code using GPT chat.
I hope the change to the code was made correctly, and this is what you meant.
But unfortunately it did not bring the desired results.
The length of the events still does not correspond to the change in the speed of the events.
Here is how the script behaves now:

Here is the updated version of the script:
https://drive.google.com/file/d/1pAEoSocmPxJngxahswtERXjYtpsdE3DM/view?usp=sharing

zzzzzz9125 wrote on 9/25/2024, 6:36 AM

@Julian-M Why did you delete CorrectVideoEventLength() and CorrectAudioEventLength()? For code that changes the length, the old one is correct. All that needs to be changed are the two lines I mentioned above that change the rate.

However, CorrectVideoEventLength() and CorrectAudioEventLength() in the old one is not very clean. For example, you can use the following code:

    void CorrectEventLength(TrackEvent ev, double playbackRate)
    {
        Timecode newLength = Timecode.FromSeconds(ev.Length.ToMilliseconds() / 1000 / playbackRate);

        ev.Length = newLength;
    }

 

Complete code with a slight change:

using System;
using System.Collections.Generic;
using System.Windows.Forms; // For pop-up window
using ScriptPortal.Vegas;

public class EntryPoint
{
    public void FromVegas(Vegas vegas)
    {
        // Entering the speed factor via the pop-up window
        string input = InputBox("Enter the speed change coefficient:", "Change speed", "1.2");

        // Replace the comma with a period if necessary
        input = input.Replace(',', '.');

        double playbackRate;
        if (!double.TryParse(input, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out playbackRate) || playbackRate <= 0)
        {
            MessageBox.Show("Invalid speed factor input.");
            return;
        }

        List<VideoEvent> videoEvents = new List<VideoEvent>();
        AddSelectedVideoEvents(vegas, videoEvents);

        // If no events are selected, do nothing.
        if (videoEvents.Count == 0)
        {
            MessageBox.Show("There are no selected events.");
            return;
        }

        // Apply stretching to every event
        foreach (VideoEvent videoEvent in videoEvents)
        {
            AdjustRateAndLength(videoEvent, playbackRate);

            // Change the speed and length of the associated audio event
            foreach (Track track in vegas.Project.Tracks)
            {
                if (track.IsAudio())
                {
                    foreach (AudioEvent audioEvent in track.Events)
                    {
                        // Check if the audio event is associated with the same track or group
                        if (audioEvent.Selected || IsLinked(audioEvent, videoEvent))
                        {
                            AdjustRateAndLength(audioEvent, playbackRate);
                        }
                    }
                }
            }
        }
    }

    void AdjustRateAndLength(TrackEvent ev, double playbackRate)
    {
        ev.AdjustPlaybackRate(playbackRate, true);
        ev.Length = Timecode.FromSeconds(ev.Length.ToMilliseconds() / 1000 / playbackRate);
    }

    // Check if an audio event is associated with a video event (group or time interval)
    bool IsLinked(AudioEvent audioEvent, VideoEvent videoEvent)
    {
        return audioEvent.Group == videoEvent.Group || 
               (audioEvent.Start == videoEvent.Start && audioEvent.End == videoEvent.End);
    }

    void AddSelectedVideoEvents(Vegas vegas, List<VideoEvent> events)
    {
        foreach (Track track in vegas.Project.Tracks)
        {
            if (track.IsVideo())
            {
                foreach (VideoEvent videoEvent in track.Events)
                {
                    if (videoEvent.Selected)
                    {
                        events.Add(videoEvent);
                    }
                }
            }
        }
    }

    // Method for popup window with input data
    public static string InputBox(string prompt, string title, string defaultValue)
    {
        Form form = new Form();
        Label label = new Label();
        TextBox textBox = new TextBox();
        Button buttonOk = new Button();
        Button buttonCancel = new Button();

        form.Text = title;
        label.Text = prompt;
        textBox.Text = defaultValue;

        buttonOk.Text = "OK";
        buttonCancel.Text = "Отмена";
        buttonOk.DialogResult = DialogResult.OK;
        buttonCancel.DialogResult = DialogResult.Cancel;

        label.SetBounds(9, 20, 372, 13);
        textBox.SetBounds(12, 36, 372, 20);
        buttonOk.SetBounds(228, 72, 75, 23);
        buttonCancel.SetBounds(309, 72, 75, 23);

        label.AutoSize = true;
        form.ClientSize = new System.Drawing.Size(396, 107);
        form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel });
        form.ClientSize = new System.Drawing.Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
        form.FormBorderStyle = FormBorderStyle.FixedDialog;
        form.StartPosition = FormStartPosition.CenterScreen;
        form.MinimizeBox = false;
        form.MaximizeBox = false;
        form.AcceptButton = buttonOk;
        form.CancelButton = buttonCancel;

        DialogResult dialogResult = form.ShowDialog();
        return textBox.Text;
    }
}

By the way, for short scripts, it's recommended to use Insert Code above the forum input box rather than uploading them to an external website.

Last changed by zzzzzz9125 on 9/25/2024, 6:38 AM, changed a total of 1 times.

Using VEGAS Pro 22 build 248 & VEGAS Pro 21 build 208.

Information about my PC:
Brand Name: HP VICTUS Laptop
System: Windows 11.0 (64-bit) 10.00.22631
CPU: 12th Gen Intel(R) Core(TM) i7-12700H
GPU: NVIDIA GeForce RTX 3050 Laptop GPU
GPU Driver: NVIDIA Studio Driver 560.70

Julian-M wrote on 9/25/2024, 6:51 AM

However, CorrectVideoEventLength() and CorrectAudioEventLength() in the old one is not very simple. For example, you can use the following code:

Complete code with a slight change:

Wow, this works flawlessly now! ✅😃🎉

Thank you so much for taking the time to fix this code for me! 🙌
Now mass stretching of event events will be much faster and more fun!) ✈️
God bless you, zzzzzz9125!!! 🙏

By the way, for short scripts, it's recommended to use Insert Code above the forum input box rather than uploading them to an external website.

I will take this into account! 👌
I just thought that this is not the smallest script, since it takes up almost two pages)) 📃

Julian-M wrote on 9/25/2024, 9:40 AM

There is a small problem with the script 🤏.

This is already a bit beyond the topic of this post.
1️⃣ For a single video + audio event, the script works well (if you do not reuse it, I will talk about this a little later).

But if I select several pairs of video\audio events, then the audio events change their length incorrectly, they become shorter, as if they are divided again by the specified coefficient ⏮.

And also the peculiarity of this phenomenon is that the more pairs of events I select, the shorter the audio events are) 🙈

2️⃣ What concerns repeated application of script 🔄:
After changing the Playback Rate value by the specified coefficient (in this case, by 1.2), the script calculates the new length of the event by dividing its original length by the same coefficient.

But if, for example, the event already has Playback Rate = 1.2 and the script is applied again, then the actual length will again be divided by 1.2 ➗.
I suggested to the GPT chat to rewrite the calculation of the new event length using the following formula:

k - the specified coefficient;
cpr - current playback rate;
elk - event length coefficient;
cel - current event length;
nel - new event length;
elk = 1 + (cpr - k);
nel = cel / elk;

Thus, when the current coefficient was equal to the one specified in the code, then the event length should not change 📊.
GPT interpreted this as:

    // Method for changing the speed and length of the event
    void AdjustRateAndLength(TrackEvent ev, double playbackRate)
    {
        // Get the current Playback Rate
        double currentPlaybackRate = ev.PlaybackRate;

        // Calculate the coefficient for changing the length of the event
        double lengthCoefficient = 1 + (currentPlaybackRate - playbackRate);

        // Calculate the new length of the event
        double newLength = ev.Length.ToMilliseconds() / 1000 / lengthCoefficient;

        // Change the speed and length of the event
        ev.AdjustPlaybackRate(playbackRate, true);
        ev.Length = Timecode.FromSeconds(newLength);
    }


But unfortunately this turned out to be a broken theory, or incorrect integration 🤷‍♂️.

zzzzzz9125 wrote on 9/25/2024, 10:35 AM

1️⃣ For a single video + audio event, the script works well (if you do not reuse it, I will talk about this a little later).

But if I select several pairs of video\audio events, then the audio events change their length incorrectly, they become shorter, as if they are divided again by the specified coefficient ⏮.

And also the peculiarity of this phenomenon is that the more pairs of events I select, the shorter the audio events are) 🙈

@Julian-M The mistake here is:

            // Change the speed and length of the associated audio event
            foreach (Track track in vegas.Project.Tracks)
            {
                if (track.IsAudio())
                {
                    foreach (AudioEvent audioEvent in track.Events)
                    {
                        // Check if the audio event is associated with the same track or group
                        if (audioEvent.Selected || IsLinked(audioEvent, videoEvent))
                        {
                            AdjustRateAndLength(audioEvent, playbackRate);
                        }
                    }
                }
            }

After changing the rate of each video event, it traverses all audio events in the project. Once one (or more) of them is selected, its length will be changed. This causes the selected audio events to change rate several times.

The correct logic would be to traverse in its event group and change the rate and length of the event in the group:

            // Change the speed and length of the associated audio event
            foreach (TrackEvent ev in videoEvent.Group)
            {
                if (ev != videoEvent)
                {
                    AdjustRateAndLength(ev, playbackRate);
                }
            }

 

2️⃣ What concerns repeated application of script 🔄:
After changing the Playback Rate value by the specified coefficient (in this case, by 1.2), the script calculates the new length of the event by dividing its original length by the same coefficient.

But if, for example, the event already has Playback Rate = 1.2 and the script is applied again, then the actual length will again be divided by 1.2 ➗.

Instead of:

    void AdjustRateAndLength(TrackEvent ev, double playbackRate)
    {
        ev.AdjustPlaybackRate(playbackRate, true);
        ev.Length = Timecode.FromSeconds(ev.Length.ToMilliseconds() / 1000 / playbackRate);
    }

Use:

    void AdjustRateAndLength(TrackEvent ev, double playbackRate)
    {
        ev.Length = Timecode.FromSeconds(ev.Length.ToMilliseconds() / 1000 * ev.PlaybackRate / playbackRate);
        ev.AdjustPlaybackRate(playbackRate, true);
    }

This is a very simple change, you just need to add the old rate into your calculation. For convenience, the order of rate and length changes is swapped.

Last changed by zzzzzz9125 on 9/25/2024, 10:36 AM, changed a total of 1 times.

Using VEGAS Pro 22 build 248 & VEGAS Pro 21 build 208.

Information about my PC:
Brand Name: HP VICTUS Laptop
System: Windows 11.0 (64-bit) 10.00.22631
CPU: 12th Gen Intel(R) Core(TM) i7-12700H
GPU: NVIDIA GeForce RTX 3050 Laptop GPU
GPU Driver: NVIDIA Studio Driver 560.70

bvideo wrote on 9/25/2024, 11:17 AM

To account for a previous rate not equal to 1.00, how about:

double newLength = videoEvent.Length.ToMilliseconds() * videoEvent.PlaybackRate / playbackRate;

Also, it could happen that the new length violates frame boundaries, so if that's important, add:

newLength = Timecode.FromFrames(newLength.FrameCount);

One other thing is that it may be easier to find grouped events by iterating the events in the group. Also, the end of a grouped event might no longer match the end of the original event that has been modified. So maybe:

           // save these before modifying videoEvent
           double oldRate = videoEvent.PlaybackRate;
           Timecode oldEnd = videoEvent.End;

           ... // (not illustrated) now change playback rate and length of videoEvent

           // find and modify grouped events that are coincident with the original videoEvent
           if (videoEvent.IsGrouped) {
                foreach (TrackEvent ge in videoEvent.Group) {
                    if (ge == videoEvent)
                        continue;
                    // see if start and end points are the same
                    if (videoEvent.Start == ge.Start && oldRate == ge.PlaybackRate && oldEnd == ge.End) {
                            ... // (not illustrated) any other tests to determine whether ge should be modified
                            ... // (not illustrated) now change the rate and length of ge
                    }

Also, I am wondering how the timeline gets rippled when an event length gets changed.

Another problem is if there are already keyframes in the events, they will be desynchronized. That's a can of worms.

Julian-M wrote on 9/25/2024, 12:33 PM

@Julian-M The mistake here is:

This is a very simple change, you just need to add the old rate into your calculation. For convenience, the order of rate and length changes is swapped.

Woohoo!!! 🌈🎆 Thank you so much, @zzzzzz9125, you saved my situation again!!! 😇

This script is now perfect!))👌

https://www.vegascreativesoftware.info/us/forum/continue-to-stretch-playback-rate-if-it-s-already-stretched--147122/

Thank you very much, Sir @jetdv, I learned several useful things regarding my questions from this post thanks to your comments! 🙏💡

With the help of GPT chat, I made another version of the script - "silent" 🤫, without a pop-up window, in which the coefficient is set in the code itself, for faster response 🔢.

Also, thanks to the explanations from the video "Accessing the Video and Audio Event Properties in Vegas Pro" by Sir JETDV 🎥📚, I was able to explain to GPT chat how to add the assignment of the ElastiqueStretchAttributes.Soloist_Speech attribute to the audio events in the code 🎤.

If suddenly you do not need it, here are the lines that need to be cut:
36-42 for silent script version
46-52 for popup script version

🔽You can download both versions from the folder at this link!

https://drive.google.com/drive/folders/1pY_FUMBJdgfHJJjX70eLtu1sKNlWGeIo?usp=sharing

Julian-M wrote on 9/25/2024, 12:52 PM

Also, I am wondering how the timeline gets rippled when an event length gets changed.

Another problem is if there are already keyframes in the events, they will be desynchronized. That's a can of worms.

It seems you are right, @bvideo, the question here is quite complex, if we take into account all possible options 🤔.

One other thing is that it may be easier to find grouped events by iterating the events in the group.

I also don't really like the idea of ​​doing processing by groups ♒️.
I think it would be more reliable to do it for each event separately, if possible 💁🏻‍♂️.

To account for a previous rate not equal to 1.00, how about: ...

Also, it could happen that the new length violates frame boundaries, so if that's important, add: ...

Also, the end of a grouped event might no longer match the end of the original event that has been modified. So maybe: ...

Thank you very much for taking the time to write this code to improve this script, as well as for the suggestion to improve the length calculation and avoid border violations! 💯🔥👏
I am sure that with your additions the script will become even more reliable and will take into account more complex moments! 💪
I will definitely check it out! ✔️