How can a containerized JAR (Java) access the default system mixer? - java

I previously took this question down, it's going back up since it's still a problem we're encountering. We have a JAR that we're resurrecting from three years ago. Back then (before I joined the project), it ran bare-metal (presumably on an RPM-based Linux distribution). Before a developer left, he had containerized it (CentOS7) and left us with very little documentation.
Ultimately, the JAR is calling the function getSourceDataLine() described here: https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioSystem.html#getSourceDataLine(javax.sound.sampled.AudioFormat)
The returned line will be provided by the default system mixer, or, if not possible, by any other mixer installed in the system that supports a matching SourceDataLine object.
However, the program encounters an IllegalArgumentException
if the system does not support at least one source data line supporting the specified audio format through any installed mixer
java.lang.IllegalArgumentException: No line matching interface SourceDataLine supporting format PCM_SIGNED 8000.0 Hz, 16 bit, stereo, 4 bytes/frame, big-endian is supported.
at javax.sound.sampled.AudioSystem.getLine(AudioSystem.java:479)
at javax.sound.sampled.AudioSystem.getSourceDataLine(AudioSystem.java:606)
I inspected the contents of the sound.properties file: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64/jre/lib/sound.properties inside the container and ./jvm/java-1.8.0-openjdk-1.8.0.322.b06-2.el8_5.x86_64/jre/lib/sound.properties on the host. Both are completely commented/empty, so I assume the default system mixer is being used.
In the docker/docker-compose run parameters, I tried adding all capabilities to the Linux container, and that did not resolve the issue (regardless of permissions, I assume the needed sound card device doesn't appear at all).
I suspected there was a --device flag that could be passed to the docker run command (or docker-compose yaml). However, I tried running the container as privileged (access to all host devices), and this did not allow access to a system mixer.
I may try inspecting the devices available to the Java runtime with code similar to here: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4269230
I have heard "audio is difficult in containers". I may try doing full virtualization with QEMU to to try to recreate the host environment, but that seems a little drastic. If anyone has worked with the Java audio system as well as containers and could point me in the right direction, that would be much appreciated.

In Linux accessing the audio devices is all done via the filesystem. So if you mount the necessary files (/dev/audio?) into the container and give suitable privileges you should be good to go.

Related

How to get absolutePath of a usb device on linux/ubuntu with java

i am working on a method to import files which are stored on an usb device to my database.
I already done it for Mac and Windows, but I don't know how to get the path to the usb device when the application is used under Linux/Ubuntu with java.
Is there a way to find the path?
I don't think there is any way that is guaranteed to work on all Linux systems. There are a few approaches I have used, with varying degrees of success.
A method that requires no external utilities is to enumerate and parse the pseudo-files in /dev/disk, /proc/mounts, and /sys/bus/usb, and build a representation of the USB devices that hold storage. There might be more than one, so you might still have to make some guesses, or offer users a choice. This is a pretty tedious method, but I think it is the most general. You can just look at /media, /run/media, as somebody else suggested, but this only works on some Linux installations, and only if that form of auto-mounting is enabled.
A less tedious approach is to invoke the utility udisksctl from your program. I think this utility exists for almost all Linux variants, but it isn't always installed by default. The output of the utility has to be parsed, but it's less hassle that working directly with the kernel psedofiles.
To get a list of disk devices, execute udisksctl status. This will tell you the model name, which will often include the text "USB", and the block device (e.g., /dev/sdb). Then you can execute udisksctl info -b /dev/sdb and look for the line that begins "MountPoints". If the device is not mount you could, if you wished, force it by executing udisksctl mount.... There's a heap of other useful stuff that udisksctl can do -- see its man page.
In Java you can use Runtime.exec() to run udisksctl, but it's probably friendlier to users to check that it is installed first, by check for the existence of /usr/bin/udisksctl.
The easiest way I found is using the Volumes directory.
/Volumes/NAME_OF_USB_DRIVE/file.whatever

Java program not displaying on high DPI screens

I am very new to Java programing but am managing a deprecated Java software as a part of my job. The old program renders perfectly on lower resolution screens but as the company moves to a higher DPI line of devices (primarily Microsoft Surfaces), the program is very small. I found many ways online to adjust the resolution of other programs through the use of Manifest files but can't seem to get anything to work. I also saw that the swing.properties file can be changed to fix it, but the program doesn't use swing. Are there any external files that can be used to change the resolution for this program?
The issue arises on Windows 10 high DPI devices and the program comes with it's own version of Java installed in the users AppData folder and thus does NOT use the Java installed regularly on the device.
I had the same problem and tried several different solutions with limited success. The only one which worked for me was to adjust the properties of the java.exe and/or the javaw.exe executables as described in this superuser answer:
https://superuser.com/a/1207925
This worked perfectly for me.

Have serial port working under Linux 64 bits with Java

I need to connect to and use a serial port using Java from a Linux 64bits machine. In my case an USB to Serial adapter since laptop have no more serial port since a while...
After struggling a lot, I got it working. Then changing my laptop, I lost it ;(. But finally got it working again.
I'll document how to do it in the answer.. so I can find it easily back next time I need it :D
First, you need the Java JAR file RXTX and the serial library for Linux (32 and 64 bits are different...).
Main library can be found here: http://rxtx.qbang.org/wiki/index.php/Download
But a nice package with a run-install.sh copying libraries directly in right place can be found here: http://fizzed.com/oss/rxtx-for-java
After that, make sure your user is member of correct groups. In openSuse, it requires lock, uucp, and dialout groups. Log off and login to make the groups effective.
To test it, there is a nice explanation and program here: http://pharos.ece.utexas.edu/wiki/index.php/How_to_Access_Your_Serial_Port_using_Java#Test_the_Installation_of_RXTX_on_Ubuntu_Linux

How to identify a specific USB Port

I'm in a project that must perform "tests" on USB Pen Drives.
For that, I'm using a USB Hub (49 port Asic Miner - I'm using it due the number of ports).
I intend to plug 49 USB Pen drives on the HUB and test them using a Linux Java App. The test consists on "check if it is recognized", "storing and deleting data" and "check the size".
My problem is on the first step. If there is any Pen Drive that is not working properly, the system will recognize 48 pen drives, but I'll never know the specific device that is not working.
My question is: Is there any way to know the address (or something like that) of a specific usb port on a usb hub? For example: If I connect just one USB Pen Drive on port "34", my software will know that the device is connected on that specific port.
Thank you very much for your time and for your help!
Use Runtime to execute the Linux command mount. It will list all devices currently mounted, manually or automatically, like:
/dev/sdb5 on /media/xdrive type ext4 (rw,nosuid,nodev,uhelper=udisks)
/dev/sdj1 on /media/Website type vfat (rw,nosuid,nodev,uhelper=udisks,uid=1000,gid=1000,shortname=mixed,dmask=0077,utf8=1,flush)
Here, second line describes the USB stick I have just plugged in (running Ubuntu). It can be easily recognized from the filesystem (vfat, and the hard drive above uses ext4) and it is mounted on /media/Website (because 'Website' is the stick label that has been used when formatting the stick in vfat). It should be trivial to parse the output and locate the mounted sticks (when formatting, I suggest to give the known labels matching the stick numbers).
If needed, you can put more information into the small file directly on the stick.
You can capture the output through the Process.getOutputStream().
When done, you can safely unmount the sticks before pulling them out:
umount /media/Website
You should check these libraries jUSB and usb2java (they are a little out of date though).
However JNI will allow you to create an interface that interacts with different Operating Systems. This would allow you for cross-platform functionality. I'd look into libUSB (because you are looking for Linux) for a good start!
Hope that helps! :)

FreeTTS, Java, Linux: Workaround for "LINE UNAVAILABLE: Format is ..."

When I run FreeTTS examples, I get this error:
LINE UNAVAILABLE: Format is pcm_signed 16000.0 Hz 16 bits 1 channel big endian
In this post, Freetts problem in Java
someone claims it's a known Linux/Java sound bug and has a workaround,
linking to http://forums.sun.com/thread.jspa?threadID=5189363 .
But this link does not work anymore since Oracle screwed it.
Archive.org seems not to have this page archived.
Does anyone have the workaround / patch for FreeTTS?
Thanks,
Ondra
Linux's ALSA is one large, complex API. OpenJDK and Sun's JDK seem to use it differently. Most modern Linux distributions also use PulseAudio, which virtualizes ALSA so that all audio goes through PulseAudio for software mixing before going to ALSA for playback.
When nothing is accessing the sound card, and Java is the only user, it tends to work. However when something else has the sound card open, Java apps quickly break with both your error and "javax.sound.sampled.LineUnavailableException: Audio Device Unavailable".
One potential workaround is to enumerate all mixers in the system with AudioSystem.getMixerInfo(), then try to open the line with AudioSystem.getSourceDataLine(format, mixerInfo) for the mixers you want. Some will work better than others. In particular the "Java Sound Audio Engine" and the "default [default]" mixers, if they exist, tend to work.
The only solution if you don't want to modify the FreeTTS source code though, is to install pulse-java. This registers a special PulseAudio sound provider, which bypasses the ALSA virtualization and goes directly to PulseAudio. Ubuntu installs this as part of its OpenJDK package.
Someone should really patch Java Sound to play with ALSA in a friendlier way. For one ALSA device names should be prefixed with plug: to get ALSA to convert sound formats and sample rates on the fly. And the other rules for safe ALSA subset should also be followed.
Hmm, I had better luck googling after asking the question, so...:
http://workorhobby.blogspot.com/2011/02/java-audio-freetts-line-unavailable.html
A big thanks to the author.
Update: Actually, this is not a nice workaround since it will keep FreeTTS on hold until the line is free.
FWIU, the mentioned patch had better solution - not demanding exclusive access or such.
Update: I've compiled a FreeTTS troubleshooting page.
A program based on FreeTTS, the free text-to-speech engine for Java, was getting occasional errors
"LINE UNAVAILABLE: Format is ..."
Turns out there is no Java Exception or other mechanism to detect this error that occurs inside the FreeTTS library. All you get is the message on System.out, so there is no good way to react programatically.
Workaround: Configure the FreeTTS audio player to attempt accessing the audio device more than once until it succeeds. In this example, a short delay of 0.1 seconds is used to not miss an opportunity to grab the audio device; we keep trying for 30 seconds:
System.setProperty("com.sun.speech.freetts.audio.AudioPlayer.openFailDelayMs", "100");
System.setProperty("com.sun.speech.freetts.audio.AudioPlayer.totalOpenFailDelayMs", "30000");
If the audio device is permanently used by another program, there is of course no way to get access. Under Linux, this command will display the ID of the process that is currently holding the audio device, so you can then try to get rid of the offending program:
/sbin/fuser /dev/dsp
Regarding the link screwed by Oracle - given that older SO answer you refer mentions horrendous java linux sound bug that is still not fixed and suggests to check the third post, it is likely that lost thread was migrated to:
https://forums.oracle.com/forums/thread.jspa?threadID=2206163
above thread starts with reference to JMF Bug 4352921 at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4352921_
third post in the thread says "Yes, according to the API docs it's a "catastrophic" error from which no player can return safely. Which is why it's so strange to be able to trigger it so easily..."
workaround to the problem discussed is described in the sixth post as follows:
I stumbled upon a blog post that suggested that Java needs to be told to use the OSS libraries as it's not yet up to ALSA. The command to use was "padsp" which forces the application to use OSS. So if I call "padsp jmstudio", it now plays and mixes the audio just fine. I tried it with my application also, which prefetches a number of players in the same JVM, and they all prefetched perfectly. So it would seem, for now, JMF applications on Linux may need to be called through padsp.

Categories