/* * 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. Load more
/* * 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,