/// <summary> /// API Handler when the Auto Segment button is clicked /// /// Replies with true if AutoSegment completed successfully, or false if there was an error. In addition, a NonFatal message/exception may be reported with the error /// </summary> public void AutoSegment(ApiRequest request) { Logger.WriteEvent("AudioSegmentationApi.AutoSegment(): AutoSegment started."); // Parse the JSON containing the text segmentation data. string json = request.RequiredPostJson(); AutoSegmentRequest requestParameters = ParseJson(json); string audioFilenameToSegment; List <string> fragmentIds; var timingStartEndRangeList = GetAeneasTimings(requestParameters, out audioFilenameToSegment, out fragmentIds); if (timingStartEndRangeList == null) { request.ReplyWithBoolean(false); return; } try { ExtractAudioSegments(fragmentIds, timingStartEndRangeList, audioFilenameToSegment); } catch (Exception e) { ProblemReportApi.ShowProblemDialog(null, e, "AudioSegmentationApi.AutoSegment(): Exception thrown during split/extract stage", "nonfatal"); request.ReplyWithBoolean(false); return; } Logger.WriteEvent("AudioSegmentationApi.AutoSegment(): Completed successfully."); request.ReplyWithBoolean(true); // Success }
internal List <Tuple <string, string> > GetAeneasTimings(AutoSegmentRequest requestParameters, out string audioFilenameToSegment, out List <string> fragmentIds) { fragmentIds = null; // The client was supposed to validate this already, but double-check in case something strange happened. string directoryName = GetAudioDirectory(); audioFilenameToSegment = GetFileNameToSegment(directoryName, requestParameters.audioFilenameBase); if (string.IsNullOrEmpty(audioFilenameToSegment)) { Logger.WriteEvent("AudioSegmentationApi.GetAeneasTimings(): No input audio file found."); ErrorReport.ReportNonFatalMessageWithStackTrace("No audio file found. Please record audio first."); return(null); } IEnumerable <AudioTextFragment> audioTextFragments = requestParameters.audioTextFragments; string requestedLangCode = requestParameters.lang; // The client was supposed to validate this already, but double-check in case something strange happened. // Since this is basically a desperate fallback that shouldn't ever happen we won't try to make the message // contain a hot link here. That code is in Typescript. string message; if (!AreAutoSegmentDependenciesMet(out message)) { var localizedFormatString = L10NSharp.LocalizationManager.GetString("EditTab.Toolbox.TalkingBook.MissingDependency", "To split recordings into sentences, first install this {0} system.", "The placeholder {0} will be replaced with the dependency that needs to be installed."); ErrorReport.ReportNonFatalMessageWithStackTrace(string.Format(localizedFormatString, message)); return(null); } // When using TTS overrides, there's no Aeneas error message that tells us if the language is unsupported. // Therefore, we explicitly test if the language is supported by the dependency (eSpeak) before getting started. string stdOut; string stdErr; string langCode = GetBestSupportedLanguage(requestedLangCode, out stdOut, out stdErr); if (string.IsNullOrEmpty(langCode)) { // FYI: The error message is expected to be in stdError with an empty stdOut, but I included both just in case. Logger.WriteEvent("AudioSegmentationApi.GetAeneasTimings(): eSpeak error."); ErrorReport.ReportNonFatalMessageWithStackTrace($"eSpeak error: {stdOut}\n{stdErr}"); return(null); } Logger.WriteMinorEvent($"AudioSegmentationApi.GetAeneasTimings(): Attempting to segment with langCode={langCode}"); string textFragmentsFilename = Path.Combine(directoryName, $"{requestParameters.audioFilenameBase}_fragments.txt"); string audioTimingsFilename = Path.Combine(directoryName, $"{requestParameters.audioFilenameBase}_timings.{kTimingsOutputFormat}"); // Clean up the fragments audioTextFragments = audioTextFragments.Where(obj => !String.IsNullOrWhiteSpace(obj.fragmentText)); // Remove entries containing only whitespace var fragmentList = audioTextFragments.Select(obj => TextUtils.TrimEndNewlines(obj.fragmentText)); if (langCode != requestedLangCode) { string collectionPath = _bookSelection.CurrentSelection.CollectionSettings.FolderPath; string orthographyConversionMappingPath = Path.Combine(collectionPath, $"convert_{requestedLangCode}_to_{langCode}.txt"); if (File.Exists(orthographyConversionMappingPath)) { fragmentList = ApplyOrthographyConversion(fragmentList, orthographyConversionMappingPath); } } // Get the GUID filenames (without extension) fragmentIds = audioTextFragments.Select(obj => obj.id).ToList(); List <Tuple <string, string> > timingStartEndRangeList = null; try { File.WriteAllLines(textFragmentsFilename, fragmentList); timingStartEndRangeList = GetSplitStartEndTimings(audioFilenameToSegment, textFragmentsFilename, audioTimingsFilename, langCode); } catch (Exception e) { ProblemReportApi.ShowProblemDialog(null, e, "AudioSegmentationApi.GetAeneasTimings(): Exception thrown during split stage", "nonfatal"); return(null); } try { RobustFile.Delete(textFragmentsFilename); } catch (Exception e) { // These exceptions are unfortunate but not bad enough that we need to inform the user Debug.Assert(false, $"Attempted to delete {textFragmentsFilename} but it threw an exception. Message={e.Message}, Stack={e.StackTrace}"); } try { RobustFile.Delete(audioTimingsFilename); } catch (Exception e) { // These exceptions are unfortunate but not bad enough that we need to inform the user Debug.Assert(false, $"Attempted to delete {audioTimingsFilename} but it threw an exception. Message={e.Message}, Stack={e.StackTrace}"); } return(timingStartEndRangeList); }