Issues with SourceDataLine format support - java

I have an application written in Java in which I need to play audio. I used OpenAL (with java-openal library) for the task however I would like to use WSOLA which is not supported by OpenAL directly. I found a nice java-native library called TarsosDSP which has support for WSOLA.
The library uses standard Java APIs for audio output. The issue occurs during the SourceDataLine setup:
IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_UNSIGNED 16000.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian is supported.
I made sure the issue is not caused by the lack of permissions (ran it as root on Linux + tried it on Windows 10) and there are no other SourceDataLines used in the project.
After tinkering with the format I found out that the format is accepted when it's changed from PCM_UNSIGNED to PCM_SIGNED. It seems like a minor issue since only moving the byte range form unsigned to signed should be pretty easy. However it's weird that it's not supported natively.
So, is there some solution in which I wouldn't have to modify the source data?
Thanks, Jan

You don't have to move the byte range by hand. After you've created an AudioInputStream, you create another AudioInputStream, with a signed format and that is connected to the first unsigned stream. If you then read the data using the signed stream, the Sound API automatically converts the format. This way you don't need to modify the source data.
File fileWithUnsignedFormat;
AudioInputStream sourceInputStream;
AudioInputStream targetInputStream;
AudioFormat sourceFormat;
AudioFormat targetFormat;
SourceDataLine sourceDataLine;
sourceInputStream = AudioSystem.getAudioInputStream(fileWithUnsignedFormat);
sourceFormat = sourceInputStream.getFormat();
targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
sourceFormat.getSampleRate(),
sourceFormat.getSampleSizeInBits(),
sourceFormat.getChannels(),
sourceFormat.getFrameSize(),
sourceFormat.getFrameRate(),
false);
targetInputStream = AudioSystem.getAudioInputStream(targetFormat, sourceInputStream);
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, targetFormat);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(targetFormat);
sourceLine.start();
// schematic
targetInputStream.read(byteArray, 0, byteArray.length);
sourceDataLine.write(byteArray, 0, byteArray.length);

Related

Ubuntu 16.4, Java, WAV file playback

This is a weird thing. I'm trying to playback some sounds via Java AudioSystem and AudioSystem.getClip(). The files are all "PCM_SIGNED, 22.050.0 Hz, 16 bit, mono, 2 bytes/frame, little endian".
On several Ubuntu 16.4 LTS Linux boxes this format is rejected by PulseAudio with an Invalid Format Exception, because the only accepted format is seemingly "PCM_SIGNED, unknown sample rate, 16 bit, stereo, 4 bytes/frame, big endian".
I already tried to re-sample my WAVs in order to match this strange constraint, to no avail. Those are not even accepted anymore by AudioSystem.getAudioInputStream()
Needless to say, that the same works fine on Mac OS and Windows. And there is also no problem to playback these files using the sox library and play file.wav
OK, solved.
Usually if one asks, how to playback WAV using Java, this is the most common answer:
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(DragonflyApp.class.getResource("/resources/" + soundFile));
clip = AudioSystem.getClip();
clip.addLineListener(e -> {
if (e.getType() == LineEvent.Type.STOP) {
// Do something on end of playback
}
});
clip.open(audioInputStream);
clip.start();
Unfortunately on some Linux systems this ends up in an "Invalid Format" exception, thrown by PulseAudio, which claims to be unable to playback the simplest WAV file (see above).
The workaround is to use this sequence under Linux instead. It generally does also work on MacOS, but the final "STOP" indication comes very late (roughly 5s after playback end), so I make a conditional execution here:
This works on Linux (at least on Ubunutu 16.04) with clips, which have formerly been rejected by PulseAudio:
DataPusher datapusher = null;
DataLine.Info lineinfo = null;
SourceDataLine sourcedataline = null;
lineinfo = new DataLine.Info(SourceDataLine.class, audioInputStream.getFormat());
if (!(AudioSystem.isLineSupported(lineinfo))) {
return;
}
sourcedataline = (SourceDataLine) AudioSystem.getLine(lineinfo);
sourcedataline.addLineListener(e -> {
if (e.getType() == LineEvent.Type.STOP) {
// Do something on end of playback
}
});
datapusher = new DataPusher(sourcedataline, audioInputStream);
datapusher.start();
Both code snippets are used conditionally:
if (System.getProperty("os.name").equals("Mac OS X")) {
// The clip solution
}
else {
// The datapusher solution
}
Hope, that helps others, who will also have this problem.

Java does not play multi channel wave file

I am unable to play a multi channel wave file in java using javax.sound.sampled package.
I am using an external USB 7.1 surround sound card from logilink.
The wave file is generated by myself. If I generate a stereo track, I am able to play the file. If I use the 6 channel track, I get the following error message:
javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 48000.0 Hz, 16 bit, 6 channels, 12 bytes/frame, little-endian not supported.
I checked if the line is supported and it is by using this code
AudioFormat format = new AudioFormat(48000, 16, 6, true, false);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
if (info.isFormatSupported(format)) {
// is supported
}
I play the wave file as follows:
AudioListener listener = new AudioListener();
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(clipFile);
Clip clip = null;
clip = AudioSystem.getClip();
clip.addLineListener(listener);
clip.open(audioInputStream);
clip.start();
Any idea what is wrong?

Error while playing sound in Java

I'm trying to play a sound in my Java Application, but every time I call the method I get this exception: javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 48000.0 Hz, 24 bit, mono, 3 bytes/frame, little-endian not supported.
Here's the code:
AudioInputStream audio = AudioSystem.getAudioInputStream(new File("src/media/ding2.wav"));
Clip clip = AudioSystem.getClip();
clip.open(audio);
clip.start();
I tried to play a file by passing a URL and it works fine, but with my "ding2.wav" nothing works.
Thanks in advance for your help.
By looking over HERE in Documention, the LineUnavailableException arises when the line is not Available, or the requested resource is in use by Another Application,
Make Sure that your audio file is not open in any other application.
EDIT
As the error message says :line with format PCM_SIGNED 48000.0 Hz, 24 bit, mono, 3 bytes/frame
the file format you are providing is not supported.
and as you said I tried to play a file by passing a URL and it works fine,
put the Old file back and check the file format by using
System.out.println(audio.getFormat());
and check what was the file format of that file , whether that was same to the above _line with format PCM_SIGNED 48000.0 Hz, 24 bit, mono, 3 _ or not,

How can I play an ALAW file in a Java application?

I need to be able to play ALAW files in a Java (desktop) application.
I've tried to follow the example at:
How to play audio in Java Application
I've created a File object from the ALAW file (which exists, according to check) and sent that File to a method where the first thing that happens is this:
AudioInputStream ais = AudioSystem.getAudioInputStream(file);
But this is where the execution stops, since I get this exception:
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
I see that there is a way to convert ALAW files if the check (ais.getFormat().getEncoding() == AudioFormat.Encoding.ALAW) is true, but how can I get there if it's not even possible to create the AudioInputStream?
Anyone who has worked with ALAW files and has an idea of what I should do?
Is there a way to convert the ALAW files programmatically before calling AudioSystem.getAudioInputStream(file)?
I really need to make this work!
Get existing file format from your AudioInputStream:
filepath is String with path to your file,which you obtain for example:
String filename="x.y";
File file = new File(filename);
String filepath=file.getCanonicalPath();
Then main conversion is done by:
AudioInputStream inputStream = AudioSystem.getAudioInputStream(new File(filepath));
AudioFormat format = inputStream.getFormat();
AudioInputStream convertedInputStream;
After that put condition, which checks if your file encoding is alaw or ulaw, and converts it to PCM which can be played by SoundCard:
if ((format.getEncoding() == AudioFormat.Encoding.ULAW) || (format.getEncoding() == AudioFormat.Encoding.ALAW))
AudioFormat tmp = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
format.getSampleRate(),
format.getSampleSizeInBits() * 2,
format.getChannels(),
format.getFrameSize() * 2,
format.getFrameRate(), true);
convertedInputStream = AudioSystem.getAudioInputStream(tmp,inputStream);
format = tmp;}
This code will convert ALAW/ULAW format of your AudioInputStream to PCM_SIGNED
JMF will help in this case.
http://www2.sys-con.com/itsg/virtualcd/java/archives/0503/decarmo/index.html

How to get audio data from a MP3?

I'm working on an application that has to process audio files. When using mp3 files I'm not sure how to handle data (the data I'm interested in are the the audio bytes, the ones that represent what we hear).
If I'm using a wav file I know I have a 44 bytes header and then the data. When it comes to an mp3, I've read that they are composed by frames, each frame containing a header and audio data. Is it possible to get all the audio data from a mp3 file?
I'm using java (I've added MP3SPI, Jlayer, and Tritonus) and I'm able to get the bytes from the file, but I'm not sure about what these bytes represent or how to handle then.
From the documentation for MP3SPI:
File file = new File(filename);
AudioInputStream in= AudioSystem.getAudioInputStream(file);
AudioInputStream din = null;
AudioFormat baseFormat = in.getFormat();
AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
baseFormat.getSampleRate(),
16,
baseFormat.getChannels(),
baseFormat.getChannels() * 2,
baseFormat.getSampleRate(),
false);
din = AudioSystem.getAudioInputStream(decodedFormat, in);
You then just read data from din - it will be the "raw" data as per decodedFormat. (See the docs for AudioFormat for more information.)
(Note that this sample code doesn't close the stream or anything like that - use appropriate try/finally blocks as normal.)
The data that you want are the actual samples, while MP3 represents the data differently. So, like what everyone else has said - you need a library to decode the MP3 data into actual samples for your purpose.
As mentioned in the other answers, you need a decoder to decode MP3 into regular audio samples.
One popular option would be JavaLayer (LGPL).

Categories