我不是音频工程师,所以我不能保证下面的所有代码都是有意义的,从声学角度来看是准确的还是有效的,只能说它在我的耳朵里听起来是合理的。我只是将其他人的代码按面值、可能的缺陷和所有内容粘合在一起,所以假设这还没有准备好生产。我欢迎反馈和修复!
对于白噪声,这是此线程中其他地方的此答案中代码的简化版本,它消除了不必要的 GUI 内容:
import java.nio.ByteBuffer;
import java.util.Random;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class WhiteNoise {
public static void main(String[] args) {
final int SAMPLE_RATE = 44100;
final int BITS = 16;
final int CHANNELS = 1;
final int SAMPLE_SIZE = 2;
final int PACKET_SIZE = 5000;
AudioFormat format = new AudioFormat(
SAMPLE_RATE,
BITS,
CHANNELS,
true, // signed
true // big endian
);
DataLine.Info info = new DataLine.Info(
SourceDataLine.class,
format,
PACKET_SIZE * 2
);
SourceDataLine line;
try {
line = (SourceDataLine)AudioSystem.getLine(info);
line.open(format);
}
catch (LineUnavailableException e) {
e.printStackTrace();
return;
}
line.start();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
//line.drain(); // seems to hang my Windows machine
line.close();
}));
ByteBuffer buffer = ByteBuffer.allocate(PACKET_SIZE);
Random random = new Random();
for (;;) {
buffer.clear();
for (int i = 0; i < PACKET_SIZE / SAMPLE_SIZE; i++) {
buffer.putShort((short)(random.nextGaussian() * Short.MAX_VALUE));
}
line.write(buffer.array(), 0, buffer.position());
}
}
}
现在,我们可以使用多种技术来更改噪声的颜色,例如将如何使用 Web Audio API 生成噪声的 JavaScript 代码改编为 Java。上面所有的样板代码都是一样的;这只是改变了块周围的代码for (;;) {...}
。
粉色的:
// ...
double b0 = 0.0;
double b1 = 0.0;
double b2 = 0.0;
double b3 = 0.0;
double b4 = 0.0;
double b5 = 0.0;
double b6 = 0.0;
for (;;) {
buffer.clear();
for (int i = 0; i < PACKET_SIZE / SAMPLE_SIZE; i++) {
double white = random.nextGaussian();
b0 = 0.99886 * b0 + white * 0.0555179;
b1 = 0.99332 * b1 + white * 0.0750759;
b2 = 0.96900 * b2 + white * 0.1538520;
b3 = 0.86650 * b3 + white * 0.3104856;
b4 = 0.55000 * b4 + white * 0.5329522;
b5 = -0.7616 * b5 - white * 0.0168980;
double output = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
output *= 0.05; // (roughly) compensate for gain
b6 = white * 0.115926;
buffer.putShort((short)(output * Short.MAX_VALUE));
}
line.write(buffer.array(), 0, buffer.position());
}
// ...
布朗尼:
// ...
double lastOut = 0.0;
for (;;) {
buffer.clear();
for (int i = 0; i < PACKET_SIZE / SAMPLE_SIZE; i++) {
double white = random.nextGaussian();
double output = (lastOut + (0.02 * white)) / 1.02;
lastOut = output;
output *= 1.5; // (roughly) compensate for gain
buffer.putShort((short)(output * Short.MAX_VALUE));
}
line.write(buffer.array(), 0, buffer.position());
}
// ...
在线程的其他地方,Mars 共享PinkNoise.java,所以我不妨把它作为后代的替代方法放在答案中。他们提供的许多建议中的一个建议是交换random.nextGaussian()
以random.nextDouble() - 0.5
提高性能。
以随机性和“声学正确性”为代价的另一种可能的优化是预先生成一堆随机缓冲区,然后从它们中随机挑选或循环通过它们。对于许多用例来说,这听起来可能足够准确。
最后,while
循环在上面的例子中做的工作可能比它需要做的要多。使用 Java 生成音频正弦波显示了用于Thread.sleep
根据线路缓冲区可用性进行节流的代码。天真地在循环中添加 aThread.sleep(20)
会大大降低进程的 CPU 使用率,而不会出现任何明显的音频丢失,但我暂时将其排除在主代码之外。