I'm a not-so-knowledgeable person in the Linux world trying to understand an audio problem I have in a project. In short, I'm trying to get an audio output through a Java library called Minim (ddf.minim). I can get an output by listening to an input and enabling "monitoring." Monitoring echoes the input to an output. I cannot however get a working output line independent of this feature.
This application is running on a BeagleBone Black with Debian Buster. This is an ARM microcontroller. My Java application starts up with the system as a systemd service as the root user. It runs in headless mode but with a virtual frame buffer (xvfb
) because it's a Processing application. I have a USB hub connected to the BeagleBone, which hosts a USB-to-AUX adapter. The AUX side splits into the pink/green input/output cables. I have a mic connected to the pink end and earbuds into the green end.
When Minim's getLineIn()
is used to get an input, no errors are seen. Enabling monitoring doesn't generate errors either. Using getLineOut()
will show errors that look like this:
==== JavaSound Minim Error ====
==== Couldn't open the line: line with format PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian not supported.
==== JavaSound Minim Error ====
==== Unable to return a SourceDataLine: unsupported format - PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian
Here's my code that asks for an output:
Mixer.Info mixers[] = AudioSystem.getMixerInfo();
for(Mixer.Info info : mixers)
{
try
{
mixer = AudioSystem.getMixer(info);
if(!mixer.isOpen())
{
mixer.open();
}
M.setOutputMixer(mixer);
LineOut = M.getLineOut();
}
catch(Exception e)
{
mixer = null; //This mixer can't be used.
continue;
}
if(mixer != null && LineOut != null)
{
break;
}
}
Errors don't always throw an exception, so that's why I have a null check. Interestingly, even when error messages show up, LineOut
will not be null. I can pass it to other methods but I can never hear anything. Only two mixers ever show up, one for the USB adapter and one that throws an exception once opened.
Playing example files with aplay
used to fail, but once I apt install-ed PulseAudio, they worked.
I don't have a soundcard 0, but the USB adapter shows as soundcard 1. I've changed the asound.conf
to have these extra lines at the bottom:
defaults.pcm.card 1
defaults.ctl.card 1
I've also added these lines in the sound.properties config file under /usr/lib/jdk1.8.0_251/jre/lib
after reading about Java's weak support for PulseAudio:
javax.sound.sampled.Clip=com.sun.media.sound.DirectAudioDeviceProvider
javax.sound.sampled.Port=com.sun.media.sound.PortMixerProvider
javax.sound.sampled.SourceDataLine=com.sun.media.sound.DirectAudioDeviceProvider
javax.sound.sampled.TargetDataLine=com.sun.media.sound.DirectAudioDeviceProvider
I've used the AudioSystem
class to query for the supported formats of the mixer. They're the same formats I'm requesting, so I'm not confident the error message accurately describes the issue. I've tried mono, stereo, different buffer sizes... and probably other things.
I've changed my application to only seek an output, and not look for an input. The issue persists.
This code works fine on Windows, which is where I test things before porting it to the Debian machine.
I've read a lot about the general fragility of sound on Linux, and so my suspicion is that I'm doing something wrong there or Java is just not fit for outputting sound with my unique setup. Help?
I figured it out!
Calling mixer.open()
was causing problems. I guess Minim does this itself and expects users to leave it unopened when requesting a line.
However, I did also have other discoveries.
getLineOut()
would fail. You can, however, call Minim.stop()
to start over and get resources again.AudioPlayer
just seems broken. I can't get it to request a working output, but I found that FilePlayer
works fine for some reason. You can even ask for an output with getLineOut()
before making a FilePlayer
. Maybe you have to call setOutputMixer()
(without also calling getLineOut()
) before getting an AudioPlayer
in order for it to work.