在Android中生成强度不等的振动模式的算法?

时间:2013-12-27 22:10:26

标签: java android algorithm android-vibration

我正在尝试以编程方式生成Android振动模式,其中“微脉冲”打开和关闭,以控制振动对最终用户的强烈感受。这是我在一些类似主题中推荐的解决方案,API的问题是没有提供控制振动强度的接口(因为硬件的功能如我所知)。

然而,生成这些模式的算法似乎只是暗示,但没有发布实际的算法。

我想做的是,如果输入强度介于0.0f和1.0f之间,则按照以下模式生成数组:

(zero intensity)
[20,0]

[9,1,9,1]
...

[3,1,3,1,3,1,3,1,3,1]

[2,1,2,1,2,1,2,1,2,1,2,1,2]

(half intensity)
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]

[1,2,1,2,1,2,1,2,1,2,1,2,1,1]

[1,3,1,3,1,3,1,3,1,3]
...

[1,9,1,9]

(full intensity)
[0,20]

编写此类算法的任何帮助(或建议更好的策略来实现相同的目标)?

编辑:我已经添加了100个声望的奖金:)

3 个答案:

答案 0 :(得分:11)

在看了一会儿的问题,而不是在数学上不是很有天赋之后,我想出了一个过于简化的算法(与我在Dithermaster指向那个方向后发现的一些PWM公式相比)。我做的几个假设首先是短脉冲宽度始终为1,而长脉冲宽度是1和振动持续时间之间的整数。我还假设长脉冲宽度是振动强度的线性函数。特别是,后一种假设并不准确。我猜这个函数应该更像是一个分贝计算(振动的“强度”类似于声音的“响度”)。

发布我的简化解决方案,以防其他人在此处结束。这对于我正在使用它的应用程序足够接近,但我仍然想要更好的东西。如果有人发布替代答案,我会测试并接受它,如果它更好。

public long[] genVibratorPattern( float intensity, long duration )
{
    float dutyCycle = Math.abs( ( intensity * 2.0f ) - 1.0f );
    long hWidth = (long) ( dutyCycle * ( duration - 1 ) ) + 1;
    long lWidth = dutyCycle == 1.0f ? 0 : 1;

    int pulseCount = (int) ( 2.0f * ( (float) duration / (float) ( hWidth + lWidth ) ) );
    long[] pattern = new long[ pulseCount ];

    for( int i = 0; i < pulseCount; i++ )
    {
        pattern[i] = intensity < 0.5f ? ( i % 2 == 0 ? hWidth : lWidth ) : ( i % 2 == 0 ? lWidth : hWidth );
    }

    return pattern;
}

答案 1 :(得分:7)

假设总持续时间为n,而不是20.您的函数执行两项强度i更改:

  • 首先,k(i),周期数会发生变化。它从k(0) = 1开始,在k(0.5) = n/2处达到峰值,然后降至k(1) = 1
  • 其次,每对中的时间开/关的比率r(i)发生变化。如果我们有一个周期[a, b],时间为a且时间为b,则为r(i)*a = b。按照您的示例,我们有r(0) = 0r(0.5) = 1,然后是渐近r(1) = infinity
  • 的渐近线

有许多功能可以匹配k(i)r(i),但让我们坚持使用简单的功能:

k(i) = (int) (n/2 - (n-2)*|i - 0.5|)             r(i) = 1 / (1.000001 - i) - 1

其中|x|表示x的绝对值。我还在1的分母中用1.000001代替r,这样我们就不必处​​理被零除错误。

现在,如果周期需要总和到n,那么任何一个周期[a, b]的长度都是n/k(i)。由于我们也有r(i)*a = b,因此

a = n/(k*(1+r))                      b = r*a

要形成强度为i的数组,我们只需重复[a, b] k次。以下是n = 20的输出示例:

Intensity: 0.00, Timings: 20.0, 0.0
Intensity: 0.05, Timings: 9.5, 0.5, 9.5, 0.5
Intensity: 0.10, Timings: 6.0, 0.7, 6.0, 0.7, 6.0, 0.7
Intensity: 0.15, Timings: 4.3, 0.7, 4.3, 0.7, 4.3, 0.7, 4.3, 0.7
Intensity: 0.20, Timings: 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8
Intensity: 0.25, Timings: 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8
Intensity: 0.30, Timings: 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9
Intensity: 0.35, Timings: 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9
Intensity: 0.40, Timings: 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9
Intensity: 0.45, Timings: 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9
Intensity: 0.50, Timings: 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0
Intensity: 0.55, Timings: 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1, 0.9, 1.1
Intensity: 0.60, Timings: 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3, 0.9, 1.3
Intensity: 0.65, Timings: 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6, 0.9, 1.6
Intensity: 0.70, Timings: 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0, 0.9, 2.0
Intensity: 0.75, Timings: 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5, 0.8, 2.5
Intensity: 0.80, Timings: 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2, 0.8, 3.2
Intensity: 0.85, Timings: 0.8, 4.2, 0.8, 4.2, 0.8, 4.2, 0.8, 4.2
Intensity: 0.90, Timings: 0.7, 6.0, 0.7, 6.0, 0.7, 6.0
Intensity: 0.95, Timings: 0.5, 9.5, 0.5, 9.5
Intensity: 1.00, Timings: 0.0, 20.0

这是伪劣的代码:

    public void Test()
    {
        foreach (var intensity in Enumerable.Range(0, 20 + 1).Select(i => i/20f))
        {
            var cycle = new List<float> {a(intensity), b(intensity)};
            var timings = Enumerable.Repeat(cycle, k(intensity)).SelectMany(timing => timing).ToArray();

            SDebug.WriteLine(
                String.Format("Intensity: {0,2:N2}, Timings: ", intensity) + 
                String.Join(", ", timings.Select(timing => String.Format("{0,2:N1}", timing))));
        }
    }

    private static float r(float i)
    {
        return 1f/(1.000001f - i) - 1f;
    }

    private static int k(float i)
    {
        return Mathf.CeilToInt(10 - 18*Mathf.Abs(i - 0.5f));
    }

    private static float a(float i)
    {
        return 20/(k(i)*(1 + r(i)));
    }

    private static float b(float i)
    {
        return r(i)*a(i);
    }

从这里做的最好的事情是功能r(i)。但是,如果可以,请先将第一个和最后一个时间放宽为[n, 1][1, n],这样可以避免使用渐近线。

答案 2 :(得分:3)

三个想法:

  1. 这是一种PWM。随着强度上升,“关闭”变小,“开”变大。

  2. 这似乎是一种抖动,就像Ordered Dither一样。但不是2D,而只是1D。

  3. 这看起来像数字微分分析仪或Bresenham's line algorithm

  4. 这些想法的某些组合应该可以解决问题。