Pesquisa personalizada

2008/07/22

Java: Sound at my Linux machine... It's ridiculous!

All right... Where is the sound?
I love my Kubuntu box, but I'm unable to play any sound from Java. Why? I think that the JVM needs exclusive access to the sound system, otherwise, nothing is played, or, other times, errors of kind LineUnavailableException are throwed. And, yes, I'm already using ALSA. It's serious, 'cause, except when I have exclusive access to the sound system, nothing bellow are working:

  • Toolkit.getDefaultToolkit().beep()
    No beep...
  • File file=new File("/opt/jdk1.6.0_06/demo/applets/JumpingBox/sounds/danger.au");
    AudioClip audioClip = Applet.newAudioClip(file).toURI().toURL();
    audioClip.loop();
    audioClip.play();

    No sound (or error)... nothing
  • System.out.print("\007");
    System.out.flush();

    No beep here, too..
  • String[] cmd = {"sh", "-c", "printf \"\\7\""};
    Runtime.getRuntime().exec(cmd);

    Again, nothing..

After search about this, I check that the current Java version (Hotspot 1.6.0_06) has some limitations about the sound. To Java share (and play) the sound system with others applications, the sound board needs to have mixer feature by hardware - software emulation isn't supported by Java. If another application is using the sound system, Java cannot play nothing. It's very displeasure.. because to listen a simple "beep" from my Java application, I must disable all other application that do sound usage.

Workaround = Gambiarra or Cat (brazilian ;)

To play some audio file with my Java application, I can do:
String path = "/opt/jdk1.6.0_06/demo/applets/JumpingBox/sounds/danger.au";
String[] cmd = {"sh", "-c", "aplay " + path};
Runtime.getRuntime().exec(cmd);
To generate some kind of noise (sound), uses:
String data = "09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azaZ";
data += "09azaZ09azaZ09azaZ09azaZ09azAZ";
data += "09aZAz09aZAz09aZAz09aZAz09aZAz";
data += "09aZAz09aZAz09aZAz09aZAz09aZAz";
String[] cmd = {"sh", "-c", "echo " + data + " | aplay -r 4"};
Runtime.getRuntime().exec(cmd);

Also we can use the festival to "say" "Beep, Beep, Beep" (or anything else). See:

String text = "Beep, Beep, Beep";
String[] cmd = {"sh", "-c", "echo \"" + text + "\" | festival --tts" };
Runtime.getRuntime().exec(cmd);

But now, if we want to create our wav sound data and play it on the fly, just do:

/** 
 * Sound utility class. 
 * @author Marcio Wesley Borges marciowb@gmail.com
 * @version 0.001, 2008-07-22
 */
public final class SoundUtil {
        
private static final AudioFormat AF = new AudioFormat(8000f, 8, 1, true, false);

public static boolean isSoundWorking() {
    SourceDataLine sdl = null;
    try {
        sdl = AudioSystem.getSourceDataLine(AF);
        sdl.open(AF);
    } catch (LineUnavailableException ex) {
        return false;
    } finally {
        if (sdl!=null)
            sdl.close();
    }
    return true;
}

public static void beep() {
    if (isSoundWorking())
        Toolkit.getDefaultToolkit().beep();
    else
        mimeBeep();
}

private static void mimeBeep() {

    final int SAMPLE_RATE = 8000; // sample per second
    final int SAMPLE_PER_MS = SAMPLE_RATE/1000; // sample per ms

    //make your adjustments here
    final int HZ = 400; // Frequency in hertz
    final int MSECS = 50; // Duration        

    final byte[] raw = new byte[MSECS * SAMPLE_PER_MS]; //duration_ms * (sample per ms)
    for (int i = 0; i < raw.length; i++) {
        double angle = i / (SAMPLE_RATE / (double) HZ) * 2.0d * Math.PI;
        raw[i] = (byte) (Math.sin(angle) * 81.11d);
    }

    final String[] cmd = {"sh", "-c", "aplay -c1 -fU8 -r" + SAMPLE_PER_MS };

    Process p = null;
    try {
        p = Runtime.getRuntime().exec(cmd);
        p.getOutputStream().write(raw);
        p.getOutputStream().close();
        Thread.sleep(MSECS+30); //Wait the outer process to play.
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        if (p!=null)
            p.destroy();
    }
}

public static void main(String[] args) {
    beep();
}

}//end class SoundUtil

Thanks to

Labels: , ,

2 Comments:

Anonymous Anonymous said...

I have the same problem with my application using JavaLayer on Gentoo Linux. Have any of these problems been fixed with Java 7 or 6 update 10?

11 September, 2008 12:29  
Blogger Sushant Kunal said...

I really appreciate you putting these in this blog. Has been very helpful.

I heard that older version of Linux do not have ALSA drivers in built. What sud be the workaround there?

28 June, 2010 08:53  

Post a Comment

<< Home