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

时间:2019-09-01 09:52:19

标签: java audio kotlin signal-processing javasound

我正在寻找一种在Kotlin / Java中生成和播放声音的方法。我一直在搜索很多,并尝试了不同的解决方案,但这并不令人满意。 我不是在寻找Java Control类,它使我可以将混响添加到现有声音中,也不是在寻找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
}

然后我要播放声音,并使扬声器的实际输出与频率,长度等有关的发送参数相匹配。当然,要创建两个具有不同频率的声音矢量将它们放在一起或至少取平均值,应会同时演奏两个音调。

如果您有向量y,这在matlab中非常简单

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

就做

sound(y,sampleRate)

尽管Java中可能没有这么简单或干净的解决方案,但我仍然觉得应该可以播放自定义声音。

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

import sun.audio.AudioPlayer
import sun.audio.AudioDataStream
import sun.audio.AudioData

private fun playsound(sound: ByteArray) {
    val audiodata = AudioData(sound)
    val audioStream = AudioDataStream(audiodata)
    AudioPlayer.player.start(audioStream)
}

但是playound(createSinWaveBuffer(440.0,10000,44100))在我的扬声器中听起来不正确。听起来不稳,不是440 Hz,不是纯正弦波,也不是十秒。 我想念什么?

1 个答案:

答案 0 :(得分:1)

首先,不要使用sun软件包。曾经。

在台式机上,方法是生成数据,获取SourceDataLine,打开并开始一行,然后将数据写入其中。该行适用于您选择生成的AudioFormat,这一点很重要。在这种情况下,采样率为8位,采样率为44,100 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)
    }
}