首页 > 解决方案 > Java Audio SourceDataLine 不支持 PCM_FLOAT

问题描述

我正在尝试在 Linux 上使用 Java 播放音频缓冲区。

尝试打开线路时出现以下异常(而不是在我向其写入音频时)...

Exception in thread "main" java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_FLOAT 44100.0 Hz, 16 bit, mono, 2 bytes/frame,  is supported.

    public boolean open()
{
    try {

        int smpSizeInBits = bytesPerSmp * 8;
        int frameSize = bytesPerSmp * channels; // just an fyi, frameSize does not always == bytesPerSmp * channels for non PCM encodings
        int frameRate = (int)smpRate; // again this might not be the case for non PCM encodings.
        boolean isBigEndian = false;

        AudioFormat af = new AudioFormat(AudioFormat.Encoding.PCM_FLOAT , smpRate, smpSizeInBits, channels, frameSize, frameRate, isBigEndian);

        DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
        int bufferSizeInBytes = bufferSizeInFrames * channels * bytesPerSmp;
        line = (SourceDataLine) AudioSystem.getLine(info);
        line.open(af, bufferSizeInBytes);
        open = true;
    }
    catch(LineUnavailableException e) {
        System.out.println("PcmFloatPlayer: Unable to open, line unavailble.");
    }

    return open;
}

我想知道我对 PCM_FLOAT 编码是什么的假设实际上是不正确的。

我有一些读入 wav 文件的代码。wavfile 是单声道、16 位、未压缩格式。然后我将音频转换为 -1.0 到 1.0 范围内的浮点数进行处理。

我假设 PCM_FLOAT 编码只是原始 PCM 数据,已转换为 -1.0 和 1.0 之间的浮点值。这个对吗?

然后我假设 SourceDataLine 会根据我传递的格式信息(单声道、16 位、2 字节/帧)将浮动音频转换为适当的格式。这个假设又是不正确的吗?

我必须将我的 float -1.0 到 1.0 音频转换回我想要的输出格式,并将 SourceDataLine 设置为 PCM_SIGNED(假设这是我想要的格式)吗?

编辑:

另外,当我使用 PCM_FLOAT 调用 AudioSystem.getTargetEncodings() 时,它会返回三个编码。这是否意味着它将接受 PCM_FLOAT,并能够根据底层音频系统支持的内容转换为返回的编码?

        AudioFormat.Encoding[] encodings = AudioSystem.getTargetEncodings(AudioFormat.Encoding.PCM_FLOAT);
    for(AudioFormat.Encoding e : encodings)
        System.out.println(e);

结果是...

PCM_SIGNED PCM_UNSIGNED PCM_FLOAT

标签: javaaudiojavasoundpcmjava-audio

解决方案


I don't know that I'll be able to answer your direct questions. But maybe the code I can show you, which I know works (including on Linux), will help you arrive at a workable solution. I have programs that generate audio signals via incoming cues, but also custom-made Synths, and I do all the mixing and effects with PCM floats in the range -1 to 1. To output, I convert the floats to a standard "CD Quality" format that Java supports.

Here is the format I use for the outputting SourceDataLine:

AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);

You'll probably want to make this mono instead of stereo. But I should say, it seems to me that if you are able to read an incoming wav file with a different format, you should be able to play back that same format, assuming you reverse all the steps taken to convert the incoming data to PCM.

For the standard "CD Quality" format, to go from pcm signed floats to bytes, there is an intermediate step of inflating to the range of a signed short (-32768 to 32767).

public static byte[] fromBufferToAudioBytes(byte[] audioBytes, float[] buffer)
{
    for (int i = 0, n = buffer.length; i < n; i++)
    {
        buffer[i] *= 32767;
        audioBytes[i*2] = (byte) buffer[i];
        audioBytes[i*2 + 1] = (byte)((int)buffer[i] >> 8 );
    }
    return audioBytes;
}

This is taken from the AudioCue library that I wrote and posted on github.

I find it reduces headaches to just deal with the one AudioFormat, to make conversions with Audacity to the one format, and not try make provisions for multiple formats. But that is just a personal preference, and I don't know if that strategy would work for your situation or not.

Hope there is something here that helps!


推荐阅读