4

Using the SoundEffects class in XNA (or alternative implementations of XNA like MonoGame or FNA), a sound effect has a certain playback duration equal to the length of the sound file which is stored in:

SoundEffect.Duration

However, when the pitch is changed for playback (between 1 and -1), this also changes the effective duration of playback (e.g. lower pitch = slower playback) redering the value in SoundEffect.Duration useless.

What is the mathematical expression to calculate the duration depending on the playback pitch?

What I am trying to do is to smoothly fade out sound effects during e.g. the last second of their playback. But in order to decrease their volume linearly in this last second i need to know when the playback acually ends hence its (pitch-modulated, actual) duration.

ares_games
  • 1,019
  • 2
  • 15
  • 32

1 Answers1

2

I worte an extension method for this which is not 100% accurate but pretty close:

public static TimeSpan GetEstimatedDuration(this SoundEffect sfx, float pitch)
{
    return TimeSpan.FromMilliseconds(sfx.Duration.TotalMilliseconds * (0.2514 * Math.Pow(pitch, 2) - 0.74 * pitch + 1));
}

Background

I was also unlucky to find the 'real' maths behind this so i wrote a little function like this:

private void DurationTest(string sfxAssetName)
    {
        for (float pitch = -1; pitch <= 1; pitch += 0.5f)
        {
            var sfx = this.Content.Load<SoundEffect>(sfxAssetName);
            using (var instance = sfx.CreateInstance())
            {
                instance.Pitch = pitch;
                //var estimatedDuration = sfx.GetEstimatedDuration(pitch);
                instance.Play();
                var sw = System.Diagnostics.Stopwatch.StartNew();
                while (instance.State == SoundState.Playing)
                { }
                sw.Stop();
                var duration = sw.Elapsed;
                System.Diagnostics.Debug.WriteLine("sfx={0} | pitch={1:0.00} | duration={2:0.00}secs", sfxAssetName, pitch, duration.TotalSeconds);
                //System.Diagnostics.Debug.WriteLine("sfx={0} | pitch={1:0.00} | estimated={2:0.00}secs | actual={3:0.00}secs", sfxAssetName, pitch, estimatedDuration.TotalSeconds, duration.TotalSeconds);
            }
        }
    }
// output
sfx=bombfuse | pitch=-1.00 | duration=3.89secs
sfx=bombfuse | pitch=-0.50 | duration=2.75secs
sfx=bombfuse | pitch= 0.00 | duration=1.95secs
sfx=bombfuse | pitch= 0.50 | duration=1.38secs
sfx=bombfuse | pitch= 1.00 | duration=0.98secs

With this data i build a chart with polynomial trendline and displayed formula in excel which is the calculation you see in GetEstimatedDuration. Then I advanced my DurationTest for comparision (commented lines above) - which outputs this:

// output bombfuse (short)
sfx=bombfuse | pitch=-1.00 | estimated=3.88secs | actual=3.89secs
sfx=bombfuse | pitch=-0.50 | estimated=2.79secs | actual=2.76secs
sfx=bombfuse | pitch= 0.00 | estimated=1.95secs | actual=1.95secs
sfx=bombfuse | pitch= 0.50 | estimated=1.35secs | actual=1.38secs
sfx=bombfuse | pitch= 1.00 | estimated=1.00secs | actual=0.98secs
// output dreiklang (long)
sfx=dreiklang | pitch=-1.00 | estimated=24.67secs | actual=24.77secs
sfx=dreiklang | pitch=-0.50 | estimated=17.75secs | actual=17.52secs
sfx=dreiklang | pitch= 0.00 | estimated=12.39secs | actual=12.39secs
sfx=dreiklang | pitch= 0.50 | estimated= 8.58secs | actual= 8.76secs
sfx=dreiklang | pitch= 1.00 | estimated= 6.33secs | actual= 6.20secs

I hope this is somewhat helpful for you, feel free to use the methods for your own needs and comparisions.

Bashn
  • 314
  • 1
  • 6
  • Wow, I did not expect this to be that complex. I assumed a somewhat simple equation but not a polynomial fit ;-) I will check this asap! – ares_games Jan 17 '16 at 23:02
  • Well, thats just the solution I fiddled out :) - maybe someone else can enlighten us – Bashn Jan 18 '16 at 08:37