我想生成具有组合频率的正弦波。我走这条路:
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);
}
但问题是我每隔一秒就听到一声咔哒声。怎么了?
答案 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;