Java Audio SourceDataLine不支持PCM_FLOAT

时间:2018-06-04 00:08:07

标签: java audio javasound pcm java-audio

我正在尝试在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

2 个答案:

答案 0 :(得分:0)

我不知道我能回答你的直接问题。但也许我可以告诉你的代码,我知道有效(包括在Linux上),可以帮助你找到一个可行的解决方案。我有通过输入提示生成音频信号的程序,还有定制的Synths,我使用-1到1范围内的PCM浮点数进行所有混音和效果。要输出,我将浮点数转换为标准的“CD质量” “Java支持的格式。

以下是我用于输出SourceDataLine的格式:

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

你可能想要制作这种单声道而不是立体声。但我应该说,在我看来,如果你能够以不同的格式读取传入的wav文件,你应该能够播放相同的格式,假设你反转将传入的数据转换为PCM所采取的所有步骤

对于标准的“CD质量”格式,从pcm signed浮动到字节,有一个中间步骤,即膨胀到有符号短路(-32768到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;
}

这取自我在github上编写并发布的AudioCue库。

我发现它减少了处理一个AudioFormat的麻烦,使用Audacity转换为一种格式,而不是尝试为多种格式做出规定。但这只是个人偏好,我不知道该策略是否适用于您的情况。

希望这里有一些有用的东西!

答案 1 :(得分:0)

public class Main {    
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread2();
        t1.start(); 
        Thread t2 = new thread3();  
        t2.start();
        Thread.sleep(5000);  
    }  
}

import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Thread2 extends Thread implements Runnable {

   @Override 
   public void run() { 
      playWav("C:/Windows/Media/feel_good_x.wav");
    }

   private static void playWav(String soundFilePath) {
      File sFile = new File(soundFilePath); 
      if (!sFile.exists()) {

         String ls = System.lineSeparator();
         System.err.println("немає в директорії»+
               ls + "(" + soundFilePath + ")" + ls);
         return;
      }

      try {
         Clip clip;

         try (AudioInputStream audioInputStream = AudioSystem.

               getAudioInputStream(sFile.getAbsoluteFile())) { 
            clip = AudioSystem.getClip();
            clip.setFramePosition(0);
            clip.open(audioInputStream);
         }
         clip.start();
      }

      catch (UnsupportedAudioFileException | IOException | LineUnavailableException ex) {

         Logger.getLogger("playWav()").log(Level.SEVERE, null, ex);
      }
   }


}