1

I am trying to play a buffer of audio using Java on Linux.

I am getting the following exception when attempting to open the line (not when I write the audio to it)...

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;
}

I am wondering if my assumptions about what PCM_FLOAT encoding is, are actually incorrect.

I have some code that reads in a wav file. The wavfile is mono, 16bit, uncompressed format. I then convert the audio to floats in range of -1.0 to 1.0 for processing.

I assumed the PCM_FLOAT encoding is just raw PCM data that has been converted to float values between -1.0 and 1.0. Is this correct?

I then assumed that the SourceDataLine would convert the float audio to the appropriate format based on my passed format info (mono, 16bit, 2bytes/frame). Again is this assumption incorrect?

Must I convert my float -1.0 to 1.0 audio back to my desired output format, and set the SourceDataLine to PCM_SIGNED (assuming that is my desired format)?

EDIT:

In addition, when I called AudioSystem.getTargetEncodings(), with PCM_FLOAT, it returns three encodings. Does that mean that it will accept PCM_FLOAT, and be capable to converting to the returned encodings, based on what the underlying audio system supports?

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

results in...

PCM_SIGNED PCM_UNSIGNED PCM_FLOAT

Scorb
  • 1,654
  • 13
  • 70
  • 144
  • 1
    I don't understand the error message. It says the requested format was `PCM_FLOAT`, 44100Hz, 16-bit, but I don't see that in your code. It would be helpful if you could edit your question to a [minimal, complete example](https://stackoverflow.com/help/mcve) which reproduces the exception you're getting. In any case, though, floating-point PCM is generally either IEEE 32- or 64-bit (the same specification as Java `float` and `double`). I don't think that Java sound actually supports floating-point for playback, though. Somebody could write an SPI for it, I guess. – Radiodef Jun 04 '18 at 01:17
  • I updated the code to properly reflect how it was when the exception occurred. – Scorb Jun 04 '18 at 01:26
  • In that case, I doubt that 16-bit floating-point would ever be supported, but I'd expect it to still throw even if you changed the sample size to 32- or 64-bit. The easiest thing is to use integers (e.g. `PCM_SIGNED`) for playback. – Radiodef Jun 04 '18 at 01:33
  • *"I updated the code.."* That's **not** an MCVE! – Andrew Thompson Jun 04 '18 at 01:39
  • @Radiodef - so just convert from float to signed int in my code before I write out? – Scorb Jun 04 '18 at 01:43
  • Regarding your edit, there's info on converters in the tutorial, if you want to try that out. https://docs.oracle.com/javase/tutorial/sound/converters.html – Radiodef Jun 04 '18 at 02:06

2 Answers2

1

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!

Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41
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);
      }
   }


}
Dave2e
  • 22,192
  • 18
  • 42
  • 50