I am new to Java and I was attempting to create a sort of audio clip player that I can play with. I have no clue why this is happening and I just though I post it on here so someone smarter can take a look
try
{
File snareFile = new File("Snare.WAV");
AudioInputStream snare = AudioSystem.getAudioInputStream(snareFile.getAbsoluteFile());
println(snare.getFormat());
clip = AudioSystem.getClip();
clip.open(snare);
}
catch(UnsupportedAudioFileException e)
{
println("unsupported audio");
}
catch(FileNotFoundException e)
{
println("try a new path");
}
catch(IOException e)
{
println("IO issue");
}
catch(Exception e)
{
println("Other issue: " + e);
}
and the error I get is:
javax.sound.sampled.LineUnavailableException: line with format ULAW 48000.0 Hz, 8 bit, mono, 1 bytes/frame not supported.
I have tried changing the format of the audio file, and spending hours researching something I know very minimally about. Thanks in advance!
It turns out that U-LAW encoding is not supported and I needed to change it to PCM-UNSIGNED
Related
I have been trying to manually read a wav file in Java and read an array of bytes then write to an audio buffer for playback. I am receiving playback but it is heavily distorted. Java sound supports 16 bit sample rates but not 24-bit.
I went in to Logic 9 and exported a 24-bit audio file in to 16-bit and then used with my program. Originally, the 24-bit samples would produces white noise. Now I can hear my sample but very distorted and sounds like it has been bit crushed.
Can anyone help me to get a clean signal?
I am very new to audio programming but I am currently working on a basic Digital Audio Workstation.
import javax.sound.sampled.*;
import javax.sound.sampled.DataLine.Info;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.*;
public class AudioData {
private String filepath;
private String filepath1;
private File file;
private byte [] fileContent;
private Mixer mixer;
private Mixer.Info[] mixInfos;
private AudioInputStream input;
private ByteArrayOutputStream byteoutput;
public static void main (String [] args) {
AudioData audiodata = new AudioData();
}
public AudioData () {
filepath = "/Users/ivaannagen/Documents/Samples/Engineering Samples - Obscure Techno Vol 3 (WAV)/ES_OT3_Kit03_Gmin_130bpm/ES_OT3_Kit03_FX_Fast_Snare_Riser_Gmin_130bpm.wav";
filepath1 = "/Users/ivaannagen/Documents/Samples/dawsampletest.wav";
file = new File (filepath1);
readAudio();
}
public void readAudio () {
mixInfos = AudioSystem.getMixerInfo();
mixer = AudioSystem.getMixer(mixInfos[0]);
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
// set up an audio format.
try {
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); // creates data line with class type and audio format.
SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
System.out.println("Size of data line buffer: " + source.getBufferSize());
fileContent = new byte [source.getBufferSize() / 50];
byteoutput = new ByteArrayOutputStream();
input = AudioSystem.getAudioInputStream(file);
int readBytes = 0;
while ((readBytes = input.read(fileContent, 0, fileContent.length)) != -1) {
byteoutput.write(fileContent, 0, readBytes);
}
System.out.println("Size of audio buffer: " + fileContent.length);
//byteoutput.write(0);
// byteoutput.write(0);
System.out.println("Size of audio buffer: " + byteoutput.size());
source.open(format, source.getBufferSize()); // line must be open to be recognised by the mixer.
Line[] lines = mixer.getSourceLines();
System.out.println("mixer lines: " + lines.length);
// for(byte bytes: fileContent) {
// System.out.println(bytes);
// }
Thread playback = new Thread () {
public void run () {
// System.out.println((byteoutput.size() +2) % 4);
source.start(); // play (buffer originally empty)
source.write(byteoutput.toByteArray(), 0, byteoutput.size()); // write input bytes to output buffer
} // end run (to do).
}; // end thread action
playback.start(); // start thread
}
catch (LineUnavailableException lue) {
System.out.println(lue.getMessage());
}
catch (FileNotFoundException fnfe) {
System.out.println(fnfe.getMessage());
}
catch(IOException ioe) {
System.out.println(ioe.getMessage());
}
catch(UnsupportedAudioFileException uafe) {
System.out.println(uafe.getMessage());
}
}
}
Whether or not you can load and play a 24-bit file is system dependent, afaik.
I use Audacity for conversions. You should be able import your file into Audacity and export it as 16-bit, stereo, little-endian, 44100 fps, and then load that export with Java's AudioInputStream.
What you hear when playing from Audacity or from Java should be pretty much identical (adjusting for volume). If not, the most likely reason probably pertains to a mistake or overlook in the code, which is very easy to do.
The use of a ByteOutputStream in your code is superfluous. Read from the AudioInputStream into a fixed-size byte array (size being the buffer length, I recommend trying 8 or 16 * 1024 bytes as a first try) and then use the SourceDataLine write method to ship that array.
Following is code that works on my system for loading a playing a "CD Quality" wav called "a3.wav" that I have that is in the same directory as the Java class. You should be able to swap in your own 44100, 16-bit, stereo, little-endian wav file.
I've commented out an attempt to load and play a 24-bit wav file called "spoken8000_24.wav". That attempt gave me an IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 8000.0 Hz, 24 bit, stereo, 6 bytes/frame, little-endian is supported.
I have to admit, I'm unclear if my system doesn't provide the needed line or if I might have coded the format incorrectly! My OS can certainly play the file. So I'm thinking there is a distinction between what an OS can do and what a "Mixer" on a given system provides to Java.
As a get-around, I just always convert everything to "CD Quality" format, as that seems to be the most widely supported.
public class TriggerSound_SDL extends JFrame
{
public TriggerSound_SDL()
{
JButton button = new JButton("Play Sound");
button.addActionListener(e -> new Thread(() -> playBuzzer()).start());
getContentPane().add(button);
}
private void playBuzzer()
{
try
{
URL url;
url = getClass().getResource("a3.wav");
// url = getClass().getResource("spoken8000_24.wav");
AudioInputStream ais = AudioSystem.getAudioInputStream(url);
System.out.println(ais.getFormat());
AudioFormat audioFmt;
// "CD Quality" 44100 fps, 16-bit, stereo, little endian
audioFmt = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
44100, 16, 2, 4, 44100, false);
// 8000 fps, 32-bit, stereo
// audioFmt = new AudioFormat(
// AudioFormat.Encoding.PCM_SIGNED,
// 8000, 24, 2, 6, 8000, false);
Info info = new DataLine.Info(SourceDataLine.class,
audioFmt);
SourceDataLine sdl = (SourceDataLine)AudioSystem.getLine(info);
int bufferSize = 16 * 1024;
byte[] buffer = new byte[bufferSize];
sdl.open(audioFmt, bufferSize);
sdl.start();
int numBytesRead = 0;
while((numBytesRead = ais.read(buffer)) != -1)
{
sdl.write(buffer, 0, numBytesRead);
}
}
catch (IOException | UnsupportedAudioFileException
| LineUnavailableException ex)
{
ex.printStackTrace();
}
}
private static void createAndShowGUI()
{
JFrame frame = new TriggerSound_SDL();
frame.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
This code, with some small tweaks should let you at least test the different formats.
EDIT:
I'm seeing where your goal is to make a DAW!
In that case, you will want to convert the bytes to PCM data. Can I suggest you borrow some code from AudioCue? I basically wrote it to be a Clip-substitute, and part of that involved making the PCM data available for manipulation. Some techniques for mixing, playing back at different frequencies, multithreading can be found in it.
Thanks for all the advice guys. I will be getting rid of the ByteOutputStream and just use the AudioInputStream, I now understand what I was doing was unnecessary!! Thanks for the advice all! I have indeed tried using AudioCue but it is not low level enough for what I want to do!
One more thing guys. Previously, I created a multitrack media player which is using the Clip class. To play all the audio tracks together, I was looping through a list of Clips and playing them. However, this means that all tracks may be playing a tiny amount after each other due to the processing of the loop. Also, Clip class created a new thread per audio. I do not wants 100 threads running on 100 tracks, I want one thread for my audio output. I am still trying to work out how to start all tracks at the same time without a loop....(im guessing AudioCue have nailed the concurrent cues).
Does anyone know the best way to play multiple audio tracks in to one output? Do I need to route/bus all my audio tracks in to one output and somehow write all data from audio files in to one output buffer then play this output in a thread?
Thanks!!
I am having trouble using Java audio on Linux. This is OpenJDK 8 on Ubuntu 14.04. The following sample fails with the .wav file from this link:
import java.net.URL;
import javax.sound.sampled.*;
public class PlaySound {
public void play() throws Exception
{
// List all mixers and default mixer
System.out.println("All mixers:");
for (Mixer.Info m : AudioSystem.getMixerInfo())
{
System.out.println(" " + m);
}
System.out.println("Default mixer: " + AudioSystem.getMixer(null).getMixerInfo());
URL url = getClass().getResource("drop.wav");
Clip clip;
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
System.out.println("Clip format: " + clip.getFormat());
clip.open(audioInputStream);
clip.start();
do { Thread.sleep(100); } while (clip.isRunning());
}
public static void main(String [] args) throws Exception {
(new PlaySound()).play();
}
}
This is the result:
All mixers:
PulseAudio Mixer, version 0.02
default [default], version 4.4.0-31-generic
Intel [plughw:0,0], version 4.4.0-31-generic
Intel [plughw:0,2], version 4.4.0-31-generic
NVidia [plughw:1,3], version 4.4.0-31-generic
NVidia [plughw:1,7], version 4.4.0-31-generic
NVidia [plughw:1,8], version 4.4.0-31-generic
NVidia [plughw:1,9], version 4.4.0-31-generic
Port Intel [hw:0], version 4.4.0-31-generic
Port NVidia [hw:1], version 4.4.0-31-generic
Default mixer: default [default], version 4.4.0-31-generic
Clip format: PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian
Exception in thread "main" java.lang.IllegalArgumentException: Invalid format
at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.createStream(PulseAudioDataLine.java:142)
at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.open(PulseAudioDataLine.java:99)
at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.open(PulseAudioDataLine.java:283)
at org.classpath.icedtea.pulseaudio.PulseAudioClip.open(PulseAudioClip.java:402)
at org.classpath.icedtea.pulseaudio.PulseAudioClip.open(PulseAudioClip.java:453)
at PlaySound.play(PlaySound.java:22)
at PlaySound.main(PlaySound.java:29)
Apparently the problem is that the PulseAudio mixer is being selected, and for some reason it cannot play the .wav file.
If I replace the AudioSystem.getClip() call with AudioSystem.getClip(null), which selects the default mixer, then it works.
How can I make sure that a compatible mixer is selected ?
Update: Following the suggestion from #Dave to loop through the available mixers until I find one that has a "compatible" format, I see the following:
Target format (from AudioInputStream.getFormat()) is:
PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian
I loop through all mixers, source lines for each mixer, and supported formats for each source line, and get the following match:
Mixer: PulseAudio Mixer, version 0.02
Source line: interface SourceDataLine supporting 42 audio formats, and buffers of 0 to 1000000 bytes
Format matches: PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, little-endian
I do get a match (using format.matches()) yet I still get the "Invalid format" exception. Perhaps because the format that matched says "Unknown sample rate" and then when I try to open the clip, it finds that it does not actually support 44100 Hz ?
If you are able to use SourceDataLine in your use case instead of Clip, then you should be able to do something like this:
URL url = getClass().getResource("drop.wav");
SourceDataLine sourceLine;
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
sourceLine = AudioSystem.getSourceDataLine(audioInputStream.getFormat());
System.out.println("Source format: " + sourceLine.getFormat());
sourceLine.open(audioInputStream);
sourceLine.start();
do { Thread.sleep(100); } while (sourceLine.isRunning());
(Note this is as yet untested on my side.)
You only need Clip if you plan on seeking or looping.
If you do need the ability to seek or loop, one (ugly) method would be calling AudioSystem.getClip(null) first to ensure you are selecting the default Mixer. The semantics of AudioSystem.getClip() are (as you have noticed) not particularly reliable. Wrap all attempts at calling Clip.open in a try/catch. If opening a clip does not work with the default mixer, then loop through available Mixer.Info objects excluding the default and call getClip(mixerInfo) with each until one works.
Another method would be to loop through Mixer.Info objects returned by AudioSystem.getMixerInfo(). Call AudioSystem.getMixer(mixerInfo) for each to get the Mixer instance. Loop through the Line.Info objects returned by Mixer.getSourceLineInfo(). For each of these, if it is an instance of DataLine.Info, cast it and call DataLine.Info.getFormats() to get the supported AudioFormat objects. Compare these against what AudioInputStream.getFormat() returns (using matches) until you find a compatible one.
Neither of these are particularly elegant. The first is a bad use of exceptions. The second is just a bit convoluted, although it seems more correct.
I'm also on Ubuntu 14.04, but have different mixer, it works fine.
I think you could specify concrete parameters, which are needed for your line:
import javax.sound.sampled.*;
import java.net.URL;
public class PlaySound {
public void play() throws Exception {
// List all mixers and default mixer
System.out.println("All mixers:");
for (Mixer.Info m : AudioSystem.getMixerInfo()) {
System.out.println(" " + m);
}
System.out.println("Default mixer: " + AudioSystem.getMixer(null).getMixerInfo());
URL url = getClass().getResource("drop.wav");
Clip clip;
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
// clip = AudioSystem.getClip();
try {
AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
44100,
16, 2, 4,
AudioSystem.NOT_SPECIFIED, true);
DataLine.Info info = new DataLine.Info(Clip.class, format);
clip = (Clip) AudioSystem.getLine(info);
} catch (LineUnavailableException e) {
System.out.println("matching line is not available due to resource restrictions");
return;
} catch (SecurityException ee) {
System.out.println("if a matching line is not available due to security restrictions");
return;
} catch (IllegalArgumentException eee) {
System.out.println("if the system does not support at least one line matching the specified Line.Info object " +
"through any installed mixer");
return;
}
System.out.println("Clip format: " + clip.getFormat());
clip.open(audioInputStream);
clip.start();
do {
Thread.sleep(100);
} while (clip.isRunning());
}
public static void main(String[] args) throws Exception {
(new PlaySound()).play();
}
}
How can I make sure that a compatible mixer is selected ?
Another cases - use default by default, or catch exception and use default on failover.
Looks like there are two separate issues involved here.
First, relying on AudioSystem.getClip() is not a good idea as basically there's no guarantee that the clip will be able to handle the specific format of the wav file. Instead, one of the following approaches should be used:
As suggested by #Dave: Loop through the available mixers and query if the target format is supported:
URL url = getClass().getResource("drop.wav");
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
AudioFormat format = audioInputStream.getFormat();
DataLine.Info lineInfo = new DataLine.Info(Clip.class, format);
Mixer.Info selectedMixer = null;
for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) {
Mixer mixer = AudioSystem.getMixer(mixerInfo);
if (mixer.isLineSupported(lineInfo)) {
selectedMixer = mixerInfo;
break;
}
}
if (selectedMixer != null) {
Clip clip = AudioSystem.getClip(selectedMixer);
[...]
}
Or, as suggested by #egorlitvinenko, use AudioSystem.getLine(DataLine.Info) to get a line with the desired capabilities.
Both of the above approaches "should" work.
On top of that, there is an additional problem with PulseAudio, which is that there is a bug which may result in an "invalid format" exception even though the PulseAudio mixer can actually handle the format. This is described in the following bug reports (the second one contains workarounds):
https://icedtea.classpath.org/bugzilla/show_bug.cgi?id=3452 (my own)
https://icedtea.classpath.org/bugzilla/show_bug.cgi?id=2915
I'm using 320 kbps roughly 1 hour long MP3 files. The project I'm working on would seek in a collection of music inside an MP3 file so that it can shuffle the songs. I would give the timestamps to the program and it would seek to the song. It would work if JavaFX's seek method wasn't highly inaccurate.
After using MediaPlayer.seek(duration) The MediaPlayer.getCurrentTime() returns the duration we seeked to as expected. However if we listen to the mp3 file(either without seeking or in an external mp3 player) we realize that the time reported and reality is very different, sometimes even seconds.
For example MediaPlayer.seek(Duration.millis(2000)) results seeking to 0 seconds. A 2 second failure rate is not acceptable.
With WAV it seems to work. Though it does not with MP3.
The two workarounds I think so far are possible:
Writing an MP3 Decoder and Player which doesn't have the bug
Using uncompressed WAV files
Does anyone know anything better?
If anyone needs the source code there isn't much more in it:
public static void main(String[] args) {
MediaPlayer player = null;
JFXPanel fxPanel = new JFXPanel(); //To initialize JavaFX
try {
String url = new File("E:\\Music\\test.mp3").toURI().toURL().toExternalForm();
player = new MediaPlayer(new Media(url));
System.out.println("File loaded!");
} catch (MalformedURLException e) {
//e.printStackTrace();
System.out.println("Error with filename!");
System.exit(0);
}
player.play();
System.out.println("Playing!");
while (true)
{
Scanner reader = new Scanner(System.in);
String string = reader.nextLine();
if (string.equals("Exit")) System.exit(0);
else if (string.equals("Seek"))
{
player.seek(Duration.millis(2000)); //this seeks to the beggining of the file
player.pause();
try {
Thread.sleep(100); //player.getCurrentTime() doesn't update immediately
} catch (InterruptedException e) { }
System.out.println("Time: " + player.getCurrentTime().toMillis() + " / " + player.getTotalDuration().toMillis());
player.play();
}
}
}
I would recommend using the javazoom library. It is an open source java library that already has this stuff written without errors(At least none that I found).
Source
http://www.javazoom.net/index.shtml
Place your call to the seek method off the UI thread or your UI will hang.
new Thread(() ->player.seek(Duration.millis(2000))).start();
So i'm writing this sort of game in Java where you play like Guitar Hero. I can play both parts of the song (the song and the guitar) and so far so good.
Next, i needed to mute the guitar part when someone missed a key. The problem is, there's a 1sec or so delay from the moment i mute the Clip until it actually mutes.
How can this delay be fixed?
try {
audioIn = AudioSystem.getAudioInputStream(new File("guitar.wav"));
guitar = AudioSystem.getClip();
guitar.open(audioIn);
} catch (Exception e) {}
guitar.start();
volume = (BooleanControl) guitar.getControl(BooleanControl.Type.MUTE);
An in the game loop:
if (missedKey()) {
volume.setValue(true);
} else {
volume.setValue(false);
}
Ended up following Radiodef's answer and use byte streams. I have a buffer filled with 0's and i either output that or the normal audio to the stream.
I'm reading in audio files in 16 and 24 bit sampling bit depths and parsing them to determine their lengths without difficulty. However when reading a 32 bit file, I get
javax.sound.sampled.UnsupportedAudioFileException: could not get audio input stream from input file
at javax.sound.sampled.AudioSystem.getAudioInputStream(AudioSystem.java:1170)
...
The 32 bit test file is manually encoded in the same manner as the others (linear PCM). I'm wondering if AudioSystem doesn't support 32 bit Wavs, or if there might be a workaround. For reference, here's my class:
import java.io.*;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
public class soundUtility {
public static double getWavDuration(File filename)
{
AudioInputStream stream = null;
try
{
stream = AudioSystem.getAudioInputStream(filename);
AudioFormat format = stream.getFormat();
return filename.length() / format.getSampleRate() / (format.getSampleSizeInBits() / 8.0) / format.getChannels();
}
catch (Exception e)
{
e.printStackTrace();
return -1;
}
finally
{
try { stream.close(); } catch (Exception ex) { }
}
}
public static void main(String[] args) {
try {
// ===== TESTS: toggle these calls to test the included files =====
// File soundFile = new File("16bit.mono.441k.30secs.wav");
// File soundFile = new File("24bit.48k.11secs.stereo.wav");
File soundFile = new File("32bit.Floating.Stereo.48k.wav");
// ===========
System.out.println(getWavDuration(soundFile));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Thanks for any insight.
Old question, but it's something I was just looking into for myself. My tests can confirm that 16bit 48kHz PCM works, and 32bit doesn't.
However my tests also imply that 24 bit doesn't work:
No line matching interface Clip supporting format PCM_SIGNED 48000.0 Hz, 24 bit, stereo, 6 bytes/frame, little-endian is supported.
This is a .wav file created at 96Khz, 32bit and exported from Audacity where it was rendered as a 24bit 48Khz wav.
This would seem reflected in the doc: https://docs.oracle.com/javase/8/docs/technotes/guides/sound/
"Sound formats: 8-bit and 16-bit audio data, in mono and stereo, with sample rates from 8 kHz to 48 kHz"
So, no 32 bit floating available, I'm afraid, and I cannot reproduce your result that 24 bit floating works.