10

我想使用 Java 创建一个彩色噪声生成器,它能够生成本文中定义的所有颜色:http ://en.wikipedia.org/wiki/Colors_of_noise

  1. 从最简单的白噪声开始,我将如何生成噪声以便它可以无限期地播放?
  2. 从那里,我将如何修改我的生成器以生成任何颜色?

我既对如何产生噪音本身感到困惑,又对如何通过扬声器输出噪音感到困惑。

任何链接或提示将不胜感激!

我还看过另一个问题:Java 生成声音

但我不完全理解其中一条评论中给出的代码中发生了什么。它也没有告诉我该代码会产生什么噪音,所以我不知道如何修改它以使其产生白噪音。

4

3 回答 3

4

这是一个在纯 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;
        }
    }
}
于 2014-11-17T00:44:04.643 回答
1

我实际上目前正在开展一个项目,用于获取白噪声并对其进行采样以产生随机数。你需要的是相反的!

声音是压力与时间的关系。基本上从 0 压力开始,然后添加从 -(最大幅度)到(最大幅度)的随机压力量。白噪声的幅度是随机且正态分布的,因此您可以使用Random.nextGaussian()生成随机 z 分数。将 z 分数乘以标准偏差(您可能需要进行一些测试才能找到您喜欢的幅度的标准偏差),然后让它成为音频文件中每个样本的幅度。

至于生成声音文件本身,如果您还没有,您应该查看Java Sound API。它具有许多用于创建声音文件和播放的好方法。

您问题的下一部分,非白噪声,恐怕我不确定波形是什么样的。它可能遵循类似的生成随机 z 分数并将它们乘以某个幅度标准偏差(或更可能乘以某个随时间变化的幅度函数)。

于 2014-11-16T23:41:03.703 回答
0

我不是音频工程师,所以我不能保证下面的所有代码都是有意义的,从声学角度来看是准确的还是有效的,只能说它在我的耳朵里听起来是合理的。我只是将其他人的代码按面值、可能的缺陷和所有内容粘合在一起,所以假设这还没有准备好生产。我欢迎反馈和修复!


对于白噪声,这是此线程中其他地方的此答案中代码的简化版本,它消除了不必要的 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 使用率,而不会出现任何明显的音频丢失,但我暂时将其排除在主代码之外。

于 2021-10-23T14:21:02.750 回答