code: def make_token_level_ass_file(
      utt_obj, output_dir_root, ass_file_config, audio_dur
  ):
      default_style_dict = {
          "Name": "Default",
          "Fontname": "Arial",
          "Fontsize": str(ass_file_config.fontsize),
          "PrimaryColour": "&Hffffff",
          "SecondaryColour": "&Hffffff",
          "OutlineColour": "&H0",
          "BackColour": "&H0",
          "Bold": "0",
          "Italic": "0",
          "Underline": "0",
          "StrikeOut": "0",
          "ScaleX": "100",
          "ScaleY": "100",
          "Spacing": "0",
          "Angle": "0",
          "BorderStyle": "1",
          "Outline": "1",
          "Shadow": "0",
          "Alignment": None,  # will specify below
          "MarginL": str(MARGINL),
          "MarginR": str(MARGINR),
          "MarginV": str(MARGINV),
          "Encoding": "0",
      }
  
      if ass_file_config.vertical_alignment == "top":
          default_style_dict[
              "Alignment"
          ] = "8"  # text will be 'center-justified' and in the top of the screen
      elif ass_file_config.vertical_alignment == "center":
          default_style_dict[
              "Alignment"
          ] = "5"  # text will be 'center-justified' and in the middle of the screen
      elif ass_file_config.vertical_alignment == "bottom":
          default_style_dict[
              "Alignment"
          ] = "2"  # text will be 'center-justified' and in the bottom of the screen
      else:
          raise ValueError(
              f"got an unexpected value for ass_file_config.vertical_alignment"
          )
  
      output_dir = os.path.join(output_dir_root, "ass", "tokens")
      os.makedirs(output_dir, exist_ok=True)
      output_file = os.path.join(output_dir, f"{utt_obj.utt_id}.ass")
  
      already_spoken_color_code = (
          r"{\c&H"
          + rgb_list_to_hex_bgr(ass_file_config.text_already_spoken_rgb)
          + r"&}"
      )
      being_spoken_color_code = (
          r"{\c&H"
          + rgb_list_to_hex_bgr(ass_file_config.text_being_spoken_rgb)
          + r"&}"
      )
      not_yet_spoken_color_code = (
          r"{\c&H"
          + rgb_list_to_hex_bgr(ass_file_config.text_not_yet_spoken_rgb)
          + r"&}"
      )
  
      with open(output_file, "w") as f:
          default_style_top_line = "Format: " + ", ".join(
              default_style_dict.keys()
          )
          default_style_bottom_line = "Style: " + ",".join(
              default_style_dict.values()
          )
  
          f.write(
              (
                  "[Script Info]\n"
                  "ScriptType: v4.00+\n"
                  f"PlayResX: {PLAYERRESX}\n"
                  f"PlayResY: {PLAYERRESY}\n"
                  "ScaledBorderAndShadow: yes\n"
                  "\n"
                  "[V4+ Styles]\n"
                  f"{default_style_top_line}\n"
                  f"{default_style_bottom_line}\n"
                  "\n"
                  "[Events]\n"
                  "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n\n"
              )
          )
  
          # write first set of subtitles for text before speech starts to be spoken
          tokens_in_first_segment = []
          for segment_or_token in utt_obj.segments_and_tokens:
              if type(segment_or_token) is Segment:
                  for (
                      word_or_token
                  ) in segment_or_token.words_and_tokens:
                      if type(word_or_token) is Token:
                          if word_or_token.text != BLANK_TOKEN:
                              tokens_in_first_segment.append(
                                  word_or_token
                              )
                      else:
                          for token in word_or_token.tokens:
                              if token.text != BLANK_TOKEN:
                                  tokens_in_first_segment.append(token)
  
                  break
  
          for token in tokens_in_first_segment:
              token.text_cased = token.text_cased.replace(
                  "▁", " "
              )  # replace underscores used in subword tokens with spaces
              token.text_cased = token.text_cased.replace(
                  SPACE_TOKEN, " "
              )  # space token with actual space
  
          text_before_speech = (
              not_yet_spoken_color_code
              + "".join([x.text_cased for x in tokens_in_first_segment])
              + r"{\r}"
          )
          subtitle_text = (
              f"Dialogue: 0,{seconds_to_ass_format(0)},{seconds_to_ass_format(tokens_in_first_segment[0].t_start)},Default,,0,0,0,,"
              + text_before_speech.rstrip()
          )
  
          f.write(subtitle_text + "\n")
  
          for segment_or_token in utt_obj.segments_and_tokens:
              if type(segment_or_token) is Segment:
                  segment = segment_or_token
  
                  tokens_in_segment = (
                      []
                  )  # make list of (non-blank) tokens
                  for word_or_token in segment.words_and_tokens:
                      if type(word_or_token) is Token:
                          if word_or_token.text != BLANK_TOKEN:
                              tokens_in_segment.append(word_or_token)
                      else:
                          for token in word_or_token.tokens:
                              if token.text != BLANK_TOKEN:
                                  tokens_in_segment.append(token)
  
                  for token in tokens_in_segment:
                      token.text_cased = token.text_cased.replace(
                          "▁", " "
                      )  # replace underscores used in subword tokens with spaces
                      token.text_cased = token.text_cased.replace(
                          SPACE_TOKEN, " "
                      )  # space token with actual space
  
                  for token_i, token in enumerate(tokens_in_segment):
                      text_before = "".join(
                          [
                              x.text_cased
                              for x in tokens_in_segment[:token_i]
                          ]
                      )
                      text_before = (
                          already_spoken_color_code
                          + text_before
                          + r"{\r}"
                      )
  
                      if token_i < len(tokens_in_segment) - 1:
                          text_after = "".join(
                              [
                                  x.text_cased
                                  for x in tokens_in_segment[
                                      token_i + 1 :
                                  ]
                              ]
                          )
                      else:
                          text_after = ""
                      text_after = (
                          not_yet_spoken_color_code
                          + text_after
                          + r"{\r}"
                      )
  
                      aligned_text = (
                          being_spoken_color_code
                          + token.text_cased
                          + r"{\r}"
                      )
                      aligned_text_off = (
                          already_spoken_color_code
                          + token.text_cased
                          + r"{\r}"
                      )
  
                      subtitle_text = (
                          f"Dialogue: 0,{seconds_to_ass_format(token.t_start)},{seconds_to_ass_format(token.t_end)},Default,,0,0,0,,"
                          + text_before
                          + aligned_text
                          + text_after.rstrip()
                      )
                      f.write(subtitle_text + "\n")
  
                      # add subtitles without word-highlighting for when words are not being spoken
                      if token_i < len(tokens_in_segment) - 1:
                          last_token_end = float(
                              tokens_in_segment[token_i].t_end
                          )
                          next_token_start = float(
                              tokens_in_segment[token_i + 1].t_start
                          )
                          if next_token_start - last_token_end > 0.001:
                              subtitle_text = (
                                  f"Dialogue: 0,{seconds_to_ass_format(last_token_end)},{seconds_to_ass_format(next_token_start)},Default,,0,0,0,,"
                                  + text_before
                                  + aligned_text_off
                                  + text_after.rstrip()
                              )
                              f.write(subtitle_text + "\n")
  
          # Write final set of subtitles for text after speech has been spoken.
          # To do this, we need to collect 'tokens_in_final_segment' so that we know what the final line is.
          tokens_in_final_segment = []
          for segment_or_token in utt_obj.segments_and_tokens[::-1]:
              # Collect tokens from final segment - will 'break' so we only look at the final one.
              if type(segment_or_token) is Segment:
                  # 'segment_or_token' is known to be Segment, which has attribute 'words_and_tokens'
                  for (
                      word_or_token
                  ) in segment_or_token.words_and_tokens:
                      if type(word_or_token) is Token:
                          if word_or_token.text != BLANK_TOKEN:
                              tokens_in_final_segment.append(
                                  word_or_token
                              )
                      else:
                          # 'word_or_token' is known to be a Word, which has attribute 'tokens'
                          for token in word_or_token.tokens:
                              if token.text != BLANK_TOKEN:
                                  tokens_in_final_segment.append(token)
                  break
  
          for token in tokens_in_final_segment:
              token.text_cased = token.text_cased.replace(
                  "▁", " "
              )  # replace underscores used in subword tokens with spaces
              token.text_cased = token.text_cased.replace(
                  SPACE_TOKEN, " "
              )  # space token with actual space
  
          text_after_speech = (
              already_spoken_color_code
              + "".join([x.text_cased for x in tokens_in_final_segment])
              + r"{\r}"
          )
          # note: for now doing some extra padding with math.ceil(audio_dur)+1) to account for the fact that the video with subtitles can become
          # longer than the original audio during the MP4 creation stage.
          subtitle_text = (
              f"Dialogue: 0,{seconds_to_ass_format(tokens_in_final_segment[-1].t_end)},{seconds_to_ass_format(math.ceil(audio_dur)+1)},Default,,0,0,0,,"
              + text_after_speech.rstrip()
          )
  
          f.write(subtitle_text + "\n")
  
      utt_obj.saved_output_files[
          f"tokens_level_ass_filepath"
      ] = output_file
  
      return utt_obj
  
file_path: NeMo/tools/nemo_forced_aligner/utils/make_ass_files.py
imports: import math
  import os
  import soundfile
  from utils.constants import BLANK_TOKEN, SPACE_TOKEN
  from utils.data_prep import Segment, Token, Word
label_desc: Use image node_id 8 for calling a global function with example usage: make_token_level_ass_file(utt_obj, output_dir_root, ass_file_config, audio_dur) and returns: utt_obj