public static RemoteSegmentWithData[] DedupeSegments(IList <RemoteSegmentWithData> segments) { // ugggh don't really need a dictionary but HashSet doesn't allow retrieveal of values var unique = new Dictionary <RemoteSegment, RemoteSegmentWithData>(segments.Count); foreach (var segment in segments) { var exists = unique.ContainsKey(segment); if (exists) { var current = unique[segment]; Contract.Ensures(unique.Remove(segment)); var data = current.Data.Concat(segment.Data).ToArray(); var combined = new RemoteSegmentWithData(current.Source, current.Offsets, data); unique.Add(combined, combined); } else { unique.Add(segment, segment); } } return(unique.Values.ToArray()); }
private async Task <IEnumerable <RemoteSegmentWithData> > DownloadRemoteMetadata( long audioRecordingId, IEnumerable <ImportedEvent> events) { // now download the metadata for the audio recording Log.Debug($"Requesting metadata for audio recording media {audioRecordingId}"); var audioRecording = await this.audioRecordingService.GetAudioRecording(audioRecordingId); Log.Trace( $"Metadata for audio recording media {audioRecordingId} retrieved, generating segments for associated events"); // now generate the segments // we need to floor the duration to ensure later rounding does round past the limit of the recording Log.Verbose($"Audio recording duration {audioRecording.DurationSeconds} will be floored"); var limit = audioRecording.DurationSeconds.Floor().AsIntervalFromZero(); var results = new List <RemoteSegmentWithData>(20); foreach (var importedEvent in events) { var target = (importedEvent.EventStartSeconds.Value, importedEvent.EventEndSeconds.Value).AsInterval(); // determine how much padding is required (dynamically scales with event size var padding = this.AnalysisDurationSeconds(target.Size()); if (target.Size() + padding > MediaService.MediaDownloadMaximumSeconds) { var newPadding = MediaService.MediaDownloadMaximumSeconds - target.Size(); Log.Warn( $"Audio event size {audioRecordingId},{target} and padding {padding} exceeds maximum media " + $"download amount - trimming padding from {padding}, to {newPadding}"); padding = newPadding; } // grow target to required analysis length // we round the grow size to the nearest integer so that we reduce caching combinatorics in the workbench Log.Trace($"Growing target event from {audioRecordingId},{target} by {padding} seconds"); var analysisRange = target.Grow(limit, padding, 0); var segment = new RemoteSegmentWithData(audioRecording, analysisRange, importedEvent.AsArray()); results.Add(segment); } return(DedupeSegments(results)); }