Android系统。产生的正弦波每秒产生爆震噪声

时间:2014-10-28 13:19:18

标签: android audio wave sine

我想生成具有组合频率的正弦波。我走这条路:

public static byte[] combineSineWave(Collection<Double> frequencies) {
    double[] sample = new double[SAMPLE_RATE];
    byte[] result = new byte[2 * SAMPLE_RATE];

    double coefficient = 2 * Math.PI;

    for (int i = 0; i < SAMPLE_RATE; i++) {
        double sum = 0;

        for (double frequency : frequencies) {
            sum += Math.sin(coefficient * i * frequency / SAMPLE_RATE);
        }

        sample[i] = sum / frequencies.size();
    }

    convertToPCM16Bit(sample, result);

    return result;
}

private static void convertToPCM16Bit(double[] sample, byte[] result) {
    int idx = 0;
    for (final double dVal : sample) {
        // scale to maximum amplitude
        final short val = (short) ((dVal * 32767));
        // in 16 bit wav PCM, first byte is the low order byte
        result[idx++] = (byte) (val & 0x00ff);
        result[idx++] = (byte) ((val & 0xff00) >>> 8);
    }
}

通过此代码播放生成的字节:

AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SoundWaveGenerator.SAMPLE_RATE,
            AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2 * SoundWaveGenerator.SAMPLE_RATE,
            AudioTrack.MODE_STREAM);

    audioTrack.play();

    while (true) {
        byte[] bytes = SoundWaveGenerator.combineSineWave(frequencies);
        audioTrack.write(bytes, 0, bytes.length);
    }

但问题是我每隔一秒就听到一声咔哒声。怎么了?

1 个答案:

答案 0 :(得分:1)

您听到的点击是每次调用combineSineWave之间的不连续性。在每次调用combineSineWave时,每个频率都会开始零度相位。但是,根据频率,在1秒间隔结束时可能不会产生偶数个周期。假设其中一个频率使得第二个样本的最后一个样本处于90度(1.0),那么当您生成下一个间隔时就存在不连续性。

要解决此问题,您需要跟踪连续调用之间的某些状态。我在下面编写了一个例子,但我不知道如何通过Java中的引用传递。此外,你还需要处理我翻身的可能性。

int i = 0;
while (true) {
    byte[] bytes = SoundWaveGenerator.combineSineWave(frequencies, ref i);
    audioTrack.write(bytes, 0, bytes.length);
}


public static byte[] combineSineWave(Collection<Double> frequencies, ref int i) {
    double[] sample = new double[SAMPLE_RATE];
    byte[] result = new byte[2 * SAMPLE_RATE];

    double coefficient = 2 * Math.PI;

    for (int j = 0; j < SAMPLE_RATE; j++) {
        double sum = 0;

        for (double frequency : frequencies) {
            sum += Math.sin(coefficient * i++ * frequency / SAMPLE_RATE);
        }
        sample[i] = sum / frequencies.size();
    }

    convertToPCM16Bit(sample, result);

    return result;
}

P.S。你可以在循环之外除以SAMPLE_RATE

double coefficient = 2 * Math.PI / SAMPLE_RATE;