def align(
            self,
            audio: Union[str, np.ndarray, torch.Tensor, bytes, AudioLoader],
            text: Union[str, List[int], WhisperResult],
            **options
    ) -> Union[WhisperResult, None]:
        """
        Align plain text or tokens with audio at word-level.

        Parameters
        ----------
        audio : str or numpy.ndarray or torch.Tensor or bytes or AudioLoader
            Path/URL to the audio file, the audio waveform, or bytes of audio file or
            instance of :class:`stable_whisper.audio.AudioLoader`.
            If audio is :class:`numpy.ndarray` or :class:`torch.Tensor`, the audio must be already at sampled to 16kHz.
        text : str or list of int or stable_whisper.result.WhisperResult
            String of plain-text, list of tokens, or instance of :class:`stable_whisper.result.WhisperResult`.

        Returns
        -------
        stable_whisper.result.WhisperResult or None
            All timestamps, words, probabilities, and other data from the alignment of ``audio``.
            Return None if alignment fails and ``remove_instant_words = True``.

        """
        self._reset()
        self._load_text(text)
        self._load_audio(audio)
        self._load_nonspeech_detector()
        for k in list(options.keys()):
            if hasattr(self, k):
                setattr(self, k, options.pop(k))
        self.options.update(options)

        with tqdm(
                total=self._initial_duration,
                unit='sec',
                disable=self.options.progress.verbose is not False,
                desc='Align'
        ) as tqdm_pbar:
            result: List[BasicWordTiming] = []
            last_ts = 0.0
            while self._all_word_tokens:

                self._time_offset = self._seek_sample / self.sample_rate
                audio_segment = self.audio_loader.next_chunk(self._seek_sample, self.n_samples)
                if audio_segment is None:
                    break

                self._nonspeech_preds = self.nonspeech_predictor.predict(audio=audio_segment, offset=self._time_offset)

                audio_segment = self._skip_nonspeech(audio_segment)
                if audio_segment is None:
                    continue

                self._curr_words = self._compute_timestamps(audio_segment, *self._get_curr_words())
                self._seg_word_tokens = [WordToken(wts.word, wts.tokens) for wts in self._curr_words]

                last_ts = self._fallback(audio_segment.shape[-1])

                self._update_pbar(tqdm_pbar, last_ts)

                result.extend(self._curr_words)

                if self.options.progress.verbose:
                    line = '\n'.join(
                        f"[{format_timestamp(wts.start)}] -> "
                        f"[{format_timestamp(wts.end)}] \"{wts.word}\""
                        for wts in self._curr_words
                    )
                    safe_print(line)

                if self.failure_threshold is not None:
                    self.failure_count += sum(1 for wts in self._curr_words if wts.end - wts.start == 0)
                    if self.failure_count > self.max_fail:
                        break

            self._update_pbar(tqdm_pbar, last_ts, self.failure_count <= self.max_fail)

        if self._temp_data.word is not None:
            result.append(self._temp_data.word)
        if not result:
            warnings.warn('Failed to align text.', stacklevel=2)
        if self.failure_count > self.max_fail:
            warnings.warn(
                f'Alignment aborted. Failed word percentage exceeded {self.failure_threshold * 100}% at '
                f'{format_timestamp(self._seek_sample / self.sample_rate)}.',
                stacklevel=2
            )
        elif self._all_word_tokens:
            last_ts_str = format_timestamp(result[-1].end if result else 0)
            warnings.warn(
                f'Failed to align the last {len(self._all_word_tokens)}/{self._total_words} words after '
                f'{last_ts_str}.',
                stacklevel=2
            )

        if self._all_word_tokens and not self.remove_instant_words:
            final_total_duration = self.audio_loader.get_duration(3)
            result.extend(
                [
                    BasicWordTiming(
                        word=w.word,
                        start=final_total_duration,
                        end=final_total_duration,
                        tokens=w.tokens,
                        probability=0.0
                    )
                    for w in self._all_word_tokens
                ]
            )

        self.audio_loader.terminate()
        self.nonspeech_predictor.finalize_timings()

        if not result:
            return

        final_result = [
            dict(word=w.word, start=w.start, end=w.end, tokens=w.tokens, probability=w.probability)
            for w in result
        ]
        if len(self._split_indices_by_char):
            word_lens = np.cumsum([len(w.word) for w in result])
            split_indices = [np.flatnonzero(word_lens >= i)[0] + 1 for i in self._split_indices_by_char]
            final_result = WhisperResult(
                [
                    final_result[i:j]
                    for i, j in zip([0] + split_indices[:-1], split_indices)
                    if i != j]
            )
        else:
            final_result = WhisperResult([final_result])

        self._suppress_silence(final_result)

        if not self.original_split:
            final_result.regroup(self.options.post.regroup)

        if fail_segs := len([None for s in final_result.segments if s.end - s.start <= 0]):
            warnings.warn(f'{fail_segs}/{len(final_result.segments)} segments failed to align.', stacklevel=2)

        return final_result

        Align plain text or tokens with audio at word-level.

        Parameters
        ----------
        audio : str or numpy.ndarray or torch.Tensor or bytes or AudioLoader
            Path/URL to the audio file, the audio waveform, or bytes of audio file or
            instance of :class:`stable_whisper.audio.AudioLoader`.
            If audio is :class:`numpy.ndarray` or :class:`torch.Tensor`, the audio must be already at sampled to 16kHz.
        text : str or list of int or stable_whisper.result.WhisperResult
            String of plain-text, list of tokens, or instance of :class:`stable_whisper.result.WhisperResult`.

        Returns
        -------
        stable_whisper.result.WhisperResult or None
            All timestamps, words, probabilities, and other data from the alignment of ``audio``.
            Return None if alignment fails and ``remove_instant_words = True``.

        
stable_whisper/non_whisper/alignment.py