我想使用 Java 创建一个彩色噪声发生器,它能够生成本文中定义的所有颜色:http://en.wikipedia.org/wiki/Colors_of_noise
- 从最简单的白噪声开始,我将如何产生噪声才能无限期地播放?
- 从那里,我将如何修改我的生成器以生成任何颜色?
噪声本身感到困惑,又对一旦产生后如何通过扬声器输出它感到困惑。
任何链接或提示将不胜感激!
我还看过另一个问题:Java生成声音
但我不完全理解其中一个评论中给出的代码中发生了什么。它也没有告诉我该代码会产生什么噪音,所以我不知道如何修改它以产生白噪声。
这是一个在纯Java中生成白噪声的程序。它可以很容易地改变以产生其他颜色的噪音。
import javax.sound.sampled.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.ByteBuffer;
import java.util.Random;
public class WhiteNoise extends JFrame {
private GeneratorThread generatorThread;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
WhiteNoise frame = new WhiteNoise();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public WhiteNoise() {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
generatorThread.exit();
System.exit(0);
}
});
setTitle("White Noise Generator");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 200, 50);
setLocationRelativeTo(null);
getContentPane().setLayout(new BorderLayout(0, 0));
generatorThread = new GeneratorThread();
generatorThread.start();
}
class GeneratorThread extends Thread {
final static public int SAMPLE_SIZE = 2;
final static public int PACKET_SIZE = 5000;
SourceDataLine line;
public boolean exitExecution = false;
public void run() {
try {
AudioFormat format = new AudioFormat(44100, 16, 1, true, true);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format, PACKET_SIZE * 2);
if (!AudioSystem.isLineSupported(info)) {
throw new LineUnavailableException();
}
line = (SourceDataLine)AudioSystem.getLine(info);
line.open(format);
line.start();
} catch (LineUnavailableException e) {
e.printStackTrace();
System.exit(-1);
}
ByteBuffer buffer = ByteBuffer.allocate(PACKET_SIZE);
Random random = new Random();
while (exitExecution == false) {
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());
}
line.drain();
line.close();
}
public void exit() {
exitExecution =true;
}
}
}
我不是音频工程师,所以我不能保证下面的所有代码都是有意义的,从声学角度来看是准确的还是有效的,只是它听起来很合理。我只是将其他人的代码粘合在一起,这些代码的表面价值,可能的疣等等,所以假设这不是生产就绪的。我欢迎反馈和修复!
<小时 />对于白噪声,这是此线程中其他地方此答案中代码的简化版本,它消除了不必要的 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());
}
}
}
现在,我们可以使用各种技术更改噪声的颜色,例如将 JavaScript 代码从如何使用 Web 音频 API 生成噪声改编为 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
根据行缓冲区可用性进行限制的代码。天真地在循环中添加一个Thread.sleep(20)
会大量降低进程的 CPU 使用率,而没有任何明显的音频丢失,但我现在将其排除在主代码之外。
我目前正在进行一个项目,用于获取白噪声并对其进行采样以产生随机数。你需要的是相反的!
声音是压力与时间的关系。基本上从 0 压力开始,然后添加从 -(最大振幅)到 (最大振幅)的随机压力量。白噪声的振幅是随机的,呈正态分布,因此您可以使用 Random.nextGaussian() 生成随机 z 分数。将 z 分数乘以标准偏差(您可能需要进行一些测试才能找到您喜欢的振幅中的标准偏差),然后让它成为音频文件中每个样本的振幅。
至于生成声音文件本身,如果你还没有,你应该看看Java Sound API。它具有许多不错的方法,用于创建声音文件和播放。
你问题的下一部分,非白噪声,恐怕我不确定波形是什么样子的。它可能遵循类似的生成随机 z 分数并将它们乘以某个幅度标准差(或者更可能乘以随时间变化的某个幅度函数)。