1488 lines · 58.4 KB · Java
RawDownload
/* * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.media.sound; import java.io.ByteArrayOutputStream;import java.io.IOException;import java.util.Vector; import javax.sound.sampled.*; // IDEA:// Use java.util.concurrent.Semaphore,// java.util.concurrent.locks.ReentrantLock and other new classes/methods// to improve this class's thread safety.  /** * A Mixer which provides direct access to audio devices * * @author Florian Bomers */class DirectAudioDevice extends AbstractMixer {     // CONSTANTS    private static final int CLIP_BUFFER_TIME = 1000; // in milliseconds     private static final int DEFAULT_LINE_BUFFER_TIME = 500; // in milliseconds     // INSTANCE VARIABLES     /** number of opened lines */    private int deviceCountOpened = 0;     /** number of started lines */    private int deviceCountStarted = 0;     // CONSTRUCTOR    DirectAudioDevice(DirectAudioDeviceProvider.DirectAudioDeviceInfo portMixerInfo) {        // pass in Line.Info, mixer, controls        super(portMixerInfo,              // Mixer.Info              null,                       // Control[]              null,                       // Line.Info[] sourceLineInfo              null);                      // Line.Info[] targetLineInfo         if (Printer.trace) Printer.trace(">> DirectAudioDevice: constructor");         // source lines        DirectDLI srcLineInfo = createDataLineInfo(true);        if (srcLineInfo != null) {            sourceLineInfo = new Line.Info[2];            // SourcedataLine            sourceLineInfo[0] = srcLineInfo;            // Clip            sourceLineInfo[1] = new DirectDLI(Clip.class, srcLineInfo.getFormats(),                                              srcLineInfo.getHardwareFormats(),                                              32, // arbitrary minimum buffer size                                              AudioSystem.NOT_SPECIFIED);        } else {            sourceLineInfo = new Line.Info[0];        }         // TargetDataLine        DataLine.Info dstLineInfo = createDataLineInfo(false);        if (dstLineInfo != null) {            targetLineInfo = new Line.Info[1];            targetLineInfo[0] = dstLineInfo;        } else {            targetLineInfo = new Line.Info[0];        }        if (Printer.trace) Printer.trace("<< DirectAudioDevice: constructor completed");    }     private DirectDLI createDataLineInfo(boolean isSource) {        Vector formats = new Vector();        AudioFormat[] hardwareFormatArray = null;        AudioFormat[] formatArray = null;         synchronized(formats) {            nGetFormats(getMixerIndex(), getDeviceID(),                        isSource /* true:SourceDataLine/Clip, false:TargetDataLine */,                        formats);            if (formats.size() > 0) {                int size = formats.size();                int formatArraySize = size;                hardwareFormatArray = new AudioFormat[size];                for (int i = 0; i < size; i++) {                    AudioFormat format = (AudioFormat)formats.elementAt(i);                    hardwareFormatArray[i] = format;                    int bits = format.getSampleSizeInBits();                    boolean isSigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);                    boolean isUnsigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED);                    if ((isSigned || isUnsigned)) {                        // will insert a magically converted format here                        formatArraySize++;                    }                }                formatArray = new AudioFormat[formatArraySize];                int formatArrayIndex = 0;                for (int i = 0; i < size; i++) {                    AudioFormat format = hardwareFormatArray[i];                    formatArray[formatArrayIndex++] = format;                    int bits = format.getSampleSizeInBits();                    boolean isSigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);                    boolean isUnsigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED);                    // add convenience formats (automatic conversion)                    if (bits == 8) {                        // add the other signed'ness for 8-bit                        if (isSigned) {                            formatArray[formatArrayIndex++] =                                new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED,                                    format.getSampleRate(), bits, format.getChannels(),                                    format.getFrameSize(), format.getSampleRate(),                                    format.isBigEndian());                        }                        else if (isUnsigned) {                            formatArray[formatArrayIndex++] =                                new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,                                    format.getSampleRate(), bits, format.getChannels(),                                    format.getFrameSize(), format.getSampleRate(),                                    format.isBigEndian());                        }                    } else if (bits > 8 && (isSigned || isUnsigned)) {                        // add the other endian'ness for more than 8-bit                        formatArray[formatArrayIndex++] =                            new AudioFormat(format.getEncoding(),                                              format.getSampleRate(), bits,                                              format.getChannels(),                                              format.getFrameSize(),                                              format.getSampleRate(),                                              !format.isBigEndian());                    }                    //System.out.println("Adding "+v.get(v.size()-1));                }            }        }        // todo: find out more about the buffer size ?        if (formatArray != null) {            return new DirectDLI(isSource?SourceDataLine.class:TargetDataLine.class,                                 formatArray, hardwareFormatArray,                                 32, // arbitrary minimum buffer size                                 AudioSystem.NOT_SPECIFIED);        }        return null;    }     // ABSTRACT MIXER: ABSTRACT METHOD IMPLEMENTATIONS     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 DataLine.Info) {             DataLine.Info dataLineInfo = (DataLine.Info)fullInfo;            AudioFormat lineFormat;            int lineBufferSize = AudioSystem.NOT_SPECIFIED;             // if a format is specified by the info class passed in, use it.            // otherwise use a format from fullInfo.             AudioFormat[] supportedFormats = null;             if (info instanceof DataLine.Info) {                supportedFormats = ((DataLine.Info)info).getFormats();                lineBufferSize = ((DataLine.Info)info).getMaxBufferSize();            }             if ((supportedFormats == null) || (supportedFormats.length == 0)) {                // use the default format                lineFormat = null;            } else {                // use the last format specified in the line.info object passed                // in by the app                lineFormat = supportedFormats[supportedFormats.length-1];                 // if something is not specified, use default format                if (!Toolkit.isFullySpecifiedPCMFormat(lineFormat)) {                    lineFormat = null;                }            }             if (dataLineInfo.getLineClass().isAssignableFrom(DirectSDL.class)) {                return new DirectSDL(dataLineInfo, lineFormat, lineBufferSize, this);            }            if (dataLineInfo.getLineClass().isAssignableFrom(DirectClip.class)) {                return new DirectClip(dataLineInfo, lineFormat, lineBufferSize, this);            }            if (dataLineInfo.getLineClass().isAssignableFrom(DirectTDL.class)) {                return new DirectTDL(dataLineInfo, lineFormat, lineBufferSize, this);            }        }        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;        }         if (fullInfo instanceof DataLine.Info) {            // DirectAudioDevices should mix !            return getMaxSimulLines();        }         return 0;    }      protected void implOpen() throws LineUnavailableException {        if (Printer.trace) Printer.trace("DirectAudioDevice: implOpen - void method");    }     protected void implClose() {        if (Printer.trace) Printer.trace("DirectAudioDevice: implClose - void method");    }     protected void implStart() {        if (Printer.trace) Printer.trace("DirectAudioDevice: implStart - void method");    }     protected void implStop() {        if (Printer.trace) Printer.trace("DirectAudioDevice: implStop - void method");    }      // IMPLEMENTATION HELPERS     int getMixerIndex() {        return ((DirectAudioDeviceProvider.DirectAudioDeviceInfo) getMixerInfo()).getIndex();    }     int getDeviceID() {        return ((DirectAudioDeviceProvider.DirectAudioDeviceInfo) getMixerInfo()).getDeviceID();    }     int getMaxSimulLines() {        return ((DirectAudioDeviceProvider.DirectAudioDeviceInfo) getMixerInfo()).getMaxSimulLines();    }     private static void addFormat(Vector v, int bits, int frameSizeInBytes, int channels, float sampleRate,                                  int encoding, boolean signed, boolean bigEndian) {        AudioFormat.Encoding enc = null;        switch (encoding) {        case PCM:            enc = signed?AudioFormat.Encoding.PCM_SIGNED:AudioFormat.Encoding.PCM_UNSIGNED;            break;        case ULAW:            enc = AudioFormat.Encoding.ULAW;            if (bits != 8) {                if (Printer.err) Printer.err("DirectAudioDevice.addFormat called with ULAW, but bitsPerSample="+bits);                bits = 8; frameSizeInBytes = channels;            }            break;        case ALAW:            enc = AudioFormat.Encoding.ALAW;            if (bits != 8) {                if (Printer.err) Printer.err("DirectAudioDevice.addFormat called with ALAW, but bitsPerSample="+bits);                bits = 8; frameSizeInBytes = channels;            }            break;        }        if (enc==null) {            if (Printer.err) Printer.err("DirectAudioDevice.addFormat called with unknown encoding: "+encoding);            return;        }        if (frameSizeInBytes <= 0) {            if (channels > 0) {                frameSizeInBytes = ((bits + 7) / 8) * channels;            } else {                frameSizeInBytes = AudioSystem.NOT_SPECIFIED;            }        }        v.add(new AudioFormat(enc, sampleRate, bits, channels, frameSizeInBytes, sampleRate, bigEndian));    }     protected static AudioFormat getSignOrEndianChangedFormat(AudioFormat format) {        boolean isSigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED);        boolean isUnsigned = format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED);        if (format.getSampleSizeInBits() > 8 && isSigned) {            // if this is PCM_SIGNED and 16-bit or higher, then try with endian-ness magic            return new AudioFormat(format.getEncoding(),                                   format.getSampleRate(), format.getSampleSizeInBits(), format.getChannels(),                                   format.getFrameSize(), format.getFrameRate(), !format.isBigEndian());        }        else if (format.getSampleSizeInBits() == 8 && (isSigned || isUnsigned)) {            // if this is PCM and 8-bit, then try with signed-ness magic            return new AudioFormat(isSigned?AudioFormat.Encoding.PCM_UNSIGNED:AudioFormat.Encoding.PCM_SIGNED,                                   format.getSampleRate(), format.getSampleSizeInBits(), format.getChannels(),                                   format.getFrameSize(), format.getFrameRate(), format.isBigEndian());        }        return null;    }        // INNER CLASSES      /**     * Private inner class for the DataLine.Info objects     * adds a little magic for the isFormatSupported so     * that the automagic conversion of endianness and sign     * does not show up in the formats array.     * I.e. the formats array contains only the formats     * that are really supported by the hardware,     * but isFormatSupported() also returns true     * for formats with wrong endianness.     */    private static class DirectDLI extends DataLine.Info {        AudioFormat[] hardwareFormats;         private DirectDLI(Class clazz, AudioFormat[] formatArray,                          AudioFormat[] hardwareFormatArray,                          int minBuffer, int maxBuffer) {            super(clazz, formatArray, minBuffer, maxBuffer);            this.hardwareFormats = hardwareFormatArray;        }         public boolean isFormatSupportedInHardware(AudioFormat format) {            if (format == null) return false;            for (int i = 0; i < hardwareFormats.length; i++) {                if (format.matches(hardwareFormats[i])) {                    return true;                }            }            return false;        }         /*public boolean isFormatSupported(AudioFormat format) {         *   return isFormatSupportedInHardware(format)         *      || isFormatSupportedInHardware(getSignOrEndianChangedFormat(format));         *}         */          private AudioFormat[] getHardwareFormats() {             return hardwareFormats;         }    }     /**     * Private inner class as base class for direct lines     */    private static class DirectDL extends AbstractDataLine implements EventDispatcher.LineMonitor {        protected int mixerIndex;        protected int deviceID;        protected long id;        protected int waitTime;        protected volatile boolean flushing = false;        protected boolean isSource;         // true for SourceDataLine, false for TargetDataLine        protected volatile long bytePosition;        protected volatile boolean doIO = false;     // true in between start() and stop() calls        protected volatile boolean stoppedWritten = false; // true if a write occured in stopped state        protected volatile boolean drained = false; // set to true when drain function returns, set to false in write()        protected boolean monitoring = false;         // if native needs to manually swap samples/convert sign, this        // is set to the framesize        protected int softwareConversionSize = 0;        protected AudioFormat hardwareFormat;         private Gain gainControl = new Gain();        private Mute muteControl = new Mute();        private Balance balanceControl = new Balance();        private Pan panControl = new Pan();        private float leftGain, rightGain;        protected volatile boolean noService = false; // do not run the nService method         // Guards all native calls.        protected final Object lockNative = new Object();         // CONSTRUCTOR        protected DirectDL(DataLine.Info info,                           DirectAudioDevice mixer,                           AudioFormat format,                           int bufferSize,                           int mixerIndex,                           int deviceID,                           boolean isSource) {            super(info, mixer, null, format, bufferSize);            if (Printer.trace) Printer.trace("DirectDL CONSTRUCTOR: info: " + info);            this.mixerIndex = mixerIndex;            this.deviceID = deviceID;            this.waitTime = 10; // 10 milliseconds default wait time            this.isSource = isSource;         }          // ABSTRACT METHOD IMPLEMENTATIONS         // ABSTRACT LINE / DATALINE         void implOpen(AudioFormat format, int bufferSize) throws LineUnavailableException {            if (Printer.trace) Printer.trace(">> DirectDL: implOpen("+format+", "+bufferSize+" bytes)");             // $$fb part of fix for 4679187: Clip.open() throws unexpected Exceptions            Toolkit.isFullySpecifiedAudioFormat(format);             // check for record permission            if (!isSource) {                JSSecurityManager.checkRecordPermission();            }            int encoding = PCM;            if (format.getEncoding().equals(AudioFormat.Encoding.ULAW)) {                encoding = ULAW;            }            else if (format.getEncoding().equals(AudioFormat.Encoding.ALAW)) {                encoding = ALAW;            }             if (bufferSize <= AudioSystem.NOT_SPECIFIED) {                bufferSize = (int) Toolkit.millis2bytes(format, DEFAULT_LINE_BUFFER_TIME);            }             DirectDLI ddli = null;            if (info instanceof DirectDLI) {                ddli = (DirectDLI) info;            }             /* set up controls */            if (isSource) {                if (!format.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)                    && !format.getEncoding().equals(AudioFormat.Encoding.PCM_UNSIGNED)) {                    // no controls for non-PCM formats */                    controls = new Control[0];                }                else if (format.getChannels() > 2                         || format.getSampleSizeInBits() > 16) {                    // no support for more than 2 channels or more than 16 bits                    controls = new Control[0];                } else {                    if (format.getChannels() == 1) {                        controls = new Control[2];                    } else {                        controls = new Control[4];                        controls[2] = balanceControl;                        /* to keep compatibility with apps that rely on                         * MixerSourceLine's PanControl                         */                        controls[3] = panControl;                    }                    controls[0] = gainControl;                    controls[1] = muteControl;                }            }            if (Printer.debug) Printer.debug("DirectAudioDevice: got "+controls.length+" controls.");             hardwareFormat = format;             /* some magic to account for not-supported endianness or signed-ness */            softwareConversionSize = 0;            if (ddli != null && !ddli.isFormatSupportedInHardware(format)) {                AudioFormat newFormat = getSignOrEndianChangedFormat(format);                if (ddli.isFormatSupportedInHardware(newFormat)) {                    // apparently, the new format can be used.                    hardwareFormat = newFormat;                    // So do endian/sign conversion in software                    softwareConversionSize = format.getFrameSize() / format.getChannels();                    if (Printer.debug) {                        Printer.debug("DirectAudioDevice: softwareConversionSize "                                      +softwareConversionSize+":");                        Printer.debug("  from "+format);                        Printer.debug("  to   "+newFormat);                    }                }            }             // align buffer to full frames            bufferSize = ((int) bufferSize / format.getFrameSize()) * format.getFrameSize();             id = nOpen(mixerIndex, deviceID, isSource,

Lines 1–500 of 1488.