NAudio playing a sine wave for x milliseconds using C# - c#

I am using NAudio to play a sinewave of a given frequency as in the blog post Playback of Sine Wave in NAudio. I just want the sound to play() for x milliseconds and then stop.
I tried a thread.sleep, but the sound stops straightaway. I tried a timer, but when the WaveOut is disposed there is a cross-thread exception.
I tried this code, but when I call beep the program freezes.
public class Beep
{
public Beep(int freq, int ms)
{
SineWaveProvider32 sineWaveProvider = new SineWaveProvider32();
sineWaveProvider.Amplitude = 0.25f;
sineWaveProvider.Frequency = freq;
NAudio.Wave.WaveOut waveOut = new NAudio.Wave.WaveOut(WaveCallbackInfo.FunctionCallback());
waveOut.Init(sineWaveProvider);
waveOut.Play();
Thread.Sleep(ms);
waveOut.Stop();
waveOut.Dispose();
}
}
public class SineWaveProvider32 : NAudio.Wave.WaveProvider32
{
int sample;
public SineWaveProvider32()
{
Frequency = 1000;
Amplitude = 0.25f; // Let's not hurt our ears
}
public float Frequency { get; set; }
public float Amplitude { get; set; }
public override int Read(float[] buffer, int offset, int sampleCount)
{
int sampleRate = WaveFormat.SampleRate;
for (int n = 0; n < sampleCount; n++)
{
buffer[n + offset] = (float)(Amplitude * Math.Sin((2 * Math.PI * sample * Frequency) / sampleRate));
sample++;
if (sample >= sampleRate)
sample = 0;
}
}

The SineWaveProvider32 class doesn't need to indefinitely provide audio. If you want the beep to have a maximum duration of a second (say), then for mono 44.1 kHz, you need to provide 44,100 samples. The Read method should return 0 when it has no more data to supply.
To make your GUI thread not block, you need to get rid of Thread.Sleep, waveOut.Stop and Dispose, and simply start playing the audio (you may find window callbacks more reliable than function).
Then, when the audio has finished playing, you can close and clean up the WaveOut object.

Check out the blog post Pass Variables to a New Thread in C# on how to pass variables to another thread.
I think what you want to do is something like create a thread that plays your sound, create a timer, and start the thread. When the timer expires, kill the thread, and when the thread closes have it do all the cleanup.

Related

Precise timing between instructions in C# [duplicate]

This question already has answers here:
High resolution timer in C#
(5 answers)
Closed 3 years ago.
I want to have a timer with about 5millisecond resolution. but the current Timer in .Net has a resolution of about 50ms.
I could not find any working solution that creates a high resolution timer, although some claim that you can do it in C#.
In regards to the information that the OP was specifically asking about the Timer class which fires events at regular intervals. I have amended this answer, with my old answer below the horizontal rule.
I tested the following code with the Timer class, and it seems like it can get at least within the 14 - 15 millisecond range on my machine. Try it out for yourself and see if you can reproduce this. So sub-50 millisecond response times are possible, but it can't get down to exactly one millisecond.
using System;
using System.Timers;
using System.Diagnostics;
public static class Test
{
public static void Main(String[] args)
{
Timer timer = new Timer();
timer.Interval = 1;
timer.Enabled = true;
Stopwatch sw = Stopwatch.StartNew();
long start = 0;
long end = sw.ElapsedMilliseconds;
timer.Elapsed += (o, e) =>
{
start = end;
end = sw.ElapsedMilliseconds;
Console.WriteLine("{0} milliseconds passed", end - start);
};
Console.ReadLine();
}
}
NB: The following is my old answer, when I thought the OP was talking about timing things. The following is merely useful information with respect to timing the duration of things, but doesn't provide any way of firing events at a regular interval. For that purpose, the Timer class is necessary.
Try using the Stopwatch class within System.Diagnostics: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
You can query it to check if it's high resolution through it's IsHighResolution field. Also, you can check the exact resolution of the Stopwatch:
int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
If you're worried about where this is actually sourced, the documentation seems to imply that it actually internally calls the lower level Win32 functions:
The Stopwatch class assists the manipulation of timing-related
performance counters within managed code. Specifically, the Frequency
field and GetTimestamp method can be used in place of the unmanaged
Win32 APIs QueryPerformanceFrequency and QueryPerformanceCounter.
What about this one?
public class HiResTimer
{
private bool isPerfCounterSupported = false;
private Int64 frequency = 0;
// Windows CE native library with QueryPerformanceCounter().
private const string lib = "coredll.dll";
[DllImport(lib)]
private static extern int QueryPerformanceCounter(ref Int64 count);
[DllImport(lib)]
private static extern int QueryPerformanceFrequency(ref Int64 frequency);
public HiResTimer()
{
// Query the high-resolution timer only if it is supported.
// A returned frequency of 1000 typically indicates that it is not
// supported and is emulated by the OS using the same value that is
// returned by Environment.TickCount.
// A return value of 0 indicates that the performance counter is
// not supported.
int returnVal = QueryPerformanceFrequency(ref frequency);
if (returnVal != 0 && frequency != 1000)
{
// The performance counter is supported.
isPerfCounterSupported = true;
}
else
{
// The performance counter is not supported. Use
// Environment.TickCount instead.
frequency = 1000;
}
}
public Int64 Frequency
{
get
{
return frequency;
}
}
public Int64 Value
{
get
{
Int64 tickCount = 0;
if (isPerfCounterSupported)
{
// Get the value here if the counter is supported.
QueryPerformanceCounter(ref tickCount);
return tickCount;
}
else
{
// Otherwise, use Environment.TickCount.
return (Int64)Environment.TickCount;
}
}
}
static void Main()
{
HiResTimer timer = new HiResTimer();
// This example shows how to use the high-resolution counter to
// time an operation.
// Get counter value before the operation starts.
Int64 counterAtStart = timer.Value;
// Perform an operation that takes a measureable amount of time.
for (int count = 0; count < 10000; count++)
{
count++;
count--;
}
// Get counter value when the operation ends.
Int64 counterAtEnd = timer.Value;
// Get time elapsed in tenths of a millisecond.
Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
Int64 timeElapseInTenthsOfMilliseconds =
(timeElapsedInTicks * 10000) / timer.Frequency;
MessageBox.Show("Time Spent in operation (tenths of ms) "
+ timeElapseInTenthsOfMilliseconds +
"\nCounter Value At Start: " + counterAtStart +
"\nCounter Value At End : " + counterAtEnd +
"\nCounter Frequency : " + timer.Frequency);
}
}
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Here is an implementation based on StopWatch timer
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
It works on all platforms and is high precision wherever StopWatch.IsHighPrecision == true
Its Elapsed event is guaranteed to be non overlapping (which might be important to know, because state changes inside the event handler may be left unprotected against multi threaded access)
Here is how to use it:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don't care and don't want to wait
Here is a benchmark (and a live example):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
The results of setting the timer for 0.5 ms on Windows 10:
It is also worth to mention that:
I had the same precision on mono on Ubuntu.
While playing with the benchmark, the maximum and a very rare deviation I saw was about 0.5 ms
(which probably means nothing, it is not realtime systems, but still worth mentioning)
Stopwatch ticks are not TimeSpan ticks. On that Windows 10 machine
HighResolutionTimer.TickLength is 0.23[ns].
CPU usage of the benchmark is 10% for 0.5ms interval and 0.1% for 200ms interval
Pretty late to the party still might be useful for someone looking for an answer as nothing has been changed for over a decade in the topic.
Background
Any .NET delay instructions would always come down to system clock resolution, the one you set with timeBeginPeriod(). No matter if it's a Thread.Sleep(N), Threading.Timer or Waitable.WaitOne(N). Yet time resolution of DateTime.Now() and System.Diagnostic.Stopwatch is way higher so there is a way of implementing precise timing events known as hot loop. Hot loops yet prone to being threatened badly by OS since those tend to occupy processor core at full. And here's what we do to prevent this:
Surrender thread time quant in a hot loop to other threads once there's no more need for it by calling Thread.Sleep(0) or .WaitOne(0)
Below is a code piece showing simple implementation of high resolution scheduler:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// High resolution scheduler.
/// License: public domain (no restrictions or obligations)
/// Author: Vitaly Vinogradov
/// </summary>
public class HiResScheduler : IDisposable
{
/// <summary>
/// Scheduler would automatically downgrade itself to cold loop (Sleep(1)) when there are no
/// tasks earlier than the treshold.
/// </summary>
public const int HOT_LOOP_TRESHOLD_MS = 16;
protected class Subscriber : IComparable<Subscriber>, IComparable
{
public Action Callback { get; set; }
public double DelayMs { get; set; }
public Subscriber(double delay, Action callback)
{
DelayMs = delay;
Callback = callback;
}
public int CompareTo(Subscriber other)
{
return DelayMs.CompareTo(other.DelayMs);
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return -1;
var other = obj as Subscriber;
if (ReferenceEquals(other, null))
return -1;
return CompareTo(other);
}
}
private Thread _spinner;
private ManualResetEvent _allowed = new ManualResetEvent(false);
private AutoResetEvent _wakeFromColdLoop = new AutoResetEvent(false);
private bool _disposing = false;
private bool _adding = false;
private List<Subscriber> _subscribers = new List<Subscriber>();
private List<Subscriber> _pendingSubscribers = new List<Subscriber>();
public bool IsActive { get { return _allowed.WaitOne(0); } }
public HiResScheduler()
{
_spinner = new Thread(DoSpin);
_spinner.Start();
}
public void Start()
{
_allowed.Set();
}
public void Pause()
{
_allowed.Reset();
}
public void Enqueue(double delayMs, Action callback)
{
lock (_pendingSubscribers)
{
_pendingSubscribers.Add(new Subscriber(delayMs, callback));
_adding = true;
if (delayMs <= HOT_LOOP_TRESHOLD_MS * 2)
_wakeFromColdLoop.Set();
}
}
private void DoSpin(object obj)
{
var sw = new Stopwatch();
sw.Start();
var nextFire = null as Subscriber;
while (!_disposing)
{
_allowed.WaitOne();
if (nextFire != null && sw.Elapsed.TotalMilliseconds >= nextFire?.DelayMs)
{
var diff = sw.Elapsed.TotalMilliseconds;
sw.Restart();
foreach (var item in _subscribers)
item.DelayMs -= diff;
foreach (var item in _subscribers.Where(p => p.DelayMs <= 0).ToList())
{
item.Callback?.Invoke();
_subscribers.Remove(item);
}
nextFire = _subscribers.FirstOrDefault();
}
if (_adding)
lock (_pendingSubscribers)
{
_subscribers.AddRange(_pendingSubscribers);
_pendingSubscribers.Clear();
_subscribers.Sort();
_adding = false;
nextFire = _subscribers.FirstOrDefault();
}
if (nextFire == null || nextFire.DelayMs > HOT_LOOP_TRESHOLD_MS)
_wakeFromColdLoop.WaitOne(1);
else
_wakeFromColdLoop.WaitOne(0);
}
}
public void Dispose()
{
_disposing = true;
}
}
The system clock "ticks" at a constant rate. In order to raise the accuracy of timer dependent function*s, call **timeGetDevCaps* which determines the minimum supported timer resolution.
Then call timeBeginPeriod setting the timer resolution to its minimum.
Caution: By invoking timeBeginPeriod, other timer dependent function can be significantly affected such as the system clock, system power usage, and the scheduler. Thus start your application with timeBeginPeriod and end it with timeEndPeriod
The previous example doesn't work unless the frequency is in milliseconds; the perf timer frequency is rarely in ms.
private static Int64 m_iPerfFrequency = -1;
public static double GetPerfCounter()
{
// see if we need to get the frequency
if (m_iPerfFrequency < 0)
{
if (QueryPerformanceFrequency(out m_iPerfFrequency) == 0)
{
return 0.0;
}
}
Int64 iCount = 0;
if (QueryPerformanceCounter(out iCount) == 0)
{
return 0.0;
}
return (double)iCount / (double)m_iPerfFrequency;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceCounter(out Int64 iCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceFrequency(out Int64 iFrequency);
This returns the perf counter in seconds. The reason you'd use the perf timer is to share a timer with legacy C++ code, or get a more precise timer than the C# StopWatch class.
You could use QueryPerformanceCounter() and QueryPerformanceTimer() as outlined in this article.

Applying a linear fade at a specify position using NAudio

I am making use of NAudio in a C# program I've written.
I want to apply a linear fade at a certain position within a piece of audio I'm working with.
In the NAudio example project is a file called FadeInOutSampleProvider.cs (Cached example) which has BeginFadeIn(double fadeDurationInMilliseconds) and BeginFadeOut(double fadeDurationInMilliseconds) methods.
I've reworked these methods to
BeginFadeOut(double fadeDurationInMilliseconds, double beginFadeAtMilliseconds)
and
BeginFadeOut(double fadeDurationInMilliseconds, double beginFadeAtMilliseconds)
However I'm having difficulty implementing the interval logic for these changes to work.
My first thought was to introduce code in the Read() method. When called it would divide the number of bytes being requested by the sample rate, which would give the number of seconds of audio requested.
I could then keep track of this and when the correct amount of auto data had been read, allow the fade to be applied.
However I'm not getting the numbers in my calculations I would expect to see. I'm sure there's a better way to approach this.
Any help would be very much appreciated.
It sounds like you are working along the right lines. As you say the amount of audio being requested can be calculated by dividing number of samples requested by the sample rate. But you must also take into account channels as well. In a stereo file there are twice as many samples per second as the sample rate.
I've put a very basic code sample of a delayed fade out in a GitHub gist here. There are improvements that could be made such as allowing the fade-out to begin part-way through the audio returned from a call to Read but holpefully this gives you a rough idea of how it can be achieved with a few small modifications to FadeInOutSampleProvider.
The main changes are an extra parameter to BeginFadeOut, that sets two new variables (fadeOutDelaySamples, fadeOutDelayPosition):
/// <summary>
/// Requests that a fade-out begins (will start on the next call to Read)
/// </summary>
/// <param name="fadeDurationInMilliseconds">Duration of fade in milliseconds</param>
public void BeginFadeOut(double fadeAfterMilliseconds, double fadeDurationInMilliseconds)
{
lock (lockObject)
{
fadeSamplePosition = 0;
fadeSampleCount = (int)((fadeDurationInMilliseconds * source.WaveFormat.SampleRate) / 1000);
fadeOutDelaySamples = (int)((fadeAfterMilliseconds * source.WaveFormat.SampleRate) / 1000);
fadeOutDelayPosition = 0;
//fadeState = FadeState.FadingOut;
}
}
Then in the Read method we can keep track of how far into the delay we are, and if so, we can start the fade-out
public int Read(float[] buffer, int offset, int count)
{
int sourceSamplesRead = source.Read(buffer, offset, count);
lock (lockObject)
{
if (fadeOutDelaySamples > 0)
{
fadeOutDelayPosition += sourceSamplesRead / WaveFormat.Channels;
if (fadeOutDelayPosition >= fadeOutDelaySamples)
{
fadeOutDelaySamples = 0;
fadeState = FadeState.FadingOut;
}
}
if (fadeState == FadeState.FadingIn)
{
FadeIn(buffer, offset, sourceSamplesRead);
}
else if (fadeState == FadeState.FadingOut)
{
FadeOut(buffer, offset, sourceSamplesRead);
}
else if (fadeState == FadeState.Silence)
{
ClearBuffer(buffer, offset, count);
}
}
return sourceSamplesRead;
}

XAudio2 OnVoiceProcessingPassStart callback stuttering playback

I'm using XAudio2 with SlimDX and I've managed to get it playing a short (~8second) wav file on loop, however as it approaches the end of the first loop, the audio begins to stutter and the stuttering continues into the next loop, getting worse and worse as time goes on.
I turned on the debug profile and in the output window I get these errors:
XAUDIO2: WARNING: Spent 5.63ms in the OnVoiceProcessingPassStart callback
XAUDIO2: WARNING: Spent 5.60ms in the OnVoiceProcessingPassStart callback
XAUDIO2: WARNING: Spent 5.59ms in the OnVoiceProcessingPassStart callback
XAUDIO2: WARNING: Spent 5.69ms in the OnVoiceProcessingPassStart callback
And these coincide with when the stuttering occurs. I am doing nothing in these callbacks (I haven't even added anything to the events), and yet it's slowing down. I've added my code below for reference:
Wave class for holding the data stream and the buffer:
public class Wave
{
public WaveStream Data { get; private set; }
public AudioBuffer Buffer { get; private set; }
public Wave(string path, bool repeating)
{
Data = new WaveStream(path);
Buffer = new AudioBuffer();
Buffer.AudioBytes = (int)Data.Length;
Buffer.AudioData = Data;
if (repeating)
{
Buffer.Flags = BufferFlags.EndOfStream;
}
else
{
Buffer.Flags = BufferFlags.None;
}
Buffer.PlayBegin = 0;
Buffer.PlayLength = 0;
Buffer.LoopBegin = 0;
Buffer.LoopCount = 100;
Buffer.LoopLength = 0;
}
}
Sound class for holding the XAudio engine and the voices, and to cover adding/removing voices:
public class Sound
{
private XAudio2 audio;
private MasteringVoice master;
private List<SourceVoice> sources;
public Sound()
{
audio = new XAudio2(XAudio2Flags.DebugEngine, ProcessorSpecifier.AnyProcessor);
master = new MasteringVoice(audio);
sources = new List<SourceVoice>();
}
public void AddSound(Wave wave)
{
SlimDX.Multimedia.WaveFormat format = wave.Data.Format;
SourceVoice source = new SourceVoice(audio, format);
source.Start();
source.SubmitSourceBuffer(wave.Buffer);
sources.Add(source);
}
}
And to run it, I use:
Wave wave = new Wave("Music2/untitled.wav", true);
Sound sound = new Sound();
sound.AddSound(wave);
Rather embarrassingly this one's my own fault. I had a D3D buffer being recreated and destroyed every frame that I'd forgotten to change to a dynamic buffer. This was causing my memory usage to balloon to several gigabytes in size, which meant there probably wasn't enough to allocate for the music.
Once I removed the memory leak, the music sounded fine.
I'm not sure if the XAudio2 exception is very well-worded though...

High resolution timer [duplicate]

This question already has answers here:
High resolution timer in C#
(5 answers)
Closed 3 years ago.
I want to have a timer with about 5millisecond resolution. but the current Timer in .Net has a resolution of about 50ms.
I could not find any working solution that creates a high resolution timer, although some claim that you can do it in C#.
In regards to the information that the OP was specifically asking about the Timer class which fires events at regular intervals. I have amended this answer, with my old answer below the horizontal rule.
I tested the following code with the Timer class, and it seems like it can get at least within the 14 - 15 millisecond range on my machine. Try it out for yourself and see if you can reproduce this. So sub-50 millisecond response times are possible, but it can't get down to exactly one millisecond.
using System;
using System.Timers;
using System.Diagnostics;
public static class Test
{
public static void Main(String[] args)
{
Timer timer = new Timer();
timer.Interval = 1;
timer.Enabled = true;
Stopwatch sw = Stopwatch.StartNew();
long start = 0;
long end = sw.ElapsedMilliseconds;
timer.Elapsed += (o, e) =>
{
start = end;
end = sw.ElapsedMilliseconds;
Console.WriteLine("{0} milliseconds passed", end - start);
};
Console.ReadLine();
}
}
NB: The following is my old answer, when I thought the OP was talking about timing things. The following is merely useful information with respect to timing the duration of things, but doesn't provide any way of firing events at a regular interval. For that purpose, the Timer class is necessary.
Try using the Stopwatch class within System.Diagnostics: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
You can query it to check if it's high resolution through it's IsHighResolution field. Also, you can check the exact resolution of the Stopwatch:
int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
If you're worried about where this is actually sourced, the documentation seems to imply that it actually internally calls the lower level Win32 functions:
The Stopwatch class assists the manipulation of timing-related
performance counters within managed code. Specifically, the Frequency
field and GetTimestamp method can be used in place of the unmanaged
Win32 APIs QueryPerformanceFrequency and QueryPerformanceCounter.
What about this one?
public class HiResTimer
{
private bool isPerfCounterSupported = false;
private Int64 frequency = 0;
// Windows CE native library with QueryPerformanceCounter().
private const string lib = "coredll.dll";
[DllImport(lib)]
private static extern int QueryPerformanceCounter(ref Int64 count);
[DllImport(lib)]
private static extern int QueryPerformanceFrequency(ref Int64 frequency);
public HiResTimer()
{
// Query the high-resolution timer only if it is supported.
// A returned frequency of 1000 typically indicates that it is not
// supported and is emulated by the OS using the same value that is
// returned by Environment.TickCount.
// A return value of 0 indicates that the performance counter is
// not supported.
int returnVal = QueryPerformanceFrequency(ref frequency);
if (returnVal != 0 && frequency != 1000)
{
// The performance counter is supported.
isPerfCounterSupported = true;
}
else
{
// The performance counter is not supported. Use
// Environment.TickCount instead.
frequency = 1000;
}
}
public Int64 Frequency
{
get
{
return frequency;
}
}
public Int64 Value
{
get
{
Int64 tickCount = 0;
if (isPerfCounterSupported)
{
// Get the value here if the counter is supported.
QueryPerformanceCounter(ref tickCount);
return tickCount;
}
else
{
// Otherwise, use Environment.TickCount.
return (Int64)Environment.TickCount;
}
}
}
static void Main()
{
HiResTimer timer = new HiResTimer();
// This example shows how to use the high-resolution counter to
// time an operation.
// Get counter value before the operation starts.
Int64 counterAtStart = timer.Value;
// Perform an operation that takes a measureable amount of time.
for (int count = 0; count < 10000; count++)
{
count++;
count--;
}
// Get counter value when the operation ends.
Int64 counterAtEnd = timer.Value;
// Get time elapsed in tenths of a millisecond.
Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
Int64 timeElapseInTenthsOfMilliseconds =
(timeElapsedInTicks * 10000) / timer.Frequency;
MessageBox.Show("Time Spent in operation (tenths of ms) "
+ timeElapseInTenthsOfMilliseconds +
"\nCounter Value At Start: " + counterAtStart +
"\nCounter Value At End : " + counterAtEnd +
"\nCounter Frequency : " + timer.Frequency);
}
}
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Here is an implementation based on StopWatch timer
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
It works on all platforms and is high precision wherever StopWatch.IsHighPrecision == true
Its Elapsed event is guaranteed to be non overlapping (which might be important to know, because state changes inside the event handler may be left unprotected against multi threaded access)
Here is how to use it:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don't care and don't want to wait
Here is a benchmark (and a live example):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
The results of setting the timer for 0.5 ms on Windows 10:
It is also worth to mention that:
I had the same precision on mono on Ubuntu.
While playing with the benchmark, the maximum and a very rare deviation I saw was about 0.5 ms
(which probably means nothing, it is not realtime systems, but still worth mentioning)
Stopwatch ticks are not TimeSpan ticks. On that Windows 10 machine
HighResolutionTimer.TickLength is 0.23[ns].
CPU usage of the benchmark is 10% for 0.5ms interval and 0.1% for 200ms interval
Pretty late to the party still might be useful for someone looking for an answer as nothing has been changed for over a decade in the topic.
Background
Any .NET delay instructions would always come down to system clock resolution, the one you set with timeBeginPeriod(). No matter if it's a Thread.Sleep(N), Threading.Timer or Waitable.WaitOne(N). Yet time resolution of DateTime.Now() and System.Diagnostic.Stopwatch is way higher so there is a way of implementing precise timing events known as hot loop. Hot loops yet prone to being threatened badly by OS since those tend to occupy processor core at full. And here's what we do to prevent this:
Surrender thread time quant in a hot loop to other threads once there's no more need for it by calling Thread.Sleep(0) or .WaitOne(0)
Below is a code piece showing simple implementation of high resolution scheduler:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// High resolution scheduler.
/// License: public domain (no restrictions or obligations)
/// Author: Vitaly Vinogradov
/// </summary>
public class HiResScheduler : IDisposable
{
/// <summary>
/// Scheduler would automatically downgrade itself to cold loop (Sleep(1)) when there are no
/// tasks earlier than the treshold.
/// </summary>
public const int HOT_LOOP_TRESHOLD_MS = 16;
protected class Subscriber : IComparable<Subscriber>, IComparable
{
public Action Callback { get; set; }
public double DelayMs { get; set; }
public Subscriber(double delay, Action callback)
{
DelayMs = delay;
Callback = callback;
}
public int CompareTo(Subscriber other)
{
return DelayMs.CompareTo(other.DelayMs);
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return -1;
var other = obj as Subscriber;
if (ReferenceEquals(other, null))
return -1;
return CompareTo(other);
}
}
private Thread _spinner;
private ManualResetEvent _allowed = new ManualResetEvent(false);
private AutoResetEvent _wakeFromColdLoop = new AutoResetEvent(false);
private bool _disposing = false;
private bool _adding = false;
private List<Subscriber> _subscribers = new List<Subscriber>();
private List<Subscriber> _pendingSubscribers = new List<Subscriber>();
public bool IsActive { get { return _allowed.WaitOne(0); } }
public HiResScheduler()
{
_spinner = new Thread(DoSpin);
_spinner.Start();
}
public void Start()
{
_allowed.Set();
}
public void Pause()
{
_allowed.Reset();
}
public void Enqueue(double delayMs, Action callback)
{
lock (_pendingSubscribers)
{
_pendingSubscribers.Add(new Subscriber(delayMs, callback));
_adding = true;
if (delayMs <= HOT_LOOP_TRESHOLD_MS * 2)
_wakeFromColdLoop.Set();
}
}
private void DoSpin(object obj)
{
var sw = new Stopwatch();
sw.Start();
var nextFire = null as Subscriber;
while (!_disposing)
{
_allowed.WaitOne();
if (nextFire != null && sw.Elapsed.TotalMilliseconds >= nextFire?.DelayMs)
{
var diff = sw.Elapsed.TotalMilliseconds;
sw.Restart();
foreach (var item in _subscribers)
item.DelayMs -= diff;
foreach (var item in _subscribers.Where(p => p.DelayMs <= 0).ToList())
{
item.Callback?.Invoke();
_subscribers.Remove(item);
}
nextFire = _subscribers.FirstOrDefault();
}
if (_adding)
lock (_pendingSubscribers)
{
_subscribers.AddRange(_pendingSubscribers);
_pendingSubscribers.Clear();
_subscribers.Sort();
_adding = false;
nextFire = _subscribers.FirstOrDefault();
}
if (nextFire == null || nextFire.DelayMs > HOT_LOOP_TRESHOLD_MS)
_wakeFromColdLoop.WaitOne(1);
else
_wakeFromColdLoop.WaitOne(0);
}
}
public void Dispose()
{
_disposing = true;
}
}
The system clock "ticks" at a constant rate. In order to raise the accuracy of timer dependent function*s, call **timeGetDevCaps* which determines the minimum supported timer resolution.
Then call timeBeginPeriod setting the timer resolution to its minimum.
Caution: By invoking timeBeginPeriod, other timer dependent function can be significantly affected such as the system clock, system power usage, and the scheduler. Thus start your application with timeBeginPeriod and end it with timeEndPeriod
The previous example doesn't work unless the frequency is in milliseconds; the perf timer frequency is rarely in ms.
private static Int64 m_iPerfFrequency = -1;
public static double GetPerfCounter()
{
// see if we need to get the frequency
if (m_iPerfFrequency < 0)
{
if (QueryPerformanceFrequency(out m_iPerfFrequency) == 0)
{
return 0.0;
}
}
Int64 iCount = 0;
if (QueryPerformanceCounter(out iCount) == 0)
{
return 0.0;
}
return (double)iCount / (double)m_iPerfFrequency;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceCounter(out Int64 iCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceFrequency(out Int64 iFrequency);
This returns the perf counter in seconds. The reason you'd use the perf timer is to share a timer with legacy C++ code, or get a more precise timer than the C# StopWatch class.
You could use QueryPerformanceCounter() and QueryPerformanceTimer() as outlined in this article.

XNA C# SoundEffectInstance - no sound

I'm trying to play SoundEffectInstances of loaded .wav files in my game, but I hear no sound whatsoever.
I have a class "ETSound"; for which each object holds one sound. So one ETSound object might hold the 'menu open' sound, and another might hold the 'tank firing' sound... Etc.
Anyway, the ETSound constructor looks like this:
public ETSound(SoundEffect se, float volume, float pitch, bool looped, int soundPriority) {
soundTemplate = se;
this.volume = volume;
this.pitch = pitch;
this.looped = looped;
if (soundPriority > 0) {
if (soundPriority > 64) soundPriority = 64;
instanceArray = new SoundEffectInstance[soundPriority];
nextInstanceIndex = 0;
for (int i = 0; i < soundPriority; ++i) {
SoundEffectInstance sei = soundTemplate.CreateInstance();
sei.Volume = volume;
sei.Pitch = pitch;
instanceArray[i] = sei;
}
}
}
This basically sets up some parameters, and creates an array of sound effect instances according to the supplied SoundEffect.
Then, I'm calling the Play() function of ETSound:
public void Play() {
if (instanceArray[nextInstanceIndex].State != SoundState.Stopped) instanceArray[nextInstanceIndex].Stop();
instanceArray[nextInstanceIndex].Play();
if (++nextInstanceIndex >= instanceArray.Length) nextInstanceIndex = 0;
}
However, nothing happens. I hear nothing.
Can anyone tell me what's going wrong? Thanks.
Sorry, everyone... Turns out the .wav file I was using to test was corrupt... A tough bug to find, but I got it. Thanks anyway.

Categories