Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Set playable duration in code

Discussion in 'Timeline' started by Mars91, Jul 13, 2017.

  1. Mars91

    Mars91

    Joined:
    Mar 6, 2012
    Posts:
    572
    Hi,
    I'm working on a dialog/subtitle playable and everything is working like a charm, my only problem is that I can't figure out how to set the playable duration in the code.
    I have an AudioClip and I would like to set playable duration the same of AudioClip lenght.
     
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    This is a known issue, and something we want to address. You can override the duration property in your PlayableAsset representing your clip, and set it to the the audio clip length.

    As an example, here is how we do the duration for the audio clip track (the extra math keeps better frame alignment):

    Code (CSharp):
    1.         public override double duration
    2.         {
    3.             get
    4.             {
    5.                 if (m_Clip == null)
    6.                     return base.duration;
    7.  
    8.                 // use this instead of length to avoid rounding precision errors,
    9.                 return (double)m_Clip.samples / (double)m_Clip.frequency;
    10.             }
    11.         }

    The problem is typically you create the clip prior to assigning the audio clip, so you need to pick a default when nothing is assigned. Once the audio clip is assigned, if you right click on the clip and go to Editing -> Reset Editing it will change the length to the audio clip in that case.

    Some of the customization improvements on our backlog is to better support drag and drop for custom types. That would allow drag and drop of an audio clip to your custom track to assign the audio clip before determining a default length.
     
    Jamez0r and Thomas-Bousquet like this.
  3. Mars91

    Mars91

    Joined:
    Mar 6, 2012
    Posts:
    572
    Hi and thank you so much for your answer. In which one of the various scripts which compose the playable should I write that code?
     
  4. renaudbedard_SEM

    renaudbedard_SEM

    Joined:
    Sep 29, 2016
    Posts:
    4
    Is there any API to invoke this "Reset Editing" functionality by code? Would be great to auto-size clips that are generated by code.
     
  5. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    Reset Editing doesn't have an API call, but if you create the clip via code, you should be able to something like

    Code (CSharp):
    1. var clip = track.CreateDefaultClip();
    2. clip.duration = (double) audioClip.sample / audioClip.frequency;
     
  6. renaudbedard_SEM

    renaudbedard_SEM

    Joined:
    Sep 29, 2016
    Posts:
    4
    That totally works, thanks! :)
     
  7. WikkidEdd1

    WikkidEdd1

    Joined:
    Nov 17, 2015
    Posts:
    10
    If you want the duration to update when you're changing the audio clip you can do it by creating a custom property drawer for your behaviour and implementing the following code.

    Taking @FootprintGames's example of dialog/subtitle track assuming you have a clip with a behaviour "template" similar to created by the playables wizard and an ExposedReference<AudioClip> variable on the clip.

    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomPropertyDrawer(typeof(SubtitleBehaviour))]
    5. public class SubtitleBehaviourDrawer : PropertyDrawer
    6. {
    7.     public override float GetPropertyHeight (SerializedProperty property, GUIContent label)
    8.     {
    9.         return EditorGUI.GetPropertyHeight(property, true);
    10.     }
    11.  
    12.     public override void OnGUI (Rect position, SerializedProperty property, GUIContent label)
    13.     {
    14.         // Draw the behaviours properties
    15.         EditorGUI.PropertyField(position, property, true);
    16.  
    17.         // Find the clip this behaviour is associated with a grab the audio clip
    18.         SubtitleClip clip = property.serializedObject.targetObject as SubtitleClip;
    19.         AudioClip audioClip = clip._Audio.Resolve(property.serializedObject.context as IExposedPropertyTable);
    20.         if(audioClip)
    21.         {
    22.             // Assume that the currently selected object is the internal class UnityEditor.Timeline.EditorClip
    23.             // this gives you access to the clip start, duration etc.
    24.             SerializedObject editorGUI = new SerializedObject(Selection.activeObject);
    25.  
    26.             // Grab the duration, set and apply modified properties
    27.             SerializedProperty duration = editorGUI.FindProperty("m_Clip.m_Duration");
    28.             duration.doubleValue = (double) audioClip.samples / audioClip.frequency;
    29.             editorGUI.ApplyModifiedProperties();
    30.         }
    31.     }
    32. }
    33.  
    You can then change the audio clip and see the clip duration update.
     
    KristianHJ likes this.
  8. KristianHJ

    KristianHJ

    Joined:
    May 28, 2010
    Posts:
    350
    Thanks, this helped me a bit closer to finding a solution my (unrelated) problem.
    I'm trying to select specific UnityEditor.Timeline.EditorClip's in a timeline from another script.
    Basically what I want to do is to set Selection.activeObject = *Some Editorclip*.

    My problem is that I cant figure out how to locate the references to editorclips, do you know where I can find the editorclips relative to a specific Timeline track?

    Any bright ideas?
     
  9. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    How would I go about getting the bound GO from the track so I could set the duration based on its data?
     
  10. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    The GO passed to CreatePlayable or CreateTrackMixer is the game object that contains the playing playable director. From the playable director you can use GetGenericBinding, passing the track as the key, to get the binding for that track. You will need to cast it to your specific type.
     
  11. Crazy-Minnow-Studio

    Crazy-Minnow-Studio

    Joined:
    Mar 22, 2014
    Posts:
    1,398
    Note: FindProperty root is "m_Item" vice "m_Clip" in Unity 2017.2+.
     
    SeigoSato likes this.
  12. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Has this gotten any easier over the last couple years?

    I have a GO in the editor scene that has a custom MB on it. That MB has frame data including its duration. When creating a CLIP from this Object in Timeline, artists want the clip to be auto set to the correct duration of the underlying object but I'm getting super confused trying to unravel all these pieces involved.

    I know I posted 2 years ago asking how to get the bound object from the tack, but what I really need is to get the bound object from the clip. I think?

    I need the clip to be created with the proper duration set from the data in the bound object.
     
    Last edited: Nov 14, 2019
  13. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    1,516
    If you are using 2019.2, you can make a clip editor for your type. It gets a callback on create, so you can set a default duration for your clip.

    Code (CSharp):
    1. [CustomTimelineEditor(typeof(MyClip))]
    2. public class MyClipEditor : ClipEditor
    3. {
    4.     public override void OnCreate(TimelineClip clip, TrackAsset track, TimelineClip clonedFrom)
    5.     {
    6.         var myClip = (MyClip) clip.asset;
    7.         var obj = TimelineEditor.inspectedDirector.GetReferenceValue(myClip.exposedProperty.exposedName, out _) as MyBindingType;
    8.         if (obj != null)
    9.             myClip.defaultDuration = obj.duration;
    10.            
    11.         base.OnCreate(clip, track, clonedFrom);
    12.     }
    13. }
    14.    
    15. public class MyClip : PlayableAsset
    16. {
    17.     public ExposedReference<MyBindingType> exposedProperty;
    18.  
    19.     public double defaultDuration = 5;
    20.    
    21.     // this is used as the default duration of the clip when created
    22.     public override double duration { get { return defaultDuration; } }
    23. }
    It sets the default duration instead of just the clip duration, so actions like Reset to default length work.
     
  14. jwvanderbeck

    jwvanderbeck

    Joined:
    Dec 4, 2014
    Posts:
    825
    Wow that looks a LOT easier than in the past. Thanks! I'll tackle this today but looks simple enough with that new ClipEditor class.
     
  15. Threeyes

    Threeyes

    Joined:
    Jun 19, 2014
    Posts:
    80
    Another question, if we change the Clip duration in Inspector, how can we receive this message from Code? I had try TrackAsset.UpdateDuration and it should work, but it get invoked many times and I can't get ExposedReference data inside UpdateDuration.
     
    Last edited: Mar 23, 2021
  16. DavidGeoffroy

    DavidGeoffroy

    Unity Technologies

    Joined:
    Sep 9, 2014
    Posts:
    542
    If it's your own track type, you should put the code in the inspector and use this API to check for changes.

    But if you want to do this for a built-in track, it might be a lot harder. Timeline doesn't typically react to clip content changes except for start time, duration and blends. It only reacts to the content changes to recreate the graph.

    Since what you are authoring are timings, it's generally a bad idea to automatically change the timing of a clip when the contents change, as it will throw off all your edits.

    What are you attempting to react to?
     
  17. AdamBebkoSL

    AdamBebkoSL

    Joined:
    Dec 9, 2021
    Posts:
    33
    @DavidGeoffroy ClipEditor doesn't seem to have a serializedObject property... how do we get the serialized object to use? Sorry if I'm missing something obvious.