Tuesday 7 January 2014

Playing Audio With Java

...Train related comment goes here....
For some reason, finding out how to play arbitrary audio using Java took ages. To save you the effort - here is the code.

All Sonic Field source code is AGPL 3.0 licensed.

The trick is to write a set of 16 bit samples into a byte array and then 'write' that into an SourceDataLine. The sequence is something like this:
  1. Get a source line (the default normally).
  2. Open it.
  3. Write to it.
  4. Drain it (wait till it has stopped playing).
  5. Stop it.
  6. Close it.
/* For Copyright and License see LICENSE.txt and COPYING.txt in the root directory */
package com.nerdscentral.audio;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import com.nerdscentral.audio.volume.SF_Normalise;
import com.nerdscentral.sfpl.Caster;
import com.nerdscentral.sfpl.SFPL_Context;
import com.nerdscentral.sfpl.SFPL_Operator;
import com.nerdscentral.sfpl.SFPL_RuntimeException;

public class SF_Monitor implements SFPL_Operator
{

    private static final long serialVersionUID = 1L;

    @Override
    public String Word()
    {
        return Messages.getString("SF_Monitor.0"); //$NON-NLS-1$
    }

    @Override
    public Object Interpret(Object input, SFPL_Context context) throws SFPL_RuntimeException
    {
        SFData dataIn = Caster.makeSFData(input).replicate();
        SF_Normalise.doNormalisation(dataIn);
        try
        {
            AudioFormat af = new AudioFormat((float) SFConstants.SAMPLE_RATE, 16, 1, true, true);
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, af);
            SourceDataLine source = (SourceDataLine) AudioSystem.getLine(info);
            source.open(af);
            source.start();
            byte[] buf = new byte[dataIn.getLength() * 2];
            for (int i = 0; i < buf.length; ++i)
            {
                short sample = (short) (dataIn.getSample(i / 2) * 32767.0);
                buf[i] = (byte) (sample >> 8);
                buf[++i] = (byte) (sample & 0xFF);
            }
            source.write(buf, 0, buf.length);
            source.drain();
            source.stop();
            source.close();
            return input;
        }
        catch (Exception e)
        {
            throw new SFPL_RuntimeException(Messages.getString("SF_Monitor.1"), e); //$NON-NLS-1$
        }
    }
}


No comments:

Post a Comment