Can't hear sound from bluetooth speaker - java

I'm using Raspberry Pi with Ubuntu 22.10 and Java 19.
I'm trying to play a sound with the following code:
private static void testMixers() throws IOException, UnsupportedAudioFileException
{
try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(Objects.requireNonNull(Listener.class.getResourceAsStream("/alarmSound.wav")))))
{
final Scanner scanner = new Scanner(System.in);
for (Mixer.Info info : AudioSystem.getMixerInfo())
{
System.out.println("Testing sound for mixer \"" + info + "\"...");
try (Clip clip = AudioSystem.getClip(info))
{
clip.open(audioInputStream);
clip.setFramePosition(0);
clip.start();
System.out.println("Did you hear any sound? (true|false)");
if (Boolean.parseBoolean(scanner.nextLine()))
return;
}
catch (Exception e)
{
System.out.println(e);
}
}
}
System.out.println("Mixer not found!");
}
alarmSound.wav is under src/main/resources (this is a Maven project).
AudioSystem.getMixerInfo() gives me the following Mixer.Info[]:
Port Headphones [hw:0], version 5.19.0-1006-raspi
Port vc4hdmi0 [hw:1], version 5.19.0-1006-raspi
Port vc4hdmi1 [hw:2], version 5.19.0-1006-raspi
Headphones [default], version 5.19.0-1006-raspi
Headphones [plughw:0,0], version 5.19.0-1006-raspi
vc4hdmi0 [plughw:1,0], version 5.19.0-1006-raspi
vc4hdmi1 [plughw:2,0], version 5.19.0-1006-raspi
Only the Headphones Mixers don't give the error:
java.lang.IllegalArgumentException: Line unsupported: interface Clip supporting format PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian
I didn't tested the headphone jack.
This is completely different from Ubuntu 22.04 which gave more Mixer.Infos.
Is there any workaround? it seems that every Ubuntu version requires different workaround for being able to play a sound that can be heard not through the headphones (but from bluetooth speakers or via HDMI for example).

Related

Java Sound LineUnavailableException: Format not supported

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

Can't select microphone TargetDataLine. Seems, I can use only input that is default for my OS system

I'm trying to read audio input from different microphones. I found very wired behavior. It seems, that whatever microphone I choose, I can grab sound only for microphone that is default for system. I know this, because sum of signals from both 'microphones' is similar, what I hit default system microphone. What am I doing wrong?
import java.util.LinkedList;
import java.util.List;
import javax.sound.sampled.*;
public class AudioTest {
public static void main(String[] args) throws LineUnavailableException {
AudioFormat format = new AudioFormat(8000.0f, 16, 1, true, true);
// list all microphones
List<Line.Info> micList = enumerateMicrophones();
// assign microphone1
TargetDataLine microphone1 = (TargetDataLine) AudioSystem.getLine(micList.get(1));
// assign microphone2
TargetDataLine microphone2 = (TargetDataLine) AudioSystem.getLine(micList.get(2));
// read sound from microphone1
byte[] data1 = new byte[1000];
microphone1.open(format);
microphone1.start();
microphone1.read(data1, 0, 1000);
// read sound from microphone2
byte[] data2 = new byte[1000];
microphone2.open(format);
microphone2.start();
microphone2.read(data2, 0, 1000);
// Print sum (bigger for laud sounds).
// Similar values means - both microphones are in fact the same
System.out.println("Mic1: " + calcSum(data1));
System.out.println("Mic2: " + calcSum(data2));
}
static long calcSum(byte[] data) {
long sum = 0;
for(byte bt: data) {
sum += Math.abs(bt);
}
return sum;
}
public static List<Line.Info> enumerateMicrophones() {
List<Line.Info> out = new LinkedList<Line.Info>();
Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
for (Mixer.Info info : mixerInfos) {
Mixer m = AudioSystem.getMixer(info);
Line.Info[] lineInfos = m.getTargetLineInfo();
for (Info lineInfo : lineInfos) {
if (lineInfo.getLineClass().equals(TargetDataLine.class)) {
out.add(lineInfo);
System.out.println("Mixer: " + info.toString() + " Line: " + lineInfo.toString());
}
}
}
return out;
}
}
The mixers/lines printed are:
Mixer: PulseAudio Mixer, version 0.02 Line: interface TargetDataLine supporting 42 audio formats, and buffers of 0 to 1000000 bytes
Mixer: default [default], version 4.15.0-108-generic Line: interface TargetDataLine supporting 512 audio formats, and buffers of at least 32 bytes
Mixer: PCH [plughw:0,0], version 4.15.0-108-generic Line: interface TargetDataLine supporting 24 audio formats, and buffers of at least 32 bytes
Sometimes I have one more device:
Mixer: Device [plughw:1,0], version 4.15.0-108-generic Line: interface TargetDataLine supporting 4 audio formats, and buffers of at least 32 bytes
Sometimes not. Wired
What my operating system Ubuntu sees:
#arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC269VC Analog [ALC269VC Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 1: Device [USB PnP Sound Device], device 0: USB Audio [USB Audio]
Subdevices: 1/1
Subdevice #0: subdevice #0
I have one microphone connected by USB, and one build into laptop.

Java audio fails to play wav file in Linux

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

how get available audio devices connected using java in mac osx

i was trying to get the available mixtures in MAC OSX using the below code. Even though i connected 3 different audio device, and able to see the same in system sound settings,below code doesn’t display all. i.e. mix.getMixerInfo()).isLineSupported(info) is not allowing to display anything and
even not able to connect using java .
The same is working fine with windows version.
public static void main(String[] args) {
String sf_ringtone = "/Users/abc.WAV";
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(new File(sf_ringtone));
} catch (UnsupportedAudioFileException ex) {
System.out.println(ex.getStackTrace().toString());
} catch (IOException ex) {
System.out.println(ex.getStackTrace().toString());
}
AudioFormat format=null;
format = stream.getFormat();
if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED) {
format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
format.getSampleRate(), format.getSampleSizeInBits() * 2,
format.getChannels(), format.getFrameSize() * 2,
format.getFrameRate(), true); // big endian
stream = AudioSystem.getAudioInputStream(format, stream);
}
DataLine.Info info = new DataLine.Info(Clip.class, stream.getFormat(),
((int) stream.getFrameLength() * format.getFrameSize()));
ArrayList<Mixer> mixerList = (ArrayList<Mixer>) getAllMixer();
for(Mixer mix:mixerList) {
System.out.println(" Mixture "+mix.getMixerInfo().getName());
}
ArrayList<String> cmbRingtonePlayback = new ArrayList<String>();
for (Mixer mix : mixerList) {
if (AudioSystem.getMixer(mix.getMixerInfo()).isLineSupported(info)) {
System.out.println(supported mixture :: "+mix.getMixerInfo().getName());
}
}
}
output:
Mixture Java Sound Audio Engine
Mixture Built-in Input
Mixture JABRA TALK
Mixture Logitech USB Headset
supported mixture :: Java Sound Audio Engine
Is this a limitation of sound API in mac osx ?, or is there any other way to do this in mac?
This issue faced was with apple Java for OS X 2014-001. Uninstalled java 1.6 from apple and tried with oracle java 1.7. Now this is working fine in both mac osx and windows.

Java Change Another Applications' Sound

I am now interested in changing volume of my system. I am running on Windows 8 and I found several of codes that can change "java application's" volume only. But I want to change not only my applications volume but also system's volume.
I used this but did not worked:
Mixer.Info[] infos = AudioSystem.getMixerInfo();
for (Mixer.Info info : infos) {
Mixer mixer = AudioSystem.getMixer(info);
if (mixer.isLineSupported(Port.Info.HEADPHONE)) {
Port port;
try {
port = (Port) mixer.getLine(Port.Info.HEADPHONE);
port.open();
if (port.isControlSupported(FloatControl.Type.VOLUME)) {
FloatControl bc = (FloatControl) port
.getControl(FloatControl.Type.VOLUME);
System.out.println(info);
System.out.println(" - " + bc.getValue());
bc.setValue(0.1f);
}
port.close();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
}
Thi code only changes javaw.exe's volume. For example if anyone plays youtube video on google chrome, it is impossible to change chrome's volume.
Which is the best way to control all system's volume ?
Thanks in advance.

Categories