我有这个方法:
public double sineWave(double t)
{
return amplitude==0?0:Math.sin(t * frequency * Math.PI*2 + phase) * amplitude;
}
它由另一个类中的另一个方法调用,以生成简单正弦波的样本,然后将其添加到缓冲区中以发送到声卡。 t
是时候了。由于某种原因,应用程序调用此方法越多,它获得的速度就越慢。它没有任何意义,在15秒之后,它足够慢,可以使用我的CPU的完整内核并使音频断断续续。
我100%肯定这是一段代码,因为如果我用返回0替换它,运行它所花费的时间(用System.nanotime()
测量)是不变的。
为什么会这样?我有什么办法可以解决这个问题吗?
答案 0 :(得分:5)
根据这里的信息 - 虽然不清楚缓冲区有多大,但每次迭代都会增加t。假设您的频率非常高,那么每次迭代都会增加Sin()参数。 检查以确定参数是否不断增加到非常高的值。 快速而肮脏的测试显示Sin性能下降 -
public class SinTest {
public static void main(String args[]) {
long angle = Long.parseLong(args[0]);
long startTime = System.nanoTime();
for(long l=0L; l<=1000000L; l++) {
Math.sin(angle);
}
long estimatedTime = System.nanoTime() - startTime;
System.out.println(estimatedTime);
}
}
$ java SinTest 100000
29181000
$ java SinTest 10000000
138598000
答案 1 :(得分:0)
请不要给出任何要点,因此解决方案将得到@mk的答案:
public double sineWave(double t)
{
final double TAU = Math.PI *2;
double a = t * frequency;
a -= (long)a;
return amplitude==0?0:Math.sin(a * TAU + phase) * amplitude;
}
答案 2 :(得分:0)
我用查找表解决了这个问题:
private static final int LUT_SIZE=1000000;
private static double[] sineLookupTable=new double[(int)(Math.PI*2*LUT_SIZE)];
static{
for(double i=0;i<sineLookupTable.length;i++){
sineLookupTable[(int)i]=Math.sin(i/(double)LUT_SIZE);
}
}
private static double sinLUT(double t){
return sineLookupTable[(int) (((long) Math.floor((t%Math.PI*2)*LUT_SIZE))%sineLookupTable.length)];
}
public double sineWave(double t) {
return amplitude==0?0:sinLUT(t * frequency * Math.PI*2 + phase) * amplitude;
}
它有效......有点问题,唯一的问题是我在高频上会受到很多的歪曲。有什么插值方法可以建议吗?
答案 3 :(得分:0)
Java框架的当前版本将尝试使用数学上完美的2π值而不是值Math.sin
将参数mod修改为Math.PI*2
。对于像你这样的代码,这意味着与使用与乘法中使用的相同比例因子(即Math.PI*2
)执行mod减少相比,代码将花费更长时间并产生更不准确的结果。为了获得良好的准确性和速度,您应该在进行乘法之前执行模数减少,使用类似:
double thisSpin = t * frequency;
thisSpin -= (thisSpin - Math.Floor(thisSpin)) * 8.0; // value of 0-7.9999=one rotation
switch((int)(thisSpin*8.0))
{
case 0: return Math.sin( thisSpin * (Math.PI/4.0));
case 1: return Math.cos((2-thisSpin) * (Math.PI/4.0));
case 2: return Math.cos((thisSpin-2) * (Math.PI/4.0));
case 3: return Math.sin((4-thisSpin) * (Math.PI/4.0));
case 4: return -Math.sin((thisSpin-4) * (Math.PI/4.0));
case 5: return -Math.cos((6-thisSpin) * (Math.PI/4.0));
case 6: return -Math.cos((thisSpin-6) * (Math.PI/4.0));
case 7: return -Math.sin((8-thisSpin) * (Math.PI/4.0));
default: return 0; // Shouldn't be possible, but thisSpin==8 would be congruent to 0
}
这将确保sin
和cos
都不会使用大于π/ 4的参数,这是根据文档中Java切换到使用缓慢和适得其反的范围缩减的点。