I've come across an odd behaviour when querying the names of audio capture devices in a JavaFX thread vs a 'plain' Java thread.
Normally I probably would not have noticed, except that I have some non-JavaFX applications which share a common configuration pool with a JavaFX application, and this caused some issues when programmatically setting a capture audio device to use.
In a nutshell, the same query main in a plain Java thread returns capture device names truncated to 31 characters, whereas in a JavaFX thread the capture device names are 'full'.
For example, truncated results...
Primary Sound Capture Driver
Microphone (HD Pro Webcam C910)
Microphone (Realtek(R) Audio)
Headset (Bose QuietComfort 35 H
Headset Microphone (Oculus Virt
...vs 'full' results:
Primary Sound Capture Driver
Microphone (HD Pro Webcam C910)
Microphone (Realtek(R) Audio)
Headset (Bose QuietComfort 35 Hands-Free)
Headset Microphone (Oculus Virtual Audio Device)
The minimal JavaFX application code below demonstrates the issue, and in my testing it happens in both 32 bit and 64 bit versions of the Java8 JDK on Windows 10. I have not tested non-Windows platforms, so I am not sure if the behaviour occurs outside this environment.
The showCaptureDeviceNames() method does the query and printout of the device names. If called from the main method, truncated devices names are shown. If commented out of main and triggered by the button press in the JavaFX thread, 'full' device names are shown.
Interestingly, if called in main first, and later in the JavaFX thread, truncated names are shown, indicating that there is some sort of caching going on (which luckily provides a workaround for my problem). Simply calling AudioSystem.getMixerInfo(); all by itself is sufficient to induce the 'caching' effect
Also, this only happens for capture devices; if the TargetDataLine.class is switched to SourceDataLine.class to filter for playback devices, the device name is always 'full'.
Can anyone offer any hints as to why this might be happening?
My current workaround is to ensure that the JavaFX application queries all audio devices before the JavaFX thread kicks off by calling AudioSystem.getMixerInfo(); in main(...). This works, but... as a "fix" it looks more like a mistake sitting there on its own for no apparent reason (although adequate commenting will help future developers who come across it).
I'd love for someone to tell me I'm doing something wrong and there's a better way.
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Line;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Mixer.Info;
import javax.sound.sampled.TargetDataLine;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class CaptureDeviceListTest extends Application
{
@Override
public void start( Stage primaryStage )
throws Exception
{
primaryStage.setTitle( "Test Capture Device Queries" );
Button btn = new Button( "Get capture device names" );
btn.setOnAction( event -> showCaptureDeviceNames() );
StackPane root = new StackPane();
root.getChildren().add( btn );
primaryStage.setScene( new Scene( root, 200, 200 ) );
primaryStage.show();
}
private static void showCaptureDeviceNames()
{
// filter for capture devices - change to `SourceTargetData.class` for playback devices
Line.Info captureLine = new Line.Info( TargetDataLine.class );
for( Info mixerInfo : AudioSystem.getMixerInfo() )
{
Mixer mixer = AudioSystem.getMixer( mixerInfo );
if( mixer.isLineSupported( captureLine ) )
System.out.println( mixerInfo.getName() );
}
}
public static void main( String[] args )
{
// COMMENT / UNCOMMENT THE NEXT LINE
// showCaptureDeviceNames();
launch( args );
}
}