Package com.sun.media.sound

Source Code of com.sun.media.sound.SimpleOutputDevice

/*
* This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com>
* Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/)
*/

/*
* @(#)SimpleOutputDevice.java  1.27 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/

package com.sun.media.sound;

import java.util.Vector;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Control;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Port;
import javax.sound.sampled.SourceDataLine;


/**
* Simple output device.  Contains mixer and data line functionality for
* simple wave-out output devices.
*
*
  1.27 03/01/23
* @author Kara Kytle
*/
class SimpleOutputDevice extends AbstractMixer {


    // INSTANCE PROPERTIES
    private AudioFormat format = null;
    private int bufferSize = AudioSystem.NOT_SPECIFIED;

    /**
     * Fixed set of ports available on the output device.
     */
    private Port[] ports;


    // CONSTRUCTOR

    /**
     * Constructs a new SimpleOutputDevice.
     */
    SimpleOutputDevice(SimpleOutputDeviceProvider.OutputDeviceInfo outputDeviceInfo) {

        super(outputDeviceInfo, // Mixer.Info
              null,             // Control[]
              null,             // Line.Info[] sourceLineInfo
              null);            // Line.Info[] targetLineInfo

        if (Printer.trace) Printer.trace(">> SimpleOutputDevice: constructor");

        // initialize platform-specific values, and load the native library
        Platform.initialize();

        // query our source line format capabilities
        // $$kk: 06.05.99 this is bad; should query the full set of supported formats
        boolean supports8 = nSupportsSampleSizeInBits(8);
        boolean supports16 = nSupportsSampleSizeInBits(16);
        boolean supportsMono = nSupportsChannels(1);
        boolean supportsStereo = nSupportsChannels(2);
        boolean supports8k = nSupportsSampleRate(8000.0f);
        boolean supports11k = nSupportsSampleRate(11025.0f);
        //$$fb 2001-07-20: added "support" for 16KHz
        boolean supports16k = nSupportsSampleRate(16000.0f);
        boolean supports22k = nSupportsSampleRate(22050.0f);
        //$$fb 2001-07-20: added "support" for 32KHz
        boolean supports32k = nSupportsSampleRate(32000.0f);
        boolean supports44k = nSupportsSampleRate(44100.0f);

        // $$kk: 06.02.99: we should handle the full set of formats
        // exposed by the device!!  we should also handle direct u-law
        // output where supported.  we should not be doing this stupid
        // query!!  this probably means we should return an array of
        // supported AudioFormat objects from a single native call.

        // populate a vector of supported formats
        Vector formats = new Vector();

        // $$fb: 2001-07-05 fixed incorrect framesize values. Bug 4469409
        // $$fb: 2001-07-20 added support for 16KHz and 32KHz. Bug 4479441

        // 8000k

        if (supports8 && supportsMono && supports8k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 8, 1, 1, 8000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 8000, 8, 1, 1, 8000, false));
        }

        if (supports8 && supportsStereo && supports8k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 8, 2, 2, 8000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 8000, 8, 2, 2, 8000, false));
        }

        if (supports16 && supportsMono && supports8k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 1, 2, 8000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 1, 2, 8000, true));
        }

        if (supports16 && supportsStereo && supports8k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 2, 4, 8000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000, 16, 2, 4, 8000, true));
        }

        // 11025

        if (supports8 && supportsMono && supports11k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 11025, 8, 1, 1, 11025, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 11025, 8, 1, 1, 11025, false));
        }

        if (supports8 && supportsStereo && supports11k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 11025, 8, 2, 2, 11025, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 11025, 8, 2, 2, 11025, false));
        }

        //$$fb 2001-07-20: added "support" for 16KHz

        // 16000

        if (supports16 && supportsMono && supports11k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 11025, 16, 1, 2, 11025, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 11025, 16, 1, 2, 11025, true));
        }

        if (supports16 && supportsStereo && supports11k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 11025, 16, 2, 4, 11025, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 11025, 16, 2, 4, 11025, true));
        }

        if (supports8 && supportsMono && supports16k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 8, 1, 1, 16000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 16000, 8, 1, 1, 16000, false));
        }

        if (supports8 && supportsStereo && supports16k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 8, 2, 2, 16000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 16000, 8, 2, 2, 16000, false));
        }

        if (supports16 && supportsMono && supports16k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, true));
        }

        if (supports16 && supportsStereo && supports16k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 2, 4, 16000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 2, 4, 16000, true));
        }


        // 22050

        if (supports8 && supportsMono && supports22k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 8, 1, 1, 22050, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 22050, 8, 1, 1, 22050, false));
        }

        if (supports8 && supportsStereo && supports22k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 8, 2, 2, 22050, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 22050, 8, 2, 2, 22050, false));
        }

        if (supports16 && supportsMono && supports22k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 16, 1, 2, 22050, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 16, 1, 2, 22050, true));
        }

        if (supports16 && supportsStereo && supports22k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 16, 2, 4, 22050, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 16, 2, 4, 22050, true));
        }

        //$$fb 2001-07-20: added "support" for 32KHz

        // 32000

        if (supports8 && supportsMono && supports32k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 32000, 8, 1, 1, 32000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 32000, 8, 1, 1, 32000, false));
        }

        if (supports8 && supportsStereo && supports32k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 32000, 8, 2, 2, 32000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 32000, 8, 2, 2, 32000, false));
        }

        if (supports16 && supportsMono && supports32k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 32000, 16, 1, 2, 32000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 32000, 16, 1, 2, 32000, true));
        }

        if (supports16 && supportsStereo && supports32k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 32000, 16, 2, 4, 32000, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 32000, 16, 2, 4, 32000, true));
        }


        // 44100

        if (supports8 && supportsMono && supports44k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 8, 1, 1, 44100, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 44100, 8, 1, 1, 44100, false));
        }

        if (supports8 && supportsStereo && supports44k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 8, 2, 2, 44100, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 44100, 8, 2, 2, 44100, false));
        }

        if (supports16 && supportsMono && supports44k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 1, 2, 44100, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 1, 2, 44100, true));
        }

        if (supports16 && supportsStereo && supports44k) {
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false));
            formats.addElement(new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, true));
        }


        // $$kk: 06.03.99: this is stupid; should store these as an array!!
        AudioFormat[] formatArray;
        synchronized(formats) {
            formatArray = new AudioFormat[formats.size()];
            for (int i = 0; i < formatArray.length; i++) {
                formatArray[i] = (AudioFormat)formats.elementAt(i);
            }
        }

        // $$kk: 05.31.99: need to figure this out!
        // set up the port info objects; these are the targets for the output device

        int numPorts = nGetNumPorts();
        targetLineInfo = new Port.Info[numPorts];
        ports = new Port[numPorts];
        String name;

        for (int i = 0; i < numPorts; i++) {

            name = nGetPortName(i);
            targetLineInfo[i] = getPortInfo(name);

            ports[i] = new OutputDevicePort((Port.Info)targetLineInfo[i], this, new Control[0]);
        }


        // set up the source line objects
        sourceLineInfo = new DataLine.Info[1];
        sourceLineInfo[0] = new DataLine.Info(SourceDataLine.class,
                                              formatArray,
                                              0,
                                              AudioSystem.NOT_SPECIFIED);


        // $$jb: 12.07.99: Fix for 4297115: NoSuchElementException thrown on
        // machines without capture devices.  We need to make sure that the
        // formats vector has elements before we set the initial format.

        // set the initial format as the best supported one
        if( formats.size() > 0 ) {
            format = (AudioFormat)formats.lastElement();
        } else {
            format = null;
        }

        if (Printer.trace) Printer.trace("<< SimpleOutputDevice: constructor completed");

    }


    // ABSTRACT MIXER: ABSTRACT METHOD IMPLEMENTATIONS

    // MIXER METHODS

    public Line getLine(Line.Info info) throws LineUnavailableException {

        Line.Info fullInfo = getLineInfo(info);

        if (fullInfo == null) {
            throw new IllegalArgumentException("Line unsupported: " + info);
        }

        if (fullInfo instanceof Port.Info) {

            for (int i = 0; i < ports.length; i++) {
                if (fullInfo.equals(ports[i].getLineInfo())) {
                    return ports[i];
                }
            }
        }

        if (fullInfo instanceof DataLine.Info) {

            DataLine.Info dataLineInfo = (DataLine.Info)info;

            if (dataLineInfo.getLineClass().isAssignableFrom(OutputDeviceDataLine.class)) {
                AudioFormat[] supportedFormats = dataLineInfo.getFormats();
                AudioFormat defaultFormat = supportedFormats[supportedFormats.length-1];
                return new OutputDeviceDataLine(dataLineInfo, this, defaultFormat, dataLineInfo.getMaxBufferSize());
            }
        }

        throw new IllegalArgumentException("Line unsupported: " + info);
    }


    public int getMaxLines(Line.Info info) {

        Line.Info fullInfo = getLineInfo(info);

        // if it's not supported at all, return 0.
        if (fullInfo == null) {
            return 0;
        }

        // $$kk: 06.02.99: for ports, let's assume we can only have one output
        // port open at a time.  the real situation may be more complicated.

        if (fullInfo instanceof Port.Info) {
            return ports.length;
        }


        // $$kk: 06.02.99: just one for now; we're really not going
        // to let anyone use it.,,,

        if (fullInfo instanceof DataLine.Info) {
            return 1;
        }

        return 0;
    }


    // ABSTRACT LINE: ABSTRACT METHOD IMPLEMENTATIONS


    protected void implOpen() throws LineUnavailableException {

        //$$fb 2001-08-01: better check for buffer size. Part of fix for bug #4326534 (flush bug)
        int bufferSizeInFrames=(bufferSize==AudioSystem.NOT_SPECIFIED)?
            AudioSystem.NOT_SPECIFIED:(bufferSize / format.getFrameSize());

        if (bufferSizeInFrames <= 0) {
            bufferSizeInFrames = (int) (format.getFrameRate() / 2);
        }

        // open the output device
        // note that this method takes the buffer size in frames
        nOpen(((SimpleOutputDeviceProvider.OutputDeviceInfo)getMixerInfo()).getIndex(),
              (int)format.getSampleRate(),
              format.getSampleSizeInBits(),
              format.getChannels(),
              bufferSizeInFrames );

        //$$fb 2001-08-01: make sure that the bufferSize field has correct and aligned buffersize
        this.bufferSize = (bufferSizeInFrames==AudioSystem.NOT_SPECIFIED)?
            AudioSystem.NOT_SPECIFIED:(bufferSizeInFrames * format.getFrameSize());
    }

    public void implClose() {

        // close the output device
        nClose();
    }


    // ABSTRACT DATA LINE: ABSTRACT METHOD IMPLEMENTATIONS

    /**
     * Start the output device
     */
    protected void implStart() {
        nStart();
    }


    /**
     * Stop the output device
     */
    protected void implStop() {
        nStart();
    }


    // HELPER METHODS


    /**
     * Utility method for converting between String names and well-known
     * Port types.  I'm only doing the ones that can be targets.  We may
     * want to do something at least a little more general here....
     */
    private Port.Info getPortInfo(String name) {

        if (name.equals(Port.Info.SPEAKER.toString())) {
            return Port.Info.SPEAKER;
        }

        if (name.equals(Port.Info.HEADPHONE.toString())) {
            return Port.Info.HEADPHONE;
        }

        if (name.equals(Port.Info.LINE_OUT.toString())) {
            return Port.Info.LINE_OUT;
        }

        // return a new port info object.
        return (new OutputDevicePortInfo(name));
    }


    // INNER CLASSES

    /**
     * Private inner class representing the source data line for the SimpleOutputDevice.
     */
    private class OutputDeviceDataLine extends AbstractDataLine implements SourceDataLine {

        private CircularBuffer circularBuffer                           = null;


        // CONSTRUCTOR

        private OutputDeviceDataLine(DataLine.Info info, SimpleOutputDevice mixer, AudioFormat initialFormat, int initialBufferSize) {

            // $$kk: 06.02.99: need to deal with controls!
            super(info, mixer, null, initialFormat, initialBufferSize);
        }


        // SOURCE DATA LINE METHODS

        /**
         * This will generate a NullPointerException if b is null, and
         * an ArrayIndexOutOfBounds exception if len frames past off
         * overruns the end of the array.
         * @param off - in bytes
         * @param len - in sample frames
         */
        public int write(byte[] b, int off, int len) {

            // fail; this doesn't really work
            throw new SecurityException("Permission to read data from the output device not granted.");
        }


        public int available() {

            // fail; this doesn't really work
            return 0;
        }


        // ABSTRACT METHOD IMPLEMENTATIONS

        // ABSTRACT LINE

        void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException {

            // fail; this doesn't really work
            throw new SecurityException("Permission to read data from the output device not granted.");
        }


        void implClose() {

            // fail; this doesn't really work
            throw new SecurityException("Permission to read data from the output device not granted.");
        }


        // ABSTRACT DATA LINE


        void implStart() {

            // fail; this doesn't really work
            throw new SecurityException("Permission to read data from the output device not granted.");
        }


        void implStop() {

            // fail; this doesn't really work
            throw new SecurityException("Permission to read data from the output device not granted.");
        }


        // METHODS FOR INTERNAL IMPLEMENTATION USE

        private CircularBuffer getCircularBuffer() {
            return circularBuffer;
        }

    } // class OutputDeviceDataLine


    /**
     * Private inner class representing a port on the output device.
     */
    private class OutputDevicePort extends AbstractLine implements Port {

        private OutputDevicePort(Port.Info info, AbstractMixer mixer, Control[] controls) {
            super(info, mixer, controls);
        }


        public void open() throws LineUnavailableException {

            /* $$kk: 06.03.99: need to implement */
        }


        public void close() {

            /* $$kk: 06.03.99: need to implement */
        }
    } // class OutputDevicePort


    /**
     * Private inner class representing an output device port info object
     */
    private static class OutputDevicePortInfo extends Port.Info {

        private OutputDevicePortInfo(String name) {
            super(Port.class, name, false);
        }
    }





    // NATIVE METHODS

    // this will open the output device and start the output thread
    // note that this takes the buffer size in frames.
    // GM_ResumeGeneralSound
    private void nOpen(int index, float sampleRate, int sampleSizeInBits, int channels, int bufferSize) throws LineUnavailableException
    {
     throw new LineUnavailableException();
    }

    // this will close the output device and stop the output thread
    // GM_PauseGeneralSound
    private void nClose() {}

    // ?
    private void nStart() {}

    // ?
    private void nStop() {}


    // these should be replaced by a better mechanism for finding supported
    // formats
    private boolean nSupportsSampleRate(float sampleRate)
    {
     return (sampleRate == 8000) || (sampleRate == 11025) ||
             (sampleRate == 16000) || (sampleRate == 22050) ||
             (sampleRate == 32000) || (sampleRate == 44100) ||
             (sampleRate == 48000);
    }

    private boolean nSupportsSampleSizeInBits(int sampleSizeInBits)
    {
     return sampleSizeInBits == 16 || sampleSizeInBits == 8;
    }

    private boolean nSupportsChannels(int channels)
    {
     return channels == 1 || channels == 2;
    }

    // gets the number of ports
    private int nGetNumPorts() { return 0; }

    // gets the name of the port with this index
    private String nGetPortName(int index) { return null; }


    //private native void nDrain();
    //private native void nFlush();
    //private native long nGetPosition();
    //private native void nClose();
    //private native void nPause();
    //private native void nResume();

    // $$kk: 03.25.99: need to implement!
    //private static /*native*/ int nGetNumDevices() {
    //  return 1;
    //}
}
TOP

Related Classes of com.sun.media.sound.SimpleOutputDevice

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.