/// <summary> /// Calculate the file segments for analysis. /// </summary> /// <param name="fileSegments"> /// The file segments. /// </param> /// <param name="settings"> /// The settings. /// </param> /// <returns> /// Enumerable of sub-segments. /// </returns> public IEnumerable <ISegment <TSource> > CalculateSegments <TSource>( IEnumerable <ISegment <TSource> > fileSegments, AnalysisSettings settings) { if (this.allowSegmentcutting) { foreach (var segment in fileSegments) { if (!(segment is RemoteSegment)) { throw new NotImplementedException( $"{nameof(RemoteSourcePreparer)} only supports operating on {nameof(RemoteSegment)}"); } var startOffset = segment.StartOffsetSeconds.Seconds(); var endOffset = segment.EndOffsetSeconds.Seconds(); var segmentDuration = endOffset - startOffset; Log.Debug($"{nameof(LocalSourcePreparer)}.{nameof(this.CalculateSegments)}: Calculating segments for duration {segmentDuration}, each {settings.AnalysisMaxSegmentDuration.Value.TotalMilliseconds} long"); // segment into exact chunks - all but the last chunk will be equal to the max duration var segments = AudioFilePreparer.DivideExactLeaveLeftoversAtEnd( Convert.ToInt64(segmentDuration.TotalMilliseconds), Convert.ToInt64(settings.AnalysisMaxSegmentDuration.Value.TotalMilliseconds)); var overlap = settings.SegmentOverlapDuration; long aggregate = 0; // yield each normal segment foreach (long offset in segments) { // The null for minimum means, do not filter short segments. Short filtering is done in AnalysisCoordinator if (LocalSourcePreparer.TryCreateSegment( ref aggregate, offset, segment, startOffset, endOffset, overlap, null /*settings.AnalysisMinSegmentDuration*/, out var validFileSegment)) { yield return((ISegment <TSource>)validFileSegment); } } } } else { foreach (var segment in fileSegments) { var duration = (segment.EndOffsetSeconds - segment.StartOffsetSeconds).Seconds(); if (duration > settings.AnalysisMaxSegmentDuration.Value) { throw new SegmentSplitException( $"Splitting segments has been disabled for" + $" {nameof(RemoteSourcePreparer)}, cannot split {segment}"); } yield return(segment); } } }
/// <summary> /// Calculate the file segments for analysis. /// </summary> /// <param name="fileSegments"> /// The file segments. /// </param> /// <param name="settings"> /// The settings. /// </param> /// <returns> /// Enumerable of sub-segments. /// </returns> public IEnumerable <ISegment <TSource> > CalculateSegments <TSource>( IEnumerable <ISegment <TSource> > fileSegments, AnalysisSettings settings) { foreach (var segment in fileSegments) { if (!(segment is FileSegment)) { throw new NotImplementedException("Anthony was too lazy to fix this properly. " + "Adding support proper support for ISegment is difficult " + "at this stage."); } var fileSegment = (FileSegment)segment; var startOffset = fileSegment.StartOffsetSeconds.Seconds(); var endOffset = fileSegment.EndOffsetSeconds.Seconds(); // process time alignment var startDelta = TimeSpan.Zero; var endDelta = TimeSpan.Zero; if (fileSegment.Alignment != TimeAlignment.None) { // FileSegment should have already verified a date will be present // ReSharper disable once PossibleInvalidOperationException var startDate = fileSegment.TargetFileStartDate.Value.ToUniversalTime(); // if there's a zero second to the time if (startDate.TimeOfDay.Seconds == 0 && startDate.TimeOfDay.Milliseconds == 0) { // then do nothing Log.Debug("TimeAlignment ignored because start date is already aligned"); } else { // calculate the delta to the next minute // 1:23:45, startOffset = 15 // 1:38:45 - start date with offset // 1:39:00 - next minute var dateWithStartOffset = startDate.Add(startOffset); var nextMinute = dateWithStartOffset.Ceiling(TimeSpan.FromMinutes(1)); startDelta = nextMinute - dateWithStartOffset; var dateWithEndOffset = startDate.Add(endOffset); var lastMinute = dateWithEndOffset.Floor(TimeSpan.FromMinutes(1)); endDelta = dateWithEndOffset - lastMinute; } } // the rest of the duration (excluding the start and end fractions from the time alignment) var fileSegmentDuration = (endOffset - startOffset - startDelta - endDelta).TotalMilliseconds; var analysisSegmentMaxDuration = settings.AnalysisMaxSegmentDuration?.TotalMilliseconds ?? fileSegmentDuration; var analysisSegmentMinDuration = this.filterShortSegments ? settings.AnalysisMinSegmentDuration : (TimeSpan?)null; Log.Debug($"{nameof(LocalSourcePreparer)}.{nameof(this.CalculateSegments)}: Calculating segments for duration {fileSegmentDuration}, each {analysisSegmentMaxDuration} long"); // segment into exact chunks - all but the last chunk will be equal to the max duration var segments = AudioFilePreparer.DivideExactLeaveLeftoversAtEnd( Convert.ToInt64(fileSegmentDuration), Convert.ToInt64(analysisSegmentMaxDuration)); var overlap = settings.SegmentOverlapDuration; long aggregate = 0; // include fractional segment cut from time alignment if ((fileSegment.Alignment == TimeAlignment.TrimEnd || fileSegment.Alignment == TimeAlignment.TrimNeither) && startDelta > TimeSpan.Zero) { Log.Debug($"Generated fractional segment for time alignment ({startOffset} - {startOffset + startDelta})"); var startAlignDelta = Convert.ToInt64(startDelta.TotalMilliseconds); if (TryCreateSegment( ref aggregate, startAlignDelta, fileSegment, startOffset, endOffset, overlap, analysisSegmentMinDuration, out var validFileSegment)) { yield return((ISegment <TSource>)validFileSegment); } } else { // advance the counter but don't produce the first segment aggregate += Convert.ToInt64(startDelta.TotalMilliseconds); } // yield each normal segment foreach (long offset in segments) { if (TryCreateSegment( ref aggregate, offset, fileSegment, startOffset, endOffset, overlap, analysisSegmentMinDuration, out var validFileSegment)) { yield return((ISegment <TSource>)validFileSegment); } } // include fractional segment cut from time alignment if ((fileSegment.Alignment == TimeAlignment.TrimStart || fileSegment.Alignment == TimeAlignment.TrimNeither) && startDelta > TimeSpan.Zero) { Log.Debug($"Generated fractional segment for time alignment ({endOffset - endDelta} - {endOffset})"); var endAlignDelta = Convert.ToInt64(endDelta.TotalMilliseconds); if (TryCreateSegment( ref aggregate, endAlignDelta, fileSegment, startOffset, endOffset, overlap, analysisSegmentMinDuration, out var validFileSegment)) { yield return((ISegment <TSource>)validFileSegment); } } } }