Search code examples
javaaudiomixerjavax.sound.sampled

Multiple Mixer outputs in Java


I am trying to make Java play different stereo audio in two outputs (front and back audio jacks).

My sound card is configured as to treat both outputs independently, and in the Windows mixer I can make them emit a test sound separately, so it's not a card issue.

I tried the approach on Change Mixer to output sound to in java using different outputs by getting two Clips with different mixers, with AudioSystem.getClip(AudioSystem.getMixerInfo()[i]);. However, this line of code works only with the Java Sound Audio Engine (AudioSystem.getMixerInfo()[0]), which outputs in Windows' default audio output. Anything else throws

java.lang.IllegalArgumentException: Line unsupported: interface Clip supporting format PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian

The following example code generates a 5-second long white noise and plays it for 1 second and then finishes. It prints Mixer information as seen on how do I get Mixer channels layout in java. It currently outputs to the "Java Sound Audio Engine" Mixer, and trying to change to any other Mixer throws the exception above.

import java.io.ByteArrayInputStream;
import java.security.SecureRandom;
import java.util.Random;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;

public class Main {
    static int SAMPLE_RATE = 44100;
    static AudioFormat format = new AudioFormat(
            AudioFormat.Encoding.PCM_SIGNED, // Encoding
            SAMPLE_RATE,    // sample rate
            8,              // sample size in bits
            2,              // channels
            4,              // frame size
            SAMPLE_RATE,    // frame rate
            true);          // is big endian

    static int DURATION = 5;
    Thread soundThread;

    // Noise audio
    static AudioInputStream inputStream = new AudioInputStream(new ByteArrayInputStream(generateNoise(DURATION*2*SAMPLE_RATE)), format, DURATION*SAMPLE_RATE);

    public static void main(String[] args) {
        try {

            // https://stackoverflow.com/questions/12863081/how-do-i-get-mixer-channels-layout-in-java
            Mixer.Info[] mi = AudioSystem.getMixerInfo();
            for (Mixer.Info info : mi) {
                System.out.println("info: " + info);
                Mixer m = AudioSystem.getMixer(info);
                System.out.println("mixer " + m);
                Line.Info[] sl = m.getSourceLineInfo();
                for (Line.Info info2 : sl) {
                    System.out.println("    info: " + info2);
                    Line line = AudioSystem.getLine(info2);
                    if (line instanceof SourceDataLine) {
                        SourceDataLine source = (SourceDataLine) line;

                        DataLine.Info i = (DataLine.Info) source.getLineInfo();
                        for (AudioFormat format : i.getFormats()) {
                            System.out.println("    format: " + format);
                        }
                    }
                }
                System.out.println("");
            }


            // Code only works for AudioSystem.getMixerInfo()[0] 
            final Clip clip = AudioSystem.getClip(AudioSystem.getMixerInfo()[0]);
            clip.open(inputStream);

            Thread soundThread = new Thread(new Runnable() {                
                @Override
                public void run() {
                    try {
                        clip.loop(Clip.LOOP_CONTINUOUSLY);
                        Thread.sleep(1000);
                        clip.close();
                        System.exit(0);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            soundThread.start();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    private static byte[] generateNoise(int size) {
        byte[] out = new byte[size];
        Random r = new SecureRandom();
        r.nextBytes(out);
        return out;
    }
}

Here is the Mixer information. This code ran with both output jacks connected and Windows recognizes as two different output devices (speakers and headphones). It looks like only Java's audio engine can play sounds.

info: Java Sound Audio Engine, version 1.0
mixer com.sun.media.sound.HeadspaceMixer@22c84d9
    info: interface SourceDataLine supporting 8 audio formats
    format: PCM_SIGNED unknown sample rate, 8 bit, mono, 1 bytes/frame, 
    format: PCM_UNSIGNED unknown sample rate, 8 bit, mono, 1 bytes/frame, 
    format: PCM_SIGNED unknown sample rate, 8 bit, stereo, 2 bytes/frame, 
    format: PCM_UNSIGNED unknown sample rate, 8 bit, stereo, 2 bytes/frame, 
    format: PCM_SIGNED unknown sample rate, 16 bit, mono, 2 bytes/frame, big-endian
    format: PCM_SIGNED unknown sample rate, 16 bit, mono, 2 bytes/frame, little-endian
    format: PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian
    format: PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, little-endian
    info: interface Clip supporting 8 audio formats, and buffers of 0 to 4194304 bytes

info: Microsoft ?T?E???h, version Unknown Version
mixer com.sun.media.sound.SimpleInputDevice@7e0df503

info: Stereo Mixer (Realtek High Defi, version Unknown Version
mixer com.sun.media.sound.SimpleInputDevice@4650d89c

info: Port Realtek HD Audio 2nd output (Re, version 6.1
mixer com.sun.media.sound.PortMixer@65bd0dd4

info: Port Stereo Mixer (Realtek High Defi, version 6.1
mixer com.sun.media.sound.PortMixer@78b5f53a
    info: ?}?X? source port

info: Port Speakers (Realtek High Definiti, version 6.1
mixer com.sun.media.sound.PortMixer@b37c60d

I am using Java 6 because of other limitations. Also, some names are broken probably because I'm in a Japanese environment and Eclipse won't fetch the names in the correct encoding (I've tried changing everything to UTF-8 and Shift_JIS but nothing changed, but I guess it's not related to this problem).

In other words, It looks like can't output in Clips from different Mixers because Java can only output to "Java Sound Audio Engine" which goes to Windows' default audio device. Is there any way to get the other Mixers to work? Is there an alternative to use multiple audio outputs?

Update: It seems like this problem was already fixed but I still can't get it to work. I disabled the "Stereo Mix" recording device and ran the example code given in that link, and got this output:

MIXER 0: Java Sound Audio Engine, version 1.0
OUTPUT LINE (SourceDataLine) 0: interface SourceDataLine supporting 8 audio formats
OUTPUT LINE (SourceDataLine) 1: interface Clip supporting 8 audio formats, and buffers of 0 to 4194304 bytes

MIXER 1: Port Realtek HD Audio 2nd output (Re, version 6.1
INPUT LINE (TargetDataLine) 0: HEADPHONE target port

MIXER 2: Port Speakers (Realtek High Definiti, version 6.1
INPUT LINE (TargetDataLine) 0: SPEAKER target port

So it looks like only Java Sound Audio Engine can output sound as both the mixers for the front and back audio jacks are treated as input lines. Played audio through either Clip or SourceDataLine outputs in the Windows' default playback device.

Also, it looks like they solved the problem by using DirectAudio but I still did not figure out how to use them.


Solution

  • While testing in different computers, I solved this problem by upgrading to Java 1.6.0_17 (which was still okay with my environment limitations to call Java from Matlab) so the DirectAudio drivers would be listed (even though it looks like it should work from Java 1.5). Each DirectAudio sound device gives a SourceDataLine and a Clip for each device.