/// <summary>
        /// Retrive the voiceover value paired to the crc ID from the scene dictionary and try to play the voiceover and set the subtitle text.
        /// </summary>
        /// <returns> returns true if the voiceover is set to be played successfully, false otherwise.
        /// <param name="key"> the voiceover id as encoded CRC Int. </param>
        /// <param name="stopCurrentIfNecessary"> - If OnVoiceoverContentChanged() is called when there is already an active subtitle content in display:
        ///                                           - setting this param to true will interrupt and stop the current vo/subtitle immediately and play the new vo;
        ///                                           - setting this param to false will not interrupt the current vocieover; the new vo will not get played and
        ///                                             the function returns false.
        /// </param>
        public bool OnVoiceoverContentChanged(VoiceoverID key, bool stopCurrentIfNecessary)
        {
            // missing json file
            if (missingFileErr)
            {
                DisplayErrorMsg();
                return(false);
            }

            // active subtitle content in display and we don't want to interrupt; fail to play the new voiceover
            if (subtitleInDisplay && !stopCurrentIfNecessary)
            {
                return(false);
            }

            // stop the current vo and play the new one
            if (subtitleInDisplay && stopCurrentIfNecessary)
            {
                audioSource.Stop();
                StopAllCoroutines();
            }

            VoiceoverLine voiceoverLine;

            if (dict.TryGetValue(key, out voiceoverLine))
            {
                float[]  timestamps = voiceoverLine.langObjects[(int)current_language].timestamps;
                string[] lines      = voiceoverLine.langObjects[(int)current_language].lines;

                subtitleInDisplay = true;
                // TODO: the load method need to be updated if audio files are stored in streaming assets folder
                audioSource.clip = (AudioClip)Resources.Load(voiceoverLine.audiofilename, typeof(AudioClip));

                // play voiceover
                audioSource.Play();

                // display subtitle for entire audio duration
                if (timestamps.Length < 1)
                {
                    DisplaySubtitle(lines[0], audioSource.clip.length);
                }
                else // display subtitle according to durations in between timestamps
                {
                    float[] durations = new float[timestamps.Length + 1];
                    durations[0] = timestamps[0];
                    durations[timestamps.Length] = audioSource.clip.length - timestamps[timestamps.Length - 1];
                    for (int i = 1; i < timestamps.Length; i++)
                    {
                        durations[i] = timestamps[i] - timestamps[i - 1];
                    }
                    StartCoroutine(DisplaySubtitles(lines, durations));
                }
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// Populate the dictionary for voiceovers in this scene
        /// The key is audio clip file name and the value is corresponding VoiceoverLine object
        /// </summary>
        private void InitializeVoiceoverManager()
        {
            string data = ReadSceneJSONFile();

            if (data == null)
            {
                missingFileErr = true;
            }
            else
            {
                VoiceoverCollection collection = VoiceoverCollection.CreateFromJSON(data);
                VoiceoverLine[]     voLines    = collection.voiceoverLines;
                for (int i = 0; i < voLines.Length; ++i)
                {
                    VoiceoverID crc = new VoiceoverID(voLines[i].key);
                    dict.Add(crc, voLines[i]);
                }
            }
        }