/// <summary> /// Summarizes an enumerable of double messages into summarized doubles. /// </summary> /// <param name="messages">Enumerable of double messages.</param> /// <param name="interval">The time interval each summary value should cover.</param> /// <returns>List of summarized doubles.</returns> public static List <IntervalData <double> > Summarizer(IEnumerable <Message <double> > messages, TimeSpan interval) { return(messages .OrderBy(msg => msg.OriginatingTime) .GroupBy(msg => GetIntervalStartTime(msg.OriginatingTime, interval)) .Select( group => { var firstMessage = group.First(); var lastMessage = group.Last(); return IntervalData.Create( lastMessage.Data, // Take last value as representative value for plotting group.Min(m => m.Data), // Minimum value group.Max(m => m.Data), // Maximum value firstMessage.OriginatingTime, // First message's OT lastMessage.OriginatingTime - firstMessage.OriginatingTime); // Interval between first and last messages }).ToList()); }
private static List <IntervalData <object> > Summarizer(IEnumerable <Message <T> > messages, TimeSpan interval) { return(messages .OrderBy(msg => msg.OriginatingTime) .GroupBy(msg => GetIntervalStartTime(msg.OriginatingTime, interval)) .Select( group => { var firstMessage = group.First(); var lastMessage = group.Last(); var representative = (object)firstMessage.Data; return IntervalData.Create( representative, default(object), default(object), firstMessage.OriginatingTime, // First message's OT lastMessage.OriginatingTime - firstMessage.OriginatingTime); // Interval between first and last messages }).ToList()); }
/// <summary> /// Summarizes an enumerable of double messages into summarized doubles. /// </summary> /// <typeparam name="T">The type of messages to summarize.</typeparam> /// <param name="messages">Enumerable of double messages.</param> /// <param name="interval">The time interval each summary value should cover.</param> /// <returns>List of summarized doubles.</returns> internal static List <IntervalData <T> > Summarizer <T>(IEnumerable <Message <T> > messages, TimeSpan interval) { return(messages .OrderBy(msg => msg.OriginatingTime) .GroupBy(msg => Summarizer <T, T> .GetIntervalStartTime(msg.OriginatingTime, interval)) .Select( group => { var firstMessage = group.First(); var lastMessage = group.Last(); // Use the last value as representative for summarization, with the first message // originating time. return IntervalData.Create( value: lastMessage.Data, minimum: group.Min(m => m.Data), maximum: group.Max(m => m.Data), originatingTime: firstMessage.OriginatingTime, interval: lastMessage.OriginatingTime - firstMessage.OriginatingTime); }).ToList()); }
/// <summary> /// Summarizes an enumerable of time interval messages into summarized time intervals. /// </summary> /// <param name="messages">Enumerable of time interval messages.</param> /// <param name="interval">The time interval each summary value should cover.</param> /// <returns>List of summarized time intervals.</returns> public static List <IntervalData <Tuple <DateTime, DateTime> > > Summarizer(IEnumerable <Message <Tuple <DateTime, DateTime> > > messages, TimeSpan interval) { return(messages .OrderBy(msg => msg.OriginatingTime) .GroupBy(msg => GetIntervalStartTime(msg.OriginatingTime, interval)) .Select( group => { var firstMessage = group.First(); var lastMessage = group.Last(); var ascendingLatencies = group.OrderBy(m => m.Data.Item2 - m.Data.Item1).Select(m => m.Data); var minLatency = ascendingLatencies.First(); var maxLatency = ascendingLatencies.Last(); // Use max latency as representative value return IntervalData.Create( maxLatency, minLatency, maxLatency, firstMessage.OriginatingTime, lastMessage.OriginatingTime - firstMessage.OriginatingTime); }).ToList()); }
/// <summary> /// Summarizes an enumerable of audio buffer messages into summarized audio data. /// </summary> /// <param name="messages">Enumerable of audio buffer messages.</param> /// <param name="interval">The time interval each summary value should cover.</param> /// <param name="channel">The audio channel to summarize.</param> /// <returns>List of summarized audio data.</returns> public static List <IntervalData <double> > Summarizer(IEnumerable <Message <AudioBuffer> > messages, TimeSpan interval, ushort channel) { var intervalData = new List <IntervalData <double> >(); // Inspect the first message and only continue if it exists Message <AudioBuffer> first = messages.FirstOrDefault(); if (first != default(Message <AudioBuffer>)) { // Assume audio format is the same for all items WaveFormat audioFormat = first.Data.Format; // Get the appropriate sample converter. Assumption is 16-bit samples // are integer and 32-bit samples are floating point values. Func <byte[], int, double> sampleConverter; switch (audioFormat.BitsPerSample) { case 16: sampleConverter = (buf, i) => BitConverter.ToInt16(buf, i); break; case 32: sampleConverter = (buf, i) => BitConverter.ToSingle(buf, i); break; default: sampleConverter = (buf, i) => buf[i]; break; } TimeSpan samplingInterval = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / audioFormat.SamplesPerSec); int byteOffset = 0 + ((channel % audioFormat.Channels) * audioFormat.BitsPerSample / 8); DateTime currentIntervalStartTime = GetIntervalStartTime(first.OriginatingTime, interval); double min = double.MaxValue; double max = double.MinValue; foreach (var message in messages) { // Each item holds an AudioBuffer containing multiple audio samples AudioBuffer audioBuffer = message.Data; // Start (originating time) of the first sample in the AudioBuffer DateTime currentTime = message.OriginatingTime - audioBuffer.Duration; // calculate min/max for specified channel over the entire audio buffer for (int offset = byteOffset; offset < audioBuffer.Length; offset += audioFormat.BlockAlign) { DateTime intervalStartTime = GetIntervalStartTime(currentTime, interval); // Check if we are still in the current interval. If not, commit the current // min/max values to the current interval if they have non-default values. if ((intervalStartTime != currentIntervalStartTime) && (min != double.MaxValue) && (max != double.MinValue)) { var current = IntervalData.Create(min + ((max - min) / 2), min, max, currentIntervalStartTime, interval); intervalData.Add(current); // Reset min-max for next interval min = double.MaxValue; max = double.MinValue; // Update next interval start time currentIntervalStartTime = intervalStartTime; } // Update min-max range for the current interval double sampleValue = sampleConverter(audioBuffer.Data, offset); if (sampleValue < min) { min = sampleValue; } if (sampleValue > max) { max = sampleValue; } // Increment by the sampling interval currentTime += samplingInterval; } } // Add the last interval intervalData.Add(IntervalData.Create(min + ((max - min) / 2), min, max, currentIntervalStartTime, interval)); } return(intervalData); }
/// <summary> /// Summarizes an enumerable of double messages into summarized doubles. /// </summary> /// <typeparam name="TKey">The type of the series key.</typeparam> /// <typeparam name="T">The type of messages to summarize.</typeparam> /// <param name="messages">Enumerable of double messages.</param> /// <param name="interval">The time interval each summary value should cover.</param> /// <returns>List of summarized doubles.</returns> internal static List <IntervalData <Dictionary <TKey, T> > > SeriesSummarizer <TKey, T>(IEnumerable <Message <Dictionary <TKey, T> > > messages, TimeSpan interval) { return(messages .OrderBy(msg => msg.OriginatingTime) .GroupBy(msg => Summarizer <T, T> .GetIntervalStartTime(msg.OriginatingTime, interval)) .Select( group => { var firstMessage = group.First(); var lastMessage = group.Last(); var min = new Dictionary <TKey, T>(); var max = new Dictionary <TKey, T>(); foreach (var message in group) { foreach (var kvp in message.Data) { // Update min if (!min.ContainsKey(kvp.Key)) { min.Add(kvp.Key, kvp.Value); } else if (kvp.Value is IComparable <T> comparable) { if (comparable.CompareTo(min[kvp.Key]) == -1) { min[kvp.Key] = kvp.Value; } } else if (kvp.Value is IComparable untypedComparable) { if (untypedComparable.CompareTo(min[kvp.Key]) == -1) { min[kvp.Key] = kvp.Value; } } else { throw new InvalidOperationException("Cannot summarize over values that are not comparable."); } // Update max if (!max.ContainsKey(kvp.Key)) { max.Add(kvp.Key, kvp.Value); } else if (kvp.Value is IComparable <T> comparable) { if (comparable.CompareTo(max[kvp.Key]) == 1) { max[kvp.Key] = kvp.Value; } } else if (kvp.Value is IComparable untypedComparable) { if (untypedComparable.CompareTo(max[kvp.Key]) == 1) { max[kvp.Key] = kvp.Value; } } else { throw new InvalidOperationException("Cannot summarize over values that are not comparable."); } } } // Use the last value as representative for summarization, with the first message // originating time. return IntervalData.Create( value: lastMessage.Data, minimum: min, maximum: max, originatingTime: firstMessage.OriginatingTime, interval: lastMessage.OriginatingTime - firstMessage.OriginatingTime); }).ToList()); }
/// <summary> /// Combines two numeric series interval data values. /// </summary> /// <typeparam name="TKey">The type of the series key.</typeparam> /// <typeparam name="T">The type of messages to summarize.</typeparam> /// <param name="left">The first value to combine.</param> /// <param name="right">The second value to combine.</param> /// <returns>The combined value.</returns> internal static IntervalData <Dictionary <TKey, T> > SeriesCombiner <TKey, T>(IntervalData <Dictionary <TKey, T> > left, IntervalData <Dictionary <TKey, T> > right) { Comparer <T> comparer = Comparer <T> .Default; var min = new Dictionary <TKey, T>(); foreach (var key in left.Minimum.Keys.Union(right.Minimum.Keys).Distinct()) { if (left.Minimum.ContainsKey(key)) { if (right.Minimum.ContainsKey(key)) { min.Add(key, comparer.Compare(left.Minimum[key], right.Minimum[key]) < 0 ? left.Minimum[key] : right.Minimum[key]); } else { min.Add(key, left.Minimum[key]); } } else { min.Add(key, right.Minimum[key]); } } var max = new Dictionary <TKey, T>(); foreach (var key in left.Maximum.Keys.Union(right.Maximum.Keys).Distinct()) { if (left.Maximum.ContainsKey(key)) { if (right.Maximum.ContainsKey(key)) { max.Add(key, comparer.Compare(left.Maximum[key], right.Maximum[key]) > 0 ? left.Maximum[key] : right.Maximum[key]); } else { max.Add(key, left.Maximum[key]); } } else { max.Add(key, right.Maximum[key]); } } Dictionary <TKey, T> value; DateTime originatingTime; // Take the value which occurs last, and the time which occurs first if (left.OriginatingTime <= right.OriginatingTime) { value = right.Value; originatingTime = left.OriginatingTime; } else { value = left.Value; originatingTime = right.OriginatingTime; } // Take the whichever end time occurs last and use it to find the interval TimeSpan interval = (right.EndTime > left.EndTime ? right.EndTime : left.EndTime) - originatingTime; return(IntervalData.Create(value, min, max, originatingTime, interval)); }