Introduction

 Create dialogues, subtitles, karaoke, interactive books, or any other text effects synchronized with playing audio using this asset!

Requirements

 For the correct work, «Audio-Text Synchronizer» requires:

  • Audio file for synchronization of text and audio;
  • Text for the audio clip.
  • How do I upgrade?

     First, back up your project.
    Next, update the asset to the latest version from Asset Store using Package Manager (Windows -> Package Manager).
    The asset has a comparison list of APIs with changes. To check it, open the v.2.0_API_Changes.pdf file.
    Also, check out this section: Upgrading from version 1.x to 2.x.
    If you have any issues or errors, contact me. I'll be happy to help!

    Quick Start

     «Audio-Text Synchronizer» contains two parts:

    • Editor Window for creating timings. Timing is a time segment on the audio timeline, which contains the text. They allow getting high accuracy of synchronization of audio and text. Timings also can be generated from subtitles in .srt format;
    • Runtime part: to synchronize text with audio, you need to add one component and set up a few parameters.

    Timings Editor

     For text and audio synchronization, you need to create timings of AudioClip, add the TextSynchronizer component, and setup its parameters.
     To create timings user can open Timings Editor from «Window -> Timings Editor» or create PhraseAsset using menu Assets/Audio Text Synchronizer/Audio Timings and open it using double-click:
    Create Audio Timings
    Timings Editor
    You can choose the AudioClip from the window using the AudioClip field: Audio Clip
    After that Timeline of the current AudioClip is displayed: Audio Clip
     A red vertical line on the left of the timeline is a cursor of the current position of playing audio. By double-clicking on the timeline user can move the cursor. At the bottom of the cursor is the current audio time position. Let's look at the elements of the window:
    Timings Editor items

  • Pointer 1 contains buttons - New, Load, Save and Validate. Use them to create new timings, load existing or generate timings from subtitles (.srt) files, save current timings or validate them;
  • Pointer 2 contains information about zooming of the timeline and the auto-save feature. You can zoom the timeline using a mouse scroll-wheel or touchpad. For zoom-in, scroll up. For zoom out, scroll down. When checkbox «Auto Save» is on, saved timings data will be saved automatically in case of timings were saved to the file;
  • Pointer 3 contains buttons - Add Timing, Remove Timing, Play/Pause and Play Selected Timing:
    • Add Timing button adds new timing into the current position of the cursor on the timeline;
    • Remove Timing button remove selected timings;
    • Play/Pause button plays AudioClip in the current position of the cursor on the timeline;
    • Play Selected Timing button plays a segment of audio containing current timing.
  • Pointer 4 contains a text field. In this text should be all text of AudioClip;
  • Pointer 5 contains the AudioClip timeline.
  •  Let’s add timings to the timeline. To add timing, click the Add Timing button or the right mouse button and choose the item «Insert new timing». Timing will be added to the current cursor position on the timeline:
    New timing
     New timing will be added and selected. At the bottom of the window, you can find information about new timing. This box contains information about «Timing name», «Start» position of timing, «End» position of timing, «Duration» of timing, «Color» of timing, and «Timing Text» - text, which timing contains.
     Now let’s add text to timings and move them to the correct positions. To change the timing duration, drag-n-drop timing left and right borders.

    Use mouse scroll to zoom in/out the timeline.
    Hold the «Shift» key and drag-n-drop its borders to move timing faster.
    Hold the «Alt» key and drag-n-drop its borders to get better precision and move timing slower.

    For example, let's resize this timing in that way: start position at 0.0052 sec, end position at 0.0842 sec, and add the text «A squirrel»:

    To make sure that it’s correct, press the button «Play Selected Timing» to play the AudioClip segment:
    Play selected timing
    In this way, add timings for all AudioClip timeline. It is also possible to choose a Color and Name of timings for convenience. Note that setting up the Color and Name is unnecessary and displays only in Timings Editor. To select many timings and move them, use the selection rectangle by mouse: Timings added
    Also, Timings should contain the main text at the bottom of the Window - it is the whole text of AudioClip: Timings text
    After finishing work with timings, timings can be saved in the file using the button Save. Timings Editor will ask to choose a filename and path, where the timings asset will be saved: Timings saving
     For loading timings from the file, double-click with the left mouse button on the timings asset (PhraseAsset), or select the timings asset and press Open in Timings Editor button: PhraseAsset
    To resize timeline height, you can drag-n-drop slider: Timings Editor size
    Saved Timings can be displayed in the Inspector Window. You can edit it in Inspector instead of Timings Editor: PhraseAsset in Inspector Window
     Timings can be separated by word or characters. User can divide audio on timing as necessary. For better synchronization accuracy, add the appropriate number of timings. It is recommended to add timings to phrases or words, depending on AudioClip.

    Timings Validation

     Timings Validation - it’s functional that helps to validate timings text corresponding main text of the audio clip. After validation, it reports if there are problems with timings to the «Console» window. To use it, fill timings with corresponding text and press Validate button in the left top corner of the «Timings Editor» window: Timings validation
    Timings validation result
    From the message, we can see that concatenation of all timings text doesn't match with all timings text and the second timing has less length than expected. Now we can fix the text of the second timing and validate it to be sure that it is correct: Fixed timing

    TextSynchronizer

    TextSynchronizer is the main runtime script of the asset. TextSynchronizer component gets the current position of AudioSource playing, finds the appropriate timing, and sends the data (current timing, percent of finish of current timing) to TextEffectBase class. It also requires GameObject with Text component and works using System.Reflection, which allows to use of custom Text components. So, you need to choose a GameObject, Component and Property name from the dropdown list and the Text Effect. To start working, add the component TextSynchronizer to some GameObject. TextSynchronizer component
    The next step is to choose a GameObject, Component, or Property of the text component. If your scene doesn't have a text component, create a new GameObject and add UI.Text or TextMesh Pro component:

    New text component TextSynchronizer component

    Note that if you use RTL (right to the left) text, you need to check "Enable RTL Editor" in the TextMeshPro component parameters.
    Then set AudioSource with AudioClip, Phrase Asset (timings), and add Text Effect: TextSynchronizer setup

    That's all, your TextSynchronizer component is ready. Note that AudioSource should play for synchronization.

    Text Effects

     Text Effect - is ScriptableObject that contains the logic for modifying text components during synchronization.
    Asset has two type of text effects: Rich and Mesh text effect.
    1. Rich Effect (Assets/AudioTextSynchronizer/TextEffects/Rich/RichTextEffect.asset) uses RichText that markups format for text highlighting. RichText can provide such features as changing text color, bolding text, resizing, etc. Rich Effect parameters:
    Rich Effect

    • Text Split Config - reference to TextSplitter ScriptableObject;
    • Text Highlight Config - reference to TextHighlighter ScriptableObject;
    • Start Tag - start tag, which is inserted into the text component;
    • End Tag - tag, which closes the StartTag;
    • Highlight Color - the color of highlighting the rich tag;
    • Char Index Offset - highlight position offset in characters count;
    • Effect Finished Action - an action that will be performed after effect finish sync: None, Clear Text, or Set Current Part Text.

    2. Mesh Effect (Assets/AudioTextSynchronizer/TextEffects/Mesh/MeshTextEffect.asset) - the effect that uses text component mesh data - position, UV, color, etc. Mesh Effect parameters:
    Mesh Effect

    • Text Split Config - reference to TextSplitter ScriptableObject;
    • Text Highlight Config - reference to TextHighlighter ScriptableObject;
    • Mesh Animation - a reference to the Mesh Animation config. Mesh Animation config it is a ScriptableObject that contains mesh animation logic of the text component. The asset has the following Mesh Animation configs:
      • AlphaColorMeshAnimation - change vertices color and alpha with specified speed. Parameters:
        • Speed - speed of animation;
        • Use Alpha - whether to use color alpha;
        • Alpha Curve - AnimationCurve of changing alpha ratio from 0 to 1;
        • Text Color - the color of vertices.
        AlphaColorMeshAnimation
      • AlphaCenterMeshAnimation - change vertices positions and alpha with specified speed. Parameters:
        • Speed - speed of animation;
        • Scale Ratio - the ratio of vertices positions while scaling;
        • Use Alpha - whether to use color alpha;
        • Alpha Curve - AnimationCurve of changing alpha ratio from 0 to 1.
        AlphaCenterMeshAnimation
      • AlphaCharScaleMeshAnimation - scale vertices positions from mesh center and change alpha with specified speed. Parameters:
        • Speed - speed of animation;
        • Use Alpha - whether to use color alpha;
        • Alpha Curve - AnimationCurve of changing alpha ratio from 0 to 1;
        • Vertices Curve - AnimationCurve of changing vertices positions ratio from 0 to 1.
        AlphaCharScaleMeshAnimation
    • Char Index Offset - current highlight position offset in characters count;
    • Animate Text Instantly - whether to animate the text part instantly. If unchecked, the text will be animated according to TextHighlighter config.
    • Text Part Finished Action - an action that will be performed after text part finish: Clear Text or Set Current Part Text.

     To create a custom effect, create a class that inherits from «TextEffectBase» class and create a ScriptableObject. See the «RichTextEffect» and «MeshTextEffect» classes to understand better how it works.

    Text Splitters

     Text Split Config (Text Splitter) is a ScriptableObject that contains the logic of text splitting by text parts. Using Text Splitter, you can choose how to display text: all timings text, per timing, or separated by strings.
    Asset has tree type of text splitters:

  • DefaultTextSplitter (Assets/AudioTextSynchronizer/TextSplitters/DefaultTextSplitter.asset) - shows all timings text (don't split the text).
  • StringTextSplitter (Assets/AudioTextSynchronizer/TextSplitters/StringTextSplitter.asset) - divides text using string list. Parameters:
    • Split Text By Strings - strings for text splitting. For example, if it contains an element with a dot (".") symbol, the text will be divided into sentences;
    • Trim Characters - the characters that will be used for trimming text parts;
    For example, Using separated by StringTextSplitter, you can display the text in sentences:

    StringTextSplitter
  • TimingsTextSplitter (Assets/AudioTextSynchronizer/TextSplitters/TimingsTextSplitter.asset) - shows current timing text.
  • Text Highlighters

     Text Highlight Config (Text Highlighter) is a ScriptableObject that contains the logic of text highlighting. For example, with Text Highlighter, you can choose how to highlight text: per each character, per timing, or separated by strings.

  • The asset has the following highlight configs:
    • DefaultHighlightConfig - highlight text per character:
      DefaultHighlightConfig
    • StringHighlightConfig - highlight text part that is divided by indents characters:
      StringHighlightConfig
    • TimingHighlightConfig - highlights text per timing:
      TimingHighlightConfig
  • API Help

    Timing

    Timing class contains timing data:
    public float StartPosition - start position on the timeline in seconds;
    public float EndPosition - end position on the timeline, in seconds;
    public string Name - the name of timings, used by Timings Editor;
    public string Text - the text which contains timing;
    public Rect Rectangle - rectangle, used by Timings Editor;
    public Color Color - the color of the current rectangle, used by Timings Editor;
    public float Size - duration of timing in seconds.

    PhraseAsset

    PhraseAsset class – it is ScriptableObject that contains timings data:
    public AudioClip Clip - AudioClip, which will be used to synchronize timings;
    public string Text - Text of AudioClip;
    public List<Timing> Timings - a list of Timings.

    TextSynchronizer

    TextSynchronizer component synchronizes text effect with AudioSource. Main public fields, properties, and methods:
    public GameObject GameObjectWithTextComponent { ... } - GameObject, for which effect will be applied, should contain text component;
    public Component TextComponent { ... } - text component, for which effect will be applied;
    public AudioSource Source { ... } - AudioSource to synchronize text effect with audio playing;
    public PhraseAsset Timings { ... } - reference to the asset with timings;
    public TextEffectBase TextEffect { ... } - reference to the TextEffect asset, which be applied to text component;
    public bool IsRunning { ... } - whether to sync is active;
    public event Action OnSyncFinished - event that will be invoked after the synchronization is finished;
    public event Action <string, int> OnWordReached - event that will be invoked when word reached while synchronization, second argument - index (position) of the word in the text;
    public void Play(bool initializeEffect = false) - run synchronization, optional argument - whether to initialize the effect;
    public void Pause() - pause synchronization;
    public void Stop(bool resetCurrentTiming = true) - stop synchronization, optional argument - whether to reset current timing;
    public void SkipPhrase() - skips current timing/text part;
    public void SplitWords() - get all words from phrases asset main text;
    public int GetCharProgress() - return index of text before current timing text;
    public int GetCharProgress(char[] trim) - return index of text before trimmed current timing text.

    TextEffectBase

    TextEffectBase base class of the text effect that interacts with text component while AudioSource is playing. Using this base class, you can create new text effects;
    Main public fields, properties, and methods:
    public event Action<Timing> OnTimingEnter - event, invokes when audio is playing, and TextSynchronizer sets the new timing (previous timing finished);
    public event Action<Timing> OnTimingStart - event, invokes when audio is playing and TextSynchronizer the new timing starts playing;
    public event Action<Timing, float> OnTimingProgress - event, invokes when audio is playing, and timing is in sync, second argument - progress of current timing from 0 to 1;
    public event Action<Timing> OnTimingEnd - event, invokes when audio playing of current timing is finished;
    protected TextSynchronizer TextSynchronizer - reference to TextSynchronizer component;
    public virtual void Init(TextSynchronizer textSynchronizer) - initialize the effect, argument - TextSynchronizer instance;
    public virtual void OnTimingEntered(Timing timing) - invoke when audio is playing, and TextSynchronizer sets the new timing (previous timing finished);
    public virtual void OnTimingStarted(Timing timing) - invoke, when audio is playing and TextSynchronizer the new timing starts playing;
    public virtual void OnTimingMoving(Timing timing, float progress) - invoke when audio is playing, and timing is in sync, second argument - progress of current timing from 0 to 1;
    public virtual void OnTimingFinished(Timing timing) - invoke, when audio playing of current timing is finished;
    public virtual void OnEffectFinished() - invoked when the effect is finished;
    protected virtual void SetTextToComponent(string text) - sets text to text component;
    protected virtual void SkipPart() - skip current timing/text part.

    MeshTextEffect

    MeshTextEffect - the effect that uses text component mesh data - position, UV, color, etc. It is used to change the mesh of UI.Text and TextMeshPro components;
    Main public fields, properties, and methods:
    public MeshAnimationBase MeshAnimation - reference to the MeshAnimation;
    public int CharIndexOffset - highlight position offset in characters count;
    public bool AnimateTextInstantly - whether to animate the text part instantly. If unchecked, the text will be animated according to the TextHighlighter config;
    public OnTextPartAction TextPartFinishedAction - an action that will be performed after text part finish: Clear Text or Set Current Part Text.

    RichTextEffect

    TextRichEffectBase class sends data for effects while AudioSource is playing. It is used for rich text effects, which are supported by many assets with custom text components;
    Main public fields, properties, and methods:
    public string StartTag - tag, which inserted to text component;
    public string EndTag - tag, which closes the StartTag;
    public Color32 HighlightColor - highlighting color;
    public int CharIndexOffset - highlight position offset in characters count;
    public OnTextAction EffectFinishedAction - an action that will be performed after effect finish sync: None, Clear Text, or Set Current Part Text.

    Frequently used methods

    Pause synchronization: [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector ... textSynchronizer.Pause();
    Play/Resume synchronization:
    [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector ... textSynchronizer.Play();
    Stop synchronization:
    [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector ... textSynchronizer.Stop();
    Change timings in runtime:
    [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector [SerializeField] private PhraseAsset timings; //assign reference to the PhraseAsset asset in the Inspector ... textSynchronizer.Timings = timings;
    Subscribe to the synchronization complete event:
    [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector ... textSynchronizer.OnSyncFinished += OnSyncFinished; ... private void OnSyncFinished() { Debug.Log("Synchronization finished!"); textSynchronizer.OnSyncFinished -= OnSyncFinished; }
    Subscribe to the synchronization word reached event:
    [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector ... textSynchronizer.OnWordReached += OnWordReached; ... private void OnWordReached(string word, int position) { Debug.Log($"Reached word: {word} at position {position}"); if (word == "SomeWord") { Debug.Log("SomeWord reached!"); textSynchronizer.OnWordReached -= OnWordReached; } }
    Subscribe to the text effect progress event:
    [SerializeField] private TextSynchronizer textSynchronizer; //assign reference to the TextSynchronizer component in the Inspector ... textSynchronizer.TextEffect.OnTimingProgress += OnTimingProgress; ... private void OnTimingProgress(Timing timing, float progress) { Debug.Log($"Timing text: {timing.Text}, progress: {progress}"); }

    Upgrading from version 1.x to 2.x

     Starting from version 2.x, Text Effects were rewritten from scratch and isn't compatible with effects from version 1.x. If you update an asset in the project that already has 1.x version, please do the following steps to make the asset work properly:

    • Make sure that you back up your project;
    • Remove the Assets/Audio-Text Synchronizer/ folder from your project;
    • Re-import the latest version from the Asset Store using Package Manager (Windows -> Package Manager);
    • Set up the TextSynchronizer component. You can refer to the TextSynchronizer paragraph for a more detailed explanation of how to do that.

    Contacts

    Please let me know if you have any questions, ideas, or suggestions.
    E-mail: unitymedved@gmail.com