在Kotlin/Java中直接从生成的声音阵列播放声音

在Kotlin/Java中直接从生成的声音阵列播放声音,java,audio,kotlin,signal-processing,javasound,Java,Audio,Kotlin,Signal Processing,Javasound,我正在寻找一种在Kotlin/Java中生成和播放声音的方法。我已经寻找了很多,并尝试了不同的解决方案,但它不是真的令人满意。 我不是在寻找Java控件类,它允许我将混响添加到现有的声音中,或者是javax.sound.midi包,它允许我进行midi排序。相反,我想通过以下方式从头开始构建声音作为声音向量/列表: fun createSinWaveBuffer(freq: Double, ms: Int, sampleRate: Int = 44100): ByteArray { va

我正在寻找一种在Kotlin/Java中生成和播放声音的方法。我已经寻找了很多,并尝试了不同的解决方案,但它不是真的令人满意。 我不是在寻找Java控件类,它允许我将混响添加到现有的声音中,或者是javax.sound.midi包,它允许我进行midi排序。相反,我想通过以下方式从头开始构建声音作为声音向量/列表:

fun createSinWaveBuffer(freq: Double, ms: Int, sampleRate: Int = 44100): ByteArray {
    val samples = (ms * sampleRate / 1000)
    val output = ByteArray(samples)
    val period = sampleRate.toDouble() / freq
    for (i in output.indices) {
        val angle = 2.0 * Math.PI * i.toDouble() / period
        output[i] = (Math.sin(angle) * 127f).toByte()
    }
    //output.forEach { println(it) }
    return output
}
然后我想播放声音,让扬声器的实际输出与发送到函数的输入参数在频率、长度等方面匹配。当然,创建两个不同频率的声音向量应该将它们相加,或者至少取平均值应该导致两个音调同时播放

这在matlab中非常简单,如果你有一个向量y,像这样

t=0:1/samplerate:duration;
y=sin(2*pi*freq*t);
照办

sound(y,sampleRate)
虽然Java中可能没有这么简单或干净的解决方案,但我仍然觉得应该可以播放自定义声音

在这里和其他地方搜索了一些之后,这是我正在尝试的最干净的解决方案之一(尽管它使用sun.audio,但其他建议更为混乱):

但是playsound(createSinWaveBuffer(440.0、10000、44100))在我的扬声器中听起来不太合适。它听起来波涛汹涌,不是440赫兹,不是纯正弦波,也不是10秒。 我缺少什么?

首先,不要使用sun软件包。永远

对于桌面,方法是生成数据,获取数据,打开并启动该行,然后将数据写入其中。重要的是,生产线适合您选择生成的产品。在这种情况下,8位/采样,采样率
44100
Hz

这里有一个Java的工作示例,我相信您可以轻松地将其转换为Kotlin

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class ClipDemo {

    private static byte[] createSinWaveBuffer(final float freq, final int ms, final float sampleRate) {
        final int samples = (int)(ms * sampleRate / 1000);
        final byte[] output = new byte[samples];
        final float period = sampleRate / freq;
        for (int i=0; i<samples; i++) {
            final float angle = (float)(2f * Math.PI * i / period);
            output[i] = (byte) (Math.sin(angle) * 127f);
        }
        return output;
    }

    public static void main(String[] args) throws LineUnavailableException {
        final int rate = 44100;
        final byte[] sineBuffer = createSinWaveBuffer(440, 5000, rate);
        // describe the audio format you're using.
        // because its byte-based, it's 8 bit/sample and signed
        // if you use 2 bytes for one sample (CD quality), you need to pay more attention
        // to endianess and data encoding in your byte buffer
        final AudioFormat format = new AudioFormat(rate, 8, 1, true, true);
        final SourceDataLine line = AudioSystem.getSourceDataLine(format);
        // open the physical line, acquire system resources
        line.open(format);
        // start the line (... to your speaker)
        line.start();
        // write to the line (... to your speaker)
        // this call blocks.
        line.write(sineBuffer, 0, sineBuffer.length);
        // cleanup, i.e. close the line again (left out in this example)
    }
}
导入javax.sound.sampled.AudioFormat;
导入javax.sound.sampled.AudioSystem;
导入javax.sound.sampled.LineUnavailableException;
导入javax.sound.sampled.SourceDataLine;
公共类ClipDemo{
专用静态字节[]createSinWaveBuffer(最终浮点频率、最终整数毫秒、最终浮点采样器){
最终整数样本=(整数)(ms*采样器/1000);
最终字节[]输出=新字节[样本];
最终浮动周期=取样器/频率;

对于(int i=0;ii如果我对这个API的简单搜索有任何好处,那么我不认为选择音频播放器是一个维护良好的选项,是未来的证明。不过我可能错了。你是在寻找桌面还是Android解决方案?不要使用
sun.audio
类-它们不是官方API的一部分,在现代版本中根本不起作用查看
javax.sound.sampled
类。标记信息有很多指向示例的链接。一般来说,不要使用
sun
类。@hendrik是一个桌面解决方案。如果可以使用任何标准的Java/kotlin类来完成,那就好了(我真的不明白为什么不能,因为这是你可以要求计算机做的最基本的事情之一),但我想嵌入式c/c++文件或.dll库也可以工作。SourceDataLine也适用于流式和连续音频。
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class ClipDemo {

    private static byte[] createSinWaveBuffer(final float freq, final int ms, final float sampleRate) {
        final int samples = (int)(ms * sampleRate / 1000);
        final byte[] output = new byte[samples];
        final float period = sampleRate / freq;
        for (int i=0; i<samples; i++) {
            final float angle = (float)(2f * Math.PI * i / period);
            output[i] = (byte) (Math.sin(angle) * 127f);
        }
        return output;
    }

    public static void main(String[] args) throws LineUnavailableException {
        final int rate = 44100;
        final byte[] sineBuffer = createSinWaveBuffer(440, 5000, rate);
        // describe the audio format you're using.
        // because its byte-based, it's 8 bit/sample and signed
        // if you use 2 bytes for one sample (CD quality), you need to pay more attention
        // to endianess and data encoding in your byte buffer
        final AudioFormat format = new AudioFormat(rate, 8, 1, true, true);
        final SourceDataLine line = AudioSystem.getSourceDataLine(format);
        // open the physical line, acquire system resources
        line.open(format);
        // start the line (... to your speaker)
        line.start();
        // write to the line (... to your speaker)
        // this call blocks.
        line.write(sineBuffer, 0, sineBuffer.length);
        // cleanup, i.e. close the line again (left out in this example)
    }
}