【问题标题】:How does the playback duration of sound files change when changing pitch in XNA/Monogame?在 XNA/Monogame 中更改音高时声音文件的播放时长如何变化?
【发布时间】:2016-01-16 14:19:35
【问题描述】:

使用 XNA 中的 SoundEffects 类(或 XNA 的替代实现,如 MonoGame 或 FNA),声音效果的播放持续时间等于存储在以下位置的声音文件的长度:

SoundEffect.Duration

但是,当播放时更改音高(在 1 和 -1 之间)时,这也会更改播放的有效持续时间(例如,较低的音高 = 较慢的播放),从而使 SoundEffect.Duration 中的值无用。

根据播放音高计算持续时间的数学表达式是什么?

我想要做的是平滑淡出声音效果,例如他们播放的最后一秒。但为了在最后一秒线性降低音量,我需要知道播放何时结束,因此它的(音高调制,实际)持续时间。

【问题讨论】:

    标签: audio xna xna-4.0 playback monogame


    【解决方案1】:

    我为此编写了一个扩展方法,它不是 100% 准确,但非常接近:

    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));
    }
    

    背景

    我也很不幸地找到了这背后的“真正”数学,所以我写了一个这样的小函数:

    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
    

    使用这些数据,我构建了一个带有多项式趋势线的图表,并在 excel 中显示了公式,这是您在 GetEstimatedDuration 中看到的计算。 然后我推进了我的 DurationTest 进行比较(上面的注释行) - 输出:

    // 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
    

    我希望这对您有所帮助,请根据自己的需要和比较随意使用这些方法。

    【讨论】:

    • 哇,没想到会这么复杂。我假设了一个简单的方程,但不是多项式拟合;-) 我会尽快检查这个!
    • 嗯,这只是我想出来的解决方案 :) - 也许其他人可以启发我们
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多