C# - Getting a RawFraction Performance Counter to show a persistent value - c#

I've created a performance counter that shows a fraction of an incremented value (RawFraction type) over a base value (RawBase).
Unfortunately, when monitoring this value, it only shows the percentage when one of the counters is incremented. At all other times it it is sampled, it shows 0. Is there some way to tell the counter to hold onto the last value until the next time it needs to recalculate the fraction?

It's hard to know what went wrong for you without seeing any of your code but here's an example of it being used correctly (from: http://msdn.microsoft.com/en-us/library/system.diagnostics.performancecountertype.aspx)
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
public class App
{
private static PerformanceCounter PC;
private static PerformanceCounter BPC;
public static void Main()
{
ArrayList samplesList = new ArrayList();
// If the category does not exist, create the category and exit.
// Performance counters should not be created and immediately used.
// There is a latency time to enable the counters, they should be created
// prior to executing the application that uses the counters.
// Execute this sample a second time to use the counters.
if (SetupCategory())
return;
CreateCounters();
CollectSamples(samplesList);
CalculateResults(samplesList);
}
private static bool SetupCategory()
{
if (!PerformanceCounterCategory.Exists("RawFractionSampleCategory"))
{
CounterCreationDataCollection CCDC = new CounterCreationDataCollection();
// Add the counter.
CounterCreationData rf = new CounterCreationData();
rf.CounterType = PerformanceCounterType.RawFraction;
rf.CounterName = "RawFractionSample";
CCDC.Add(rf);
// Add the base counter.
CounterCreationData rfBase = new CounterCreationData();
rfBase.CounterType = PerformanceCounterType.RawBase;
rfBase.CounterName = "RawFractionSampleBase";
CCDC.Add(rfBase);
// Create the category.
PerformanceCounterCategory.Create("RawFractionSampleCategory",
"Demonstrates usage of the RawFraction performance counter type.",
PerformanceCounterCategoryType.SingleInstance, CCDC);
return (true);
}
else
{
Console.WriteLine("Category exists - RawFractionSampleCategory");
return (false);
}
}
private static void CreateCounters()
{
// Create the counters.
PC = new PerformanceCounter("RawFractionSampleCategory",
"RawFractionSample",
false);
BPC = new PerformanceCounter("RawFractionSampleCategory",
"RawFractionSampleBase",
false);
PC.RawValue = 0;
BPC.RawValue = 0;
}
private static void CollectSamples(ArrayList samplesList)
{
Random r = new Random(DateTime.Now.Millisecond);
// Initialize the performance counter.
PC.NextSample();
// Loop for the samples.
for (int j = 0; j < 100; j++)
{
int value = r.Next(1, 10);
Console.Write(j + " = " + value);
// Increment the base every time, because the counter measures the number
// of high hits (raw fraction value) against all the hits (base value).
BPC.Increment();
// Get the % of samples that are 9 or 10 out of all the samples taken.
if (value >= 9)
PC.Increment();
// Copy out the next value every ten times around the loop.
if ((j % 10) == 9)
{
Console.WriteLine("; NextValue() = " + PC.NextValue().ToString());
OutputSample(PC.NextSample());
samplesList.Add(PC.NextSample());
}
else
Console.WriteLine();
System.Threading.Thread.Sleep(50);
}
}
private static void CalculateResults(ArrayList samplesList)
{
for (int i = 0; i < samplesList.Count; i++)
{
// Output the sample.
OutputSample((CounterSample)samplesList[i]);
// Use .NET to calculate the counter value.
Console.WriteLine(".NET computed counter value = " +
CounterSampleCalculator.ComputeCounterValue((CounterSample)samplesList[i]));
// Calculate the counter value manually.
Console.WriteLine("My computed counter value = " +
MyComputeCounterValue((CounterSample)samplesList[i]));
}
}
//++++++++//++++++++//++++++++//++++++++//++++++++//++++++++//++++++++//++++++++
// Formula from MSDN -
// Description - This counter type shows the ratio of a subset to its set as a percentage.
// For example, it compares the number of bytes in use on a disk to the
// total number of bytes on the disk. Counters of this type display the
// current percentage only, not an average over time.
//
// Generic type - Instantaneous, Percentage
// Formula - (N0 / D0), where D represents a measured attribute and N represents one
// component of that attribute.
//
// Average - SUM (N / D) /x
// Example - Paging File\% Usage Peak
//++++++++//++++++++//++++++++//++++++++//++++++++//++++++++//++++++++//++++++++
private static Single MyComputeCounterValue(CounterSample rfSample)
{
Single numerator = (Single)rfSample.RawValue;
Single denomenator = (Single)rfSample.BaseValue;
Single counterValue = (numerator / denomenator) * 100;
return (counterValue);
}
// Output information about the counter sample.
private static void OutputSample(CounterSample s)
{
Console.WriteLine("+++++++++++");
Console.WriteLine("Sample values - \r\n");
Console.WriteLine(" BaseValue = " + s.BaseValue);
Console.WriteLine(" CounterFrequency = " + s.CounterFrequency);
Console.WriteLine(" CounterTimeStamp = " + s.CounterTimeStamp);
Console.WriteLine(" CounterType = " + s.CounterType);
Console.WriteLine(" RawValue = " + s.RawValue);
Console.WriteLine(" SystemFrequency = " + s.SystemFrequency);
Console.WriteLine(" TimeStamp = " + s.TimeStamp);
Console.WriteLine(" TimeStamp100nSec = " + s.TimeStamp100nSec);
Console.WriteLine("++++++++++++++++++++++");
}
}

Related

Calculate max on a sliding window for TimeSeries

Input:
public class MyObject
{
public double Value { get; set; }
public DateTime Date { get; set; }
}
Method to generate test objects:
public static MyObject[] GetTestObjects()
{
var rnd = new Random();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
var result = new List<MyObject>();
for (int i = 0; i < 50000; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
var myObject = new MyObject()
{
Value = rnd.NextDouble(),
Date = date.AddMinutes(15 * i)
};
result.Add(myObject);
}
return result.ToArray();
}
Given this I require to calculate maximum Value for previous 12 month for each myObject. I could just think of doing this InParallel, but maybe there is an optimized solution?
Sorry for being unclear, this is what I use right now to get what I want:
public MyObject[] BruteForceBackward(MyObject[] testData)
{
return testData.AsParallel().Select(point =>
{
var max = testData.Where(x => x.Date <= point.Date && x.Date >= point.Date.AddYears(-1)).Max(x => x.Value);
return new MyObject() { Date = point.Date, Value = point.Value / max };
}).OrderBy(r => r.Date).ToArray();
}
This works but it is slow and eats processor resources (imagine, you have 100k objects), I believe there must be something better
I had a simillar project where i had to calculate such stuff on tons of sensor data.
You can now find a little more refined version in my Github repository, which should be ready to use (.Net):
https://github.com/forReason/Statistics-Helper-Library
In general you want to reduce the amount of loops going over all your data. At best, you want to touch each element only one single time.
Process Array (equiv. of BruteForceBackwards)
public static MyObject[] FlowThroughForward(ref MyObject[] testData)
{
// generate return array
MyObject[] returnData = new MyObject[testData.Length];
// keep track to minimize processing
double currentMaximum = 0;
List<MyObject> maximumValues = new List<MyObject>();
// go through the elements
for (int i = 0; i < testData.Length; i++)
{
// calculate the oldest date to keep in tracking list
DateTime targetDate = testData[i].Date.AddYears(-1);
// maximum logic
if (testData[i].Value >= currentMaximum)
{
// new maximum found, clear tracking list
// this is the best case scenario
maximumValues.Clear();
currentMaximum = testData[i].Value;
}
else
{
// unfortunately, no new maximum was found
// go backwards the maximum tracking list and check for smaller values
// clear the list of all smaller values. The list should therefore always
// be in descending order
for (int b = maximumValues.Count - 1; b >= 0; b--)
{
if (maximumValues[b].Value <= testData[i].Value)
{
// a lower value has been found. We have a newer, higher value
// clear this waste value from the tracking list
maximumValues.RemoveAt(b);
}
else
{
// there are no more lower values.
// stop looking for smaller values to save time
break;
}
}
}
// append new value to tracking list, no matter if higher or lower
// all future values might be lower
maximumValues.Add(testData[i]);
// check if the oldest value is too old to be kept in the tracking list
while (maximumValues[0].Date < targetDate)
{
// oldest value is to be removed
maximumValues.RemoveAt(0);
// update maximum
currentMaximum = maximumValues[0].Value;
}
// add object to result list
returnData[i] = new MyObject() { Date = testData[i].Date, Value = testData[i].Value / currentMaximum }; ;
}
return returnData;
}
Real Time Data or Streamed Data
Note: If you have really large lists, you might get memory issues with your approach to pass a full array. In this case: pass one value at a time, pass them from oldest value to newest value. Store the values back one at a time.
This Function can also be used on real time data.
The test method is included in code.
static void Main(string[] args)
{
int length = 50000;
Stopwatch stopWatch1 = new Stopwatch();
stopWatch1.Start();
var myObject = new MyObject();
var result = new List<MyObject>();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
for (int i = 0; i < length; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
myObject.Value = rnd.NextDouble();
myObject.Date = date.AddMinutes(15 * i);
result.Add(CalculateNextObject(ref myObject));
}
stopWatch1.Stop();
Console.WriteLine("test code executed in " + stopWatch1.ElapsedMilliseconds + " ms");
Thread.Sleep(1000000);
}
private static Random rnd = new Random();
private static double currentMaximum = 0;
private static List<MyObject> maximumValues = new List<MyObject>();
public static MyObject CalculateNextObject(ref MyObject input)
{
// calculate the oldest date to keep in tracking list
DateTime targetDate = input.Date.AddYears(-1);
// maximum logic
if (input.Value >= currentMaximum)
{
// new maximum found, clear tracking list
// this is the best case scenario
maximumValues.Clear();
currentMaximum = input.Value;
}
else
{
// unfortunately, no new maximum was found
// go backwards the maximum tracking list and check for smaller values
// clear the list of all smaller values. The list should therefore always
// be in descending order
for (int b = maximumValues.Count - 1; b >= 0; b--)
{
if (maximumValues[b].Value <= input.Value)
{
// a lower value has been found. We have a newer, higher value
// clear this waste value from the tracking list
maximumValues.RemoveAt(b);
}
else
{
// there are no more lower values.
// stop looking for smaller values to save time
break;
}
}
}
// append new value to tracking list, no matter if higher or lower
// all future values might be lower
maximumValues.Add(input);
// check if the oldest value is too old to be kept in the tracking list
while (maximumValues[0].Date < targetDate)
{
// oldest value is to be removed
maximumValues.RemoveAt(0);
// update maximum
currentMaximum = maximumValues[0].Value;
}
// add object to result list
MyObject returnData = new MyObject() { Date = input.Date, Value = input.Value / currentMaximum };
return returnData;
}
Test Method
static void Main(string[] args)
{
MyObject[] testData = GetTestObjects();
Stopwatch stopWatch1 = new Stopwatch();
Stopwatch stopWatch2 = new Stopwatch();
stopWatch1.Start();
MyObject[] testresults1 = BruteForceBackward(testData);
stopWatch1.Stop();
Console.WriteLine("BruteForceBackward executed in " + stopWatch1.ElapsedMilliseconds + " ms");
stopWatch2.Start();
MyObject[] testresults2 = FlowThroughForward(ref testData);
stopWatch2.Stop();
Console.WriteLine("FlowThroughForward executed in " + stopWatch2.ElapsedMilliseconds + " ms");
Console.WriteLine();
Console.WriteLine("Comparing some random test results: ");
var rnd = new Random();
for (int i = 0; i < 10; i++)
{
int index = rnd.Next(0, testData.Length);
Console.WriteLine("Index: " + index + " brute: " + testresults1[index].Value + " flow: " + testresults2[index].Value);
}
Thread.Sleep(1000000);
}
Test result
Tests were performed on a machine with 32 cores, so in teory multithreaded aproach should be at advantage but youll see ;)
Function
Function Time
time %
BruteForceBackward
5334 ms
99.9%
FlowThroughForward
5 ms
0.094%
Performance improvement factor: ~time/1000
console output with data validation:
BruteForceBackward executed in 5264 ms
FlowThroughForward executed in 5 ms
Comparing some random test results:
Index: 25291 brute: 0.989688139105413 flow: 0.989688139105413
Index: 11945 brute: 0.59670821976193 flow: 0.59670821976193
Index: 30282 brute: 0.413238225210297 flow: 0.413238225210297
Index: 33898 brute: 0.38258761939139 flow: 0.38258761939139
Index: 8824 brute: 0.833512217105447 flow: 0.833512217105447
Index: 22092 brute: 0.648052464067263 flow: 0.648052464067263
Index: 24633 brute: 0.35859417692481 flow: 0.35859417692481
Index: 24061 brute: 0.540642018793402 flow: 0.540642018793402
Index: 34219 brute: 0.498785766613022 flow: 0.498785766613022
Index: 2396 brute: 0.151471808392111 flow: 0.151471808392111
Cpu usage was a lot higher on Bruteforce backwards due to parallelisation.
The worst case scenario are long periods of decreasing values. The code can still be vastly optimized but I guess this should be sufficient. For further optimisation, one might look to reduce the list shuffles when removing/adding elements to maximumValues.
An interesting and challenging problem. I put together a solution using a dynamic programming approach (first learned back in CS algorithms class back in '78). First, a tree is constructed containing pre-calculated local max values over recursively defined ranges. Once constructed, the max value for an arbitrary range can be efficiently calculated mostly using the pre-calculated values. Only at the fringes of the range does the calculation drop down to the element level.
It is not as fast as julian bechtold's FlowThroughForward method, but random access to ranges may be a plus.
Code to add to Main:
Console.WriteLine();
Stopwatch stopWatch3 = new Stopwatch();
stopWatch3.Start();
MyObject[] testresults3 = RangeTreeCalculation(ref testData, 10);
stopWatch3.Stop();
Console.WriteLine($"RangeTreeCalculation executed in {stopWatch3.ElapsedMilliseconds} ms");
... test comparison
Console.WriteLine($"Index: {index} brute: {testresults1[index].Value} flow: {testresults2[index].Value} rangeTree: {testresults3[index].Value}");
Test function:
public static MyObject[] RangeTreeCalculation(ref MyObject[] testDataArray, int partitionThreshold)
{
// For this implementation, we need to convert the Array to an ArrayList, because we need a
// reference type object that can be shared.
List<MyObject> testDataList = testDataArray.ToList();
// Construct a tree containing recursive collections of pre-calculated values
var rangeTree = new RangeTree(testDataList, partitionThreshold);
MyObject[] result = new MyObject[testDataList.Count];
Parallel.ForEach(testDataList, (item, state, i) =>
{
var max = rangeTree.MaxForDateRange(item.Date.AddYears(-1), item.Date);
result[i] = new MyObject() { Date = item.Date, Value = item.Value / max };
});
return result;
}
Supporting class:
// Class used to divide and conquer using dynamic programming.
public class RangeTree
{
public List<MyObject> Data; // This reference is shared by all members of the tree
public int Start { get; } // Index of first element covered by this node.
public int Count { get; } // Number of elements covered by this node.
public DateTime FirstDateTime { get; }
public DateTime LastDateTime { get; }
public double MaxValue { get; } // Pre-calculated max for all elements covered by this node.
List<RangeTree> ChildRanges { get; }
// Top level node constructor
public RangeTree(List<MyObject> data, int partitionThreshold)
: this(data, 0, data.Count, partitionThreshold)
{
}
// Child node constructor, which covers an recursively decreasing range of element.
public RangeTree(List<MyObject> data, int start, int count, int partitionThreshold)
{
Data = data;
Start = start;
Count = count;
FirstDateTime = Data[Start].Date;
LastDateTime = Data[Start + Count - 1].Date;
if (count <= partitionThreshold)
{
// If the range is smaller than the threshold, just calculate the local max
// directly from the items. No child ranges are defined.
MaxValue = Enumerable.Range(Start, Count).Select(i => Data[i].Value).Max();
}
else
{
// We still have a significant range. Decide how to further divide them up into sub-ranges.
// (There may be room for improvement here to better balance the tree.)
int partitionSize = (count - 1) / partitionThreshold + 1;
int partitionCount = (count - 1) / partitionSize + 1;
if (count < partitionThreshold * partitionThreshold)
{
// When one away from leaf nodes, prefer fewer full leaf nodes over more
// less populated leaf nodes.
partitionCount = (count - 1) / partitionThreshold + 1;
partitionSize = (count - 1) / partitionCount + 1;
}
ChildRanges = Enumerable.Range(0, partitionCount)
.Select(partitionNum => new {
ChildStart = Start + partitionNum * partitionSize,
ChildCount = Math.Min(partitionSize, Count - partitionNum * partitionSize)
})
.Where(part => part.ChildCount > 0) // Defensive
.Select(part => new RangeTree(Data, part.ChildStart, part.ChildCount, partitionThreshold))
.ToList();
// Now is the dynamic programming part:
// Calculate the local max as the max of all child max values.
MaxValue = ChildRanges.Max(chile => chile.MaxValue);
}
}
// Get the max value for a given range of dates withing this rangeTree node.
// This used the precalculated values as much as possible.
// Only at the fringes of the date range to we calculate at the element level.
public double MaxForDateRange(DateTime fromDate, DateTime thruDate)
{
double calculatedMax = Double.MinValue;
if (fromDate > this.LastDateTime || thruDate < this.FirstDateTime)
{
// Entire range is excluded. Nothing of interest here folks.
calculatedMax = Double.MinValue;
}
else if (fromDate <= this.FirstDateTime && thruDate >= this.LastDateTime)
{
// Entire range is included. Use the already-calculated max.
calculatedMax = this.MaxValue;
}
else if (ChildRanges != null)
{
// We have child ranges. Recurse and accumulate.
// Possible optimization: Calculate max for middle ranges first, and only bother
// with extreme partial ranges if their local max values exceed the preliminary result.
for (int i = 0; i < ChildRanges.Count; ++i)
{
double childMax = ChildRanges[i].MaxForDateRange(fromDate, thruDate);
if (childMax > calculatedMax)
{
calculatedMax = childMax;
}
}
}
else
{
// Leaf range. Loop through just this limited range of notes, checking individually for
// date in range and accumulating the result.
for (int i = 0; i < this.Count; ++i)
{
var element = Data[this.Start + i];
if (fromDate <= element.Date && element.Date <= thruDate && element.Value > calculatedMax)
{
calculatedMax = element.Value;
}
}
}
return calculatedMax;
}
}
There's plenty of room for improvement, such as parameterizing the types and generalizing the functionality to support more than just Max(Value), but the framework is there.
Assuming you meant you need the maximum Value for each of the last 12 months from result, then you can use LINQ:
var beginDateTime = DateTime.Now.AddMonths(-12);
var ans = result.Where(r => r.Date >= beginDateTime).GroupBy(r => r.Date.Month).Select(mg => mg.MaxBy(r => r.Value)).ToList();
Running some timing, I get that putting AsParallel after result changes the run time from around 16ms (first run) to around 32ms, so it is actually slower. It is about the same after the Where and about 23ms after the GroupBy (processing the 12 groups in parallel). On my PC at least, there isn't enough data or complex operations for parallelism, but the GroupBy isn't the most efficient.
Using an array and testing each element, I get the results in about 1.2ms:
var maxMOs = new MyObject[12];
foreach (var r in result.Where(r => r.Date >= beginDateTime)) {
var monthIndex = r.Date.Month-1;
if (maxMOs[monthIndex] == null || r.Value > maxMOs[monthIndex].Value)
maxMOs[monthIndex] = r;
}
Note that the results are not chronological; you could offset monthIndex by today's month to order the results if desired.
var maxMOs = new MyObject[12];
var offset = DateTime.Now.Month-11;
foreach (var r in result.Where(r => r.Date >= beginDateTime)) {
var monthIndex = r.Date.Month-offset;
if (maxMOs[monthIndex] == null || r.Value > maxMOs[monthIndex].Value)
maxMOs[monthIndex] = r;
}
A micro-optimization (mostly useful on repeat runnings) is to invert the test and use the null-propagating operator:
if (!(r.Value <= maxMOs[monthIndex]?.Value))
This saves about 0.2ms on the first run but up to 0.5ms on subsequent runs.
Here is a solution similar to julian bechtold's answer. Difference is that the maximum (and all related variables) are kept hidden away from the main implementation, in a separate class whose purpose is solely to keep track of the maximum over the past year. Algorithm is the same, I just use a few Linq expressions here and there.
We keep track of the maximum in the following class:
public class MaxSlidingWindow
{
private readonly List<MyObject> _maximumValues;
private double _max;
public MaxSlidingWindow()
{
_maximumValues = new List<MyObject>();
_max = double.NegativeInfinity;
}
public double Max => _max;
public void Add(MyObject myObject)
{
if (myObject.Value >= _max)
{
_maximumValues.Clear();
_max = myObject.Value;
}
else
{
RemoveValuesSmallerThan(myObject.Value);
}
_maximumValues.Add(myObject);
RemoveObservationsBefore(myObject.Date.AddYears(-1));
_max = _maximumValues[0].Value;
}
private void RemoveObservationsBefore(DateTime targetDate)
{
var toRemoveFromFront = 0;
while (_maximumValues[toRemoveFromFront].Date < targetDate && toRemoveFromFront <= maximumValues3.Count -1)
{
toRemoveFromFront++;
}
_maximumValues.RemoveRange(0, toRemoveFromFront);
}
private void RemoveValuesSmallerThan(double targetValue)
{
var maxEntry = _maximumValues.Count - 1;
var toRemoveFromBack = 0;
while (toRemoveFromBack <= maxEntry && _maximumValues[maxEntry - toRemoveFromBack].Value <= targetValue)
{
toRemoveFromBack++;
}
_maximumValues.RemoveRange(maxEntry - toRemoveFromBack + 1, toRemoveFromBack);
}
}
It can be used as follows:
public static MyObject[] GetTestObjects_MaxSlidingWindow()
{
var rnd = new Random();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
var result = new List<MyObject>();
var maxSlidingWindow = new MaxSlidingWindow();
for (int i = 0; i < 50000; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
var myObject = new MyObject()
{
Value = rnd.NextDouble(),
Date = date.AddMinutes(15 * i)
};
maxSlidingWindow.Add(myObject);
var max = maxSlidingWindow.Max;
result.Add(new MyObject { Date = myObject.Date, Value = myObject.Value / max });
}
return result.ToArray();
}
See the relative timings below - above solution is slightly faster (timed over 10 million runs), but barely noticeable:
Relative timings

How to get the real time of a midi event using Naudio

I am trying to read midi notes and extract the real time of each one of them using NAudio library
I wrote this code but it isn't calculating the time correctly, I used a formula that i found here
((note.AbsTime - lastTempoEvent.AbsTime) / midi.ticksPerQuarterNote) * tempo + lastTempoEvent.RealTime
The code:
var strictMode = false;
var mf = new MidiFile("Assets/Audios/Evangelion Midi.mid", strictMode);
mf.Events.MidiFileType = 0;
List<MidiEvent> midiNotes = new List<MidiEvent>();
List<TempoEvent> tempoEvents = new List<TempoEvent>();
for (int n = 0; n < mf.Tracks; n++)
{
foreach (var midiEvent in mf.Events[n])
{
if (!MidiEvent.IsNoteOff(midiEvent))
{
midiNotes.Add(midiEvent);
TempoEvent tempoE;
try { tempoE = (TempoEvent)midiEvent; tempoEvents.Add(tempoE);
Debug.Log("Absolute Time " + tempoE.AbsoluteTime);
}
catch { }
}
}
}
notesArray = midiNotes.ToArray();
tempoEventsArr = tempoEvents.ToArray();
eventsTimesArr = new float[notesArray.Length];
eventsTimesArr[0] = 0;
for (int i = 1; i < notesArray.Length; i++)
{
((notesArray[i].AbsoluteTime - tempoEventsArr[tempoEventsArr.Length - 1].AbsoluteTime) / mf.DeltaTicksPerQuarterNote)
* tempoEventsArr[tempoEventsArr.Length - 1].MicrosecondsPerQuarterNote + eventsTimesArr[i-1];
}
I got these values which are clearly not correct
Does anyone where I am wrong?
It is really good to see someone into MIDI here.
The note.AbsTime - lastTempoEvent.AbsTime part in the referenced code is implemented incorrectly on your side.
The lastTempoEvent variable in this code can not mean the last tempo change in the midi file (as you've implemented it using notesArray[i].AbsoluteTime - tempoEventsArr[tempoEventsArr.Length - 1].AbsoluteTime).
What the referenced code is trying to do is to get the tempo at the time of the current note, (probably by storing the last appeared tempo change event in this variable) while your code is subtracting the absolute time of the latest tempo change in the whole midi file. This is the root cause of the negative numbers (if there are any tempo changes after the current note).
Side note: I also recommend keeping the timings of note-off events. How do you close a note if you don't know when it is released?
Try this. I tested it and it works. Please read the inline comments carefully.
Be safe.
static void CalculateMidiRealTimes()
{
var strictMode = false;
var mf = new MidiFile("C:\\Windows\\Media\\onestop.mid", strictMode);
mf.Events.MidiFileType = 0;
// Have just one collection for both non-note-off and tempo change events
List<MidiEvent> midiEvents = new List<MidiEvent>();
for (int n = 0; n < mf.Tracks; n++)
{
foreach (var midiEvent in mf.Events[n])
{
if (!MidiEvent.IsNoteOff(midiEvent))
{
midiEvents.Add(midiEvent);
// Instead of causing stack unwinding with try/catch,
// we just test if the event is of type TempoEvent
if (midiEvent is TempoEvent)
{
Debug.Write("Absolute Time " + (midiEvent as TempoEvent).AbsoluteTime);
}
}
}
}
// Now we have only one collection of both non-note-off and tempo events
// so we cannot be sure of the size of the time values array.
// Just employ a List<float>
List<float> eventsTimesArr = new List<float>();
// we introduce this variable to keep track of the tempo changes
// during play, which affects the timing of all the notes coming
// after it.
TempoEvent lastTempoChange = null;
for (int i = 0; i < midiEvents.Count; i++)
{
MidiEvent midiEvent = midiEvents[i];
TempoEvent tempoEvent = midiEvent as TempoEvent;
if (tempoEvent != null)
{
lastTempoChange = tempoEvent;
// Remove the tempo event to make events and timings match - index-wise
// Do not add to the eventTimes
midiEvents.RemoveAt(i);
i--;
continue;
}
if (lastTempoChange == null)
{
// If we haven't come accross a tempo change yet,
// set the time to zero.
eventsTimesArr.Add(0);
continue;
}
// This is the correct formula for calculating the real time of the event
// in microseconds:
var realTimeValue =
((midiEvent.AbsoluteTime - lastTempoChange.AbsoluteTime) / mf.DeltaTicksPerQuarterNote)
*
lastTempoChange.MicrosecondsPerQuarterNote + eventsTimesArr[eventsTimesArr.Count - 1];
// Add the time to the collection.
eventsTimesArr.Add(realTimeValue);
Debug.WriteLine("Time for {0} is: {1}", midiEvents.ToString(), realTimeValue);
}
}
EDIT:
The division while calculating the real times was an int/float which resulted in zero when the ticks between events are smaller than delta ticks per quarter note.
Here is the correct way to calculate the values using the numeric type decimal which has the best precision.
The midi song onestop.mid İS 4:08 (248 seconds) long and our final event real time is 247.3594906770833
static void CalculateMidiRealTimes()
{
var strictMode = false;
var mf = new MidiFile("C:\\Windows\\Media\\onestop.mid", strictMode);
mf.Events.MidiFileType = 0;
// Have just one collection for both non-note-off and tempo change events
List<MidiEvent> midiEvents = new List<MidiEvent>();
for (int n = 0; n < mf.Tracks; n++)
{
foreach (var midiEvent in mf.Events[n])
{
if (!MidiEvent.IsNoteOff(midiEvent))
{
midiEvents.Add(midiEvent);
// Instead of causing stack unwinding with try/catch,
// we just test if the event is of type TempoEvent
if (midiEvent is TempoEvent)
{
Debug.Write("Absolute Time " + (midiEvent as TempoEvent).AbsoluteTime);
}
}
}
}
// Switch to decimal from float.
// decimal has 28-29 digits percision
// while float has only 6-9
// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types
// Now we have only one collection of both non-note-off and tempo events
// so we cannot be sure of the size of the time values array.
// Just employ a List<float>
List<decimal> eventsTimesArr = new List<decimal>();
// Keep track of the last absolute time and last real time because
// tempo events also can occur "between" events
// which can cause incorrect times when calculated using AbsoluteTime
decimal lastRealTime = 0m;
decimal lastAbsoluteTime = 0m;
// instead of keeping the tempo event itself, and
// instead of multiplying every time, just keep
// the current value for microseconds per tick
decimal currentMicroSecondsPerTick = 0m;
for (int i = 0; i < midiEvents.Count; i++)
{
MidiEvent midiEvent = midiEvents[i];
TempoEvent tempoEvent = midiEvent as TempoEvent;
// Just append to last real time the microseconds passed
// since the last event (DeltaTime * MicroSecondsPerTick
if (midiEvent.AbsoluteTime > lastAbsoluteTime)
{
lastRealTime += ((decimal)midiEvent.AbsoluteTime - lastAbsoluteTime) * currentMicroSecondsPerTick;
}
lastAbsoluteTime = midiEvent.AbsoluteTime;
if (tempoEvent != null)
{
// Recalculate microseconds per tick
currentMicroSecondsPerTick = (decimal)tempoEvent.MicrosecondsPerQuarterNote / (decimal)mf.DeltaTicksPerQuarterNote;
// Remove the tempo event to make events and timings match - index-wise
// Do not add to the eventTimes
midiEvents.RemoveAt(i);
i--;
continue;
}
// Add the time to the collection.
eventsTimesArr.Add(lastRealTime);
Debug.WriteLine("Time for {0} is: {1}", midiEvent, lastRealTime / 1000000m);
}
}

Why won't this loop increment a variable?

I am trying to do a calculation which involves a do while loop in c#
I want to execute the loop and minus the quantum from the remainingTime array and assign the new value to timeelapsed.
My loop should execute while timeelapsed is > 0 and stop once it hits 0
However, the loop does not carry out the - quantum part of the code, i don't understand why.
static void Main(string[] args)
{
int quantum, noOfProcesses, processNumber = 0; // noOfprocesses variable is to determine array sizes, processNumber tells user what process number they input
int timeelapsed, count = 0;
Console.WriteLine("Please enter the number of processes:");
noOfProcesses = Convert.ToInt32(Console.ReadLine()); // allows numerical input
int[] bursttime = new int[noOfProcesses];
int[] arrivalTime = new int[noOfProcesses];
int[] remainingTime = new int[noOfProcesses];
for (int i = 0; i < noOfProcesses; i++)
{
Console.WriteLine("Enter Burst time for process #" + processNumber);
bursttime[i] = Convert.ToInt32(Console.ReadLine());
processNumber++;
}
processNumber = 1; // resets the process number for user interface
for (int i = 0; i < noOfProcesses; i++)
{
Console.WriteLine("Enter arrival time for process #" + processNumber);
arrivalTime[i] = Convert.ToInt32(Console.ReadLine());
processNumber++;
}
Console.WriteLine("Enter the time quantum:");
quantum = Convert.ToInt32(Console.ReadLine()); // allows numerical input
// calculations
processNumber = 0;
if (count <= noOfProcesses)
{
// remainingTime[0] = bursttime[0] + arrivalTime[0]; // burst + arrival = remaining time
for (int i = 0; i < noOfProcesses; i++)
{
do
{
remainingTime[i] = bursttime[i] + arrivalTime[i]; // burst + arrival = remaining time
timeelapsed = remainingTime[i] - quantum; // - the quantum gives whats remaining
timeelapsed = timeelapsed - quantum;
}
while (timeelapsed > 0);
Console.WriteLine("#" + processNumber + " Time taken: " + timeelapsed + "ms");
processNumber++;
count++;
//timeelapsed = bursttime[i] + arrivalTime[i];
}
}
// Console.WriteLine(arrivalTime[j]);
Console.ReadLine();
}
Please note that the noOfProcesses and processNumber variables are defined in the program i don't believe these are causing the issue.
All you are doing in this loop is recalculating the same values over and over again:
do {
remainingTime[i] = bursttime[i] + arrivalTime[i]; // burst + arrival = remaining time
timeelapsed = remainingTime[i] - quantum; // - the quantum gives whats remaining
timeelapsed = timeelapsed - quantum;
} while (timeelapsed > 0);
For example, you might see this if you use real numbers:
do {
remainingTime = 100 + 100;
timeelapsed = 200 - 10
timeelapsed = 190 - 10;
} while (timeelapsed > 0);
The loop never ends. I'm not sure exactly what your goal is, but you probably need to keep a running total, like:
remainingTime[i] += bursttime[i] + arrivalTime[i];
Or
remainingTime[i] -= bursttime[i] + arrivalTime[i];
This is really debugging and tough to decide without having the code in front of me. I'm assuming that the 'quantum' code is the section in the do part of the loop. In which case, the only reason why it isn't running is one of the three:
noOfProcesses is less than 1
timeelapsed is <= 0
It is getting to the first iteration of the loop but an exception is being thrown.
Since you're using c#, and hopefully VS, you should use the debugger and step through your program.

Get set of random numbers from input List having fixed sum using C#

I am looking for a C# algorithm that would give me a set of random integers from input List, such that the sum of obtained random integers is N.
For example:
If the list is {1,2,3,4,5,6...100} and N is 20, then the algorithm should return a set of random numbers like {5,6,9} or {9,11} or {1,2,3,4,10} etc.
Note that the count of integers in result set need not be fixed. Also, the input list can have duplicate integers. Performance is one of my priority as the input list can be large (around 1000 integers) and I need to randomize about 2-3 times in a single web request. I am flexible with not sticking to List as datatype if there is a performance issue with Lists.
I have tried below method which is very rudimentary and performance inefficient:
Use the Random class to get a random index from the input list
Get the integer from input list present at index obtained in #1. Lets call this integer X.
Sum = Sum + X.
Remove X from input list so that it does not get selected next.
If Sum is less than required total N, add X to outputList and go back to #1.
If the Sum is more than required total N, reinitialize everything and restart the process.
If the Sum is equal to required total N, return outputList
while(!reachedTotal)
{
//Initialize everything
inputList.AddRange(originalInputList);
outputList = new List<int>();
while (!reachedTotal)
{
random = r.Next(inputList.Count);
sum += inputList.ElementAt(random);
if(sum<N)
{
outputList.Add(inputList.ElementAt(random));
inputList.RemoveAt(random);
}
else if(sum>N)
break;
else
reachedTotal = true;
}
}
This is a stochastical approach that gives you a solution within a 10% range of N - Assuming one exists
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace StackOverflowSnippets
{
class Program
{
static void Main(string[] args)
{
// ----------------------------------------------------------------------------------
// The code you are interested in starts below this line
const Int32 N = 100;
Int32 nLowerBound = (90 * N) / 100; Int32 nUpperBound = (110 * N) / 100;
Random rnd = new Random();
Int32 runningSum = 0;
Int32 nextIndex = 0;
List<Int32> inputList = GenerateRandomList( /* entries = */ 1000);
List<Int32> o = new List<Int32>();
while (runningSum < nLowerBound)
{
nextIndex = rnd.Next(inputList.Count); if (nUpperBound < (runningSum + inputList[nextIndex])) continue;
runningSum += inputList[nextIndex];
o.Add(inputList[nextIndex]);
inputList.RemoveAt(nextIndex);
}
// The code you are interested in ends above this line
// ----------------------------------------------------------------------------------
StringBuilder b = new StringBuilder();
for(Int32 i = 0; i < o.Count;i++)
{
if (b.Length != 0) b.Append(",");
b.Append(o[i].ToString());
}
Console.WriteLine("Exact N : " + N);
Console.WriteLine("Upper Bound: " + nUpperBound);
Console.WriteLine("Lower Bound: " + nLowerBound);
Console.WriteLine();
Console.WriteLine("sum(" + b.ToString() + ")=" + GetSum(o).ToString());
Console.ReadLine();
}
// -------------------------------------------------------------------
#region Helper methods
private static object GetSum(List<int> o)
{
Int32 sum = 0;
foreach (Int32 i in o) sum += i;
return sum;
}
private static List<Int32> GenerateRandomList(Int32 entries)
{
List<Int32> l = new List<Int32>();
for(Int32 i = 1; i < entries; i++)
{
l.Add(i);
}
return l;
}
#endregion
}
}
EDIT
Forgot to remove the element from the input-list so it cannot be selected twice
Fixed the 'remove element' insertion

Simple File I/O - Read a new line

I have a file that stores exam scores for a class of students. I am trying to write a program that successfully opens the file, reads in the exam scores, finds the average score, the highest score, and the lowest score, and prints these out. The average score should be printed with 2 digits after the decimal point.
This is what I have so far:
static void Main()
{
string myData = "";
int temp = 0;
int max = 0;
int min = 0;
double average = 0;
StreamReader fileReader = new StreamReader("data.txt");
do
{
myData = fileReader.ReadLine();
if (myData != null)
{
max = int.Parse(myData);
temp = int.Parse(myData);
if (temp > max)
temp = max;
}
} while (myData != null);
fileReader.Close();
Console.ReadLine();
}//End Main()
I don't exactly know how to proceed. How do I read in a new line and assign it to temp? I don't think I'm doing it right.
Here is one way that will make your teacher sad :P
static void Main(string[] args)
{
var file = new StreamReader("scores.txt");
var split = file.ReadToEnd().Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
IEnumerable<int> ints = split.Select(x => int.Parse(x));
Console.WriteLine("Total Scores:" + ints.Count());
Console.WriteLine("Max:" + ints.Max());
Console.WriteLine("Min:" + ints.Min());
Console.WriteLine("Average:" + ints.Average().ToString("0.00"));
Console.ReadLine();
}
While this is technically correct, this is counter-productive to understanding algorithms (however basic they may be), and I suggest you look at the other answers. But this demonstrates how versatile the .NET framework is.
<3 LINQ
The first error I found is that max will always be exactly equal to temp because you assign them the same value, so this if will never be true:
max = int.Parse(myData);
temp = int.Parse(myData);
if (temp > max)
Not bad. Here's some pseudocode:
File.ReadAllLines to get an array of strings (call this lines)
Foreach line in lines
Parse the double (? is each line guaranteed to be an int?)
if max < parsed, max = parsed
if min > parsed, min = parsed
sum += parsed
Print out min, max, (sum / lines.count).ToString("000.00")
Assumes the file looks like:
25
12
33.5
100
75
...
Since this is homework, I'll just give you some clues.
Right now, you're setting "max" each time through the loop. Try only setting temp, and see what happens. You might want to consider defaulting max (when you create it) to a very small number instead of 0.
Also, you'll need to do something similar for "min", but default it to a very large number.
int max = int.MinValue;
int min = int.MaxValue;
In order to get the average, you'll need to have a sum and a count, and keep track of those. Then, at the end, use a double to compute the average, and print. To get 2 decimal places, you can use average.ToString("N") - the "N" format does a nicely formatted number with 2 decimal places by default.
How about you separate out the processes? Do the reading and fill a list of integers with the contents of the file. Then perform the processing for min / max and average later on.
Isolating issues help you focus on them. I like to call this noise reduction. As a contractor I get to work on a lot of messy code and one of the reasons they are hard to understand is that too much is going on at the same time. If you simplify whats going on the code almost writes itself. This is also called Separation of Concerns. This is a very important programming principle.
After you've isolated the issues and got the code working you can then try to put it all together again so the process is more efficient (if you do it inline with the file reading then you will only hold one line in memory at a time).
For starters, you need to get rid of the line "max = int.Parse(myData)". Otherwise, you'll keep overwriting max with the current value.
I take it that this is the general gist of your assignment. You should be leery of copying this code.
double count = 0.0;
double min = double.MaxValue;
double max = double.MinValue;
double total = 0.0;
using(StreamReader sr = new StreamReader(#"c:\data.txt"))
{
while (!sr.EndOfStream)
{
String line = sr.ReadLine();
double value = double.Parse(line.Trim());
if (value < min) min = value;
if (value > max) max = value;
total += value;
count++;
}
}
Console.WriteLine("Min: {0}", min);
Console.WriteLine("Max: {0}", max);
Console.WriteLine("Avg: {0}", (total / count).ToString("0.00"));
Console.ReadLine();
static void Main(string[] args)
{
const string filename = #"data.txt";
bool first = true;
int min=0, max=0, total=0;
var lines = File.ReadAllLines(filename);
foreach (var line in lines)
{
var score = int.Parse(line.Trim());
if (first)
{
min = max = total = score;
first = false;
continue;
}
if (score < min)
min = score;
if (score > max)
max = score;
total += score;
}
if (first)
{
Console.WriteLine("no input");
return;
}
var average = (double)total/lines.Length;
Console.WriteLine(string.Format("Min: {0}, Max: {1}, Average: {2:F2}", min, max, average));
}
Hey thanks to everybody who helped out and offered suggestions, here is my final code implementation (without using arrays)
using System;
using System.IO;
class Program
{
static void Main()
{
string line = "";
int value = 0;
int max = 0;
int min = 100;
int total = 0;
double count = 0.0;
double average = 0;
StreamReader fileReader = new StreamReader(#"data.txt");
do
{
line = fileReader.ReadLine();
if (line != null)
{
value = int.Parse(line);
if (value > max)
max = value;
if (value < min)
min = value;
total += value;
count++;
}
} while (line != null);
average = total / count;
Console.WriteLine("Max: {0}", max);
Console.WriteLine("Min: {0}", min);
Console.WriteLine("Avg: {0:f2}", average);
Console.ReadLine();
}//End Main()
}//End class Program

Categories