829N/A/*
6321N/A * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
829N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
829N/A *
829N/A * This code is free software; you can redistribute it and/or modify it
829N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
829N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
829N/A *
829N/A * This code is distributed in the hope that it will be useful, but WITHOUT
829N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
829N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
829N/A * version 2 for more details (a copy is included in the LICENSE file that
829N/A * accompanied this code).
829N/A *
829N/A * You should have received a copy of the GNU General Public License version
829N/A * 2 along with this work; if not, write to the Free Software Foundation,
829N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
829N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
829N/A */
829N/Apackage com.sun.media.sound;
829N/A
829N/Aimport java.io.ByteArrayOutputStream;
829N/Aimport java.io.IOException;
829N/Aimport java.io.InputStream;
829N/Aimport java.util.Arrays;
829N/A
829N/Aimport javax.sound.sampled.AudioFormat;
829N/Aimport javax.sound.sampled.AudioInputStream;
829N/Aimport javax.sound.sampled.AudioSystem;
829N/Aimport javax.sound.sampled.Clip;
829N/Aimport javax.sound.sampled.DataLine;
829N/Aimport javax.sound.sampled.LineEvent;
829N/Aimport javax.sound.sampled.LineUnavailableException;
829N/A
829N/A/**
829N/A * Clip implemention for the SoftMixingMixer.
829N/A *
829N/A * @author Karl Helgason
829N/A */
6321N/Apublic final class SoftMixingClip extends SoftMixingDataLine implements Clip {
829N/A
829N/A private AudioFormat format;
829N/A
829N/A private int framesize;
829N/A
829N/A private byte[] data;
829N/A
6321N/A private final InputStream datastream = new InputStream() {
829N/A
829N/A public int read() throws IOException {
829N/A byte[] b = new byte[1];
829N/A int ret = read(b);
829N/A if (ret < 0)
829N/A return ret;
829N/A return b[0] & 0xFF;
829N/A }
829N/A
829N/A public int read(byte[] b, int off, int len) throws IOException {
829N/A
829N/A if (_loopcount != 0) {
829N/A int bloopend = _loopend * framesize;
829N/A int bloopstart = _loopstart * framesize;
829N/A int pos = _frameposition * framesize;
829N/A
829N/A if (pos + len >= bloopend)
829N/A if (pos < bloopend) {
829N/A int offend = off + len;
829N/A int o = off;
829N/A while (off != offend) {
829N/A if (pos == bloopend) {
829N/A if (_loopcount == 0)
829N/A break;
829N/A pos = bloopstart;
829N/A if (_loopcount != LOOP_CONTINUOUSLY)
829N/A _loopcount--;
829N/A }
829N/A len = offend - off;
829N/A int left = bloopend - pos;
829N/A if (len > left)
829N/A len = left;
829N/A System.arraycopy(data, pos, b, off, len);
829N/A off += len;
829N/A }
829N/A if (_loopcount == 0) {
829N/A len = offend - off;
829N/A int left = bloopend - pos;
829N/A if (len > left)
829N/A len = left;
829N/A System.arraycopy(data, pos, b, off, len);
829N/A off += len;
829N/A }
829N/A _frameposition = pos / framesize;
829N/A return o - off;
829N/A }
829N/A }
829N/A
829N/A int pos = _frameposition * framesize;
829N/A int left = bufferSize - pos;
829N/A if (left == 0)
829N/A return -1;
829N/A if (len > left)
829N/A len = left;
829N/A System.arraycopy(data, pos, b, off, len);
829N/A _frameposition += len / framesize;
829N/A return len;
829N/A }
829N/A
829N/A };
829N/A
829N/A private int offset;
829N/A
829N/A private int bufferSize;
829N/A
829N/A private float[] readbuffer;
829N/A
829N/A private boolean open = false;
829N/A
829N/A private AudioFormat outputformat;
829N/A
829N/A private int out_nrofchannels;
829N/A
829N/A private int in_nrofchannels;
829N/A
829N/A private int frameposition = 0;
829N/A
829N/A private boolean frameposition_sg = false;
829N/A
829N/A private boolean active_sg = false;
829N/A
829N/A private int loopstart = 0;
829N/A
829N/A private int loopend = -1;
829N/A
829N/A private boolean active = false;
829N/A
829N/A private int loopcount = 0;
829N/A
829N/A private boolean _active = false;
829N/A
829N/A private int _frameposition = 0;
829N/A
829N/A private boolean loop_sg = false;
829N/A
829N/A private int _loopcount = 0;
829N/A
829N/A private int _loopstart = 0;
829N/A
829N/A private int _loopend = -1;
829N/A
829N/A private float _rightgain;
829N/A
829N/A private float _leftgain;
829N/A
829N/A private float _eff1gain;
829N/A
829N/A private float _eff2gain;
829N/A
829N/A private AudioFloatInputStream afis;
829N/A
6321N/A SoftMixingClip(SoftMixingMixer mixer, DataLine.Info info) {
829N/A super(mixer, info);
829N/A }
829N/A
829N/A protected void processControlLogic() {
829N/A
829N/A _rightgain = rightgain;
829N/A _leftgain = leftgain;
829N/A _eff1gain = eff1gain;
829N/A _eff2gain = eff2gain;
829N/A
829N/A if (active_sg) {
829N/A _active = active;
829N/A active_sg = false;
829N/A } else {
829N/A active = _active;
829N/A }
829N/A
829N/A if (frameposition_sg) {
829N/A _frameposition = frameposition;
829N/A frameposition_sg = false;
829N/A afis = null;
829N/A } else {
829N/A frameposition = _frameposition;
829N/A }
829N/A if (loop_sg) {
829N/A _loopcount = loopcount;
829N/A _loopstart = loopstart;
829N/A _loopend = loopend;
829N/A }
829N/A
829N/A if (afis == null) {
829N/A afis = AudioFloatInputStream.getInputStream(new AudioInputStream(
829N/A datastream, format, AudioSystem.NOT_SPECIFIED));
829N/A
829N/A if (Math.abs(format.getSampleRate() - outputformat.getSampleRate()) > 0.000001)
829N/A afis = new AudioFloatInputStreamResampler(afis, outputformat);
829N/A }
829N/A
829N/A }
829N/A
829N/A protected void processAudioLogic(SoftAudioBuffer[] buffers) {
829N/A if (_active) {
829N/A float[] left = buffers[SoftMixingMainMixer.CHANNEL_LEFT].array();
829N/A float[] right = buffers[SoftMixingMainMixer.CHANNEL_RIGHT].array();
829N/A int bufferlen = buffers[SoftMixingMainMixer.CHANNEL_LEFT].getSize();
829N/A
829N/A int readlen = bufferlen * in_nrofchannels;
829N/A if (readbuffer == null || readbuffer.length < readlen) {
829N/A readbuffer = new float[readlen];
829N/A }
829N/A int ret = 0;
829N/A try {
829N/A ret = afis.read(readbuffer);
829N/A if (ret == -1) {
829N/A _active = false;
829N/A return;
829N/A }
829N/A if (ret != in_nrofchannels)
829N/A Arrays.fill(readbuffer, ret, readlen, 0);
829N/A } catch (IOException e) {
829N/A }
829N/A
829N/A int in_c = in_nrofchannels;
829N/A for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
829N/A left[i] += readbuffer[ix] * _leftgain;
829N/A }
829N/A
829N/A if (out_nrofchannels != 1) {
829N/A if (in_nrofchannels == 1) {
829N/A for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
829N/A right[i] += readbuffer[ix] * _rightgain;
829N/A }
829N/A } else {
829N/A for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
829N/A right[i] += readbuffer[ix] * _rightgain;
829N/A }
829N/A }
829N/A
829N/A }
829N/A
829N/A if (_eff1gain > 0.0002) {
829N/A
829N/A float[] eff1 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT1]
829N/A .array();
829N/A for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
829N/A eff1[i] += readbuffer[ix] * _eff1gain;
829N/A }
829N/A if (in_nrofchannels == 2) {
829N/A for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
829N/A eff1[i] += readbuffer[ix] * _eff1gain;
829N/A }
829N/A }
829N/A }
829N/A
829N/A if (_eff2gain > 0.0002) {
829N/A float[] eff2 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT2]
829N/A .array();
829N/A for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
829N/A eff2[i] += readbuffer[ix] * _eff2gain;
829N/A }
829N/A if (in_nrofchannels == 2) {
829N/A for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
829N/A eff2[i] += readbuffer[ix] * _eff2gain;
829N/A }
829N/A }
829N/A }
829N/A
829N/A }
829N/A }
829N/A
829N/A public int getFrameLength() {
829N/A return bufferSize / format.getFrameSize();
829N/A }
829N/A
829N/A public long getMicrosecondLength() {
829N/A return (long) (getFrameLength() * (1000000.0 / (double) getFormat()
829N/A .getSampleRate()));
829N/A }
829N/A
829N/A public void loop(int count) {
829N/A LineEvent event = null;
829N/A
829N/A synchronized (control_mutex) {
829N/A if (isOpen()) {
829N/A if (active)
829N/A return;
829N/A active = true;
829N/A active_sg = true;
829N/A loopcount = count;
829N/A event = new LineEvent(this, LineEvent.Type.START,
829N/A getLongFramePosition());
829N/A }
829N/A }
829N/A
829N/A if (event != null)
829N/A sendEvent(event);
829N/A
829N/A }
829N/A
829N/A public void open(AudioInputStream stream) throws LineUnavailableException,
829N/A IOException {
829N/A if (isOpen()) {
829N/A throw new IllegalStateException("Clip is already open with format "
829N/A + getFormat() + " and frame lengh of " + getFrameLength());
829N/A }
829N/A if (AudioFloatConverter.getConverter(stream.getFormat()) == null)
829N/A throw new IllegalArgumentException("Invalid format : "
829N/A + stream.getFormat().toString());
829N/A
829N/A if (stream.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
829N/A byte[] data = new byte[(int) stream.getFrameLength()
829N/A * stream.getFormat().getFrameSize()];
829N/A int readsize = 512 * stream.getFormat().getFrameSize();
829N/A int len = 0;
829N/A while (len != data.length) {
829N/A if (readsize > data.length - len)
829N/A readsize = data.length - len;
829N/A int ret = stream.read(data, len, readsize);
829N/A if (ret == -1)
829N/A break;
829N/A if (ret == 0)
829N/A Thread.yield();
829N/A len += ret;
829N/A }
829N/A open(stream.getFormat(), data, 0, len);
829N/A } else {
829N/A ByteArrayOutputStream baos = new ByteArrayOutputStream();
829N/A byte[] b = new byte[512 * stream.getFormat().getFrameSize()];
829N/A int r = 0;
829N/A while ((r = stream.read(b)) != -1) {
829N/A if (r == 0)
829N/A Thread.yield();
829N/A baos.write(b, 0, r);
829N/A }
829N/A open(stream.getFormat(), baos.toByteArray(), 0, baos.size());
829N/A }
829N/A
829N/A }
829N/A
829N/A public void open(AudioFormat format, byte[] data, int offset, int bufferSize)
829N/A throws LineUnavailableException {
829N/A synchronized (control_mutex) {
829N/A if (isOpen()) {
829N/A throw new IllegalStateException(
829N/A "Clip is already open with format " + getFormat()
829N/A + " and frame lengh of " + getFrameLength());
829N/A }
829N/A if (AudioFloatConverter.getConverter(format) == null)
829N/A throw new IllegalArgumentException("Invalid format : "
829N/A + format.toString());
829N/A if (bufferSize % format.getFrameSize() != 0)
829N/A throw new IllegalArgumentException(
829N/A "Buffer size does not represent an integral number of sample frames!");
829N/A
829N/A this.data = data;
829N/A this.offset = offset;
829N/A this.bufferSize = bufferSize;
829N/A this.format = format;
829N/A this.framesize = format.getFrameSize();
829N/A
829N/A loopstart = 0;
829N/A loopend = -1;
829N/A loop_sg = true;
829N/A
829N/A if (!mixer.isOpen()) {
829N/A mixer.open();
829N/A mixer.implicitOpen = true;
829N/A }
829N/A
829N/A outputformat = mixer.getFormat();
829N/A out_nrofchannels = outputformat.getChannels();
829N/A in_nrofchannels = format.getChannels();
829N/A
829N/A open = true;
829N/A
829N/A mixer.getMainMixer().openLine(this);
829N/A }
829N/A
829N/A }
829N/A
829N/A public void setFramePosition(int frames) {
829N/A synchronized (control_mutex) {
829N/A frameposition_sg = true;
829N/A frameposition = frames;
829N/A }
829N/A }
829N/A
829N/A public void setLoopPoints(int start, int end) {
829N/A synchronized (control_mutex) {
829N/A if (end != -1) {
829N/A if (end < start)
829N/A throw new IllegalArgumentException("Invalid loop points : "
829N/A + start + " - " + end);
829N/A if (end * framesize > bufferSize)
829N/A throw new IllegalArgumentException("Invalid loop points : "
829N/A + start + " - " + end);
829N/A }
829N/A if (start * framesize > bufferSize)
829N/A throw new IllegalArgumentException("Invalid loop points : "
829N/A + start + " - " + end);
829N/A if (0 < start)
829N/A throw new IllegalArgumentException("Invalid loop points : "
829N/A + start + " - " + end);
829N/A loopstart = start;
829N/A loopend = end;
829N/A loop_sg = true;
829N/A }
829N/A }
829N/A
829N/A public void setMicrosecondPosition(long microseconds) {
829N/A setFramePosition((int) (microseconds * (((double) getFormat()
829N/A .getSampleRate()) / 1000000.0)));
829N/A }
829N/A
829N/A public int available() {
829N/A return 0;
829N/A }
829N/A
829N/A public void drain() {
829N/A }
829N/A
829N/A public void flush() {
829N/A }
829N/A
829N/A public int getBufferSize() {
829N/A return bufferSize;
829N/A }
829N/A
829N/A public AudioFormat getFormat() {
829N/A return format;
829N/A }
829N/A
829N/A public int getFramePosition() {
829N/A synchronized (control_mutex) {
829N/A return frameposition;
829N/A }
829N/A }
829N/A
829N/A public float getLevel() {
829N/A return AudioSystem.NOT_SPECIFIED;
829N/A }
829N/A
829N/A public long getLongFramePosition() {
829N/A return getFramePosition();
829N/A }
829N/A
829N/A public long getMicrosecondPosition() {
829N/A return (long) (getFramePosition() * (1000000.0 / (double) getFormat()
829N/A .getSampleRate()));
829N/A }
829N/A
829N/A public boolean isActive() {
829N/A synchronized (control_mutex) {
829N/A return active;
829N/A }
829N/A }
829N/A
829N/A public boolean isRunning() {
829N/A synchronized (control_mutex) {
829N/A return active;
829N/A }
829N/A }
829N/A
829N/A public void start() {
829N/A
829N/A LineEvent event = null;
829N/A
829N/A synchronized (control_mutex) {
829N/A if (isOpen()) {
829N/A if (active)
829N/A return;
829N/A active = true;
829N/A active_sg = true;
829N/A loopcount = 0;
829N/A event = new LineEvent(this, LineEvent.Type.START,
829N/A getLongFramePosition());
829N/A }
829N/A }
829N/A
829N/A if (event != null)
829N/A sendEvent(event);
829N/A }
829N/A
829N/A public void stop() {
829N/A LineEvent event = null;
829N/A
829N/A synchronized (control_mutex) {
829N/A if (isOpen()) {
829N/A if (!active)
829N/A return;
829N/A active = false;
829N/A active_sg = true;
829N/A event = new LineEvent(this, LineEvent.Type.STOP,
829N/A getLongFramePosition());
829N/A }
829N/A }
829N/A
829N/A if (event != null)
829N/A sendEvent(event);
829N/A }
829N/A
829N/A public void close() {
829N/A LineEvent event = null;
829N/A
829N/A synchronized (control_mutex) {
829N/A if (!isOpen())
829N/A return;
829N/A stop();
829N/A
829N/A event = new LineEvent(this, LineEvent.Type.CLOSE,
829N/A getLongFramePosition());
829N/A
829N/A open = false;
829N/A mixer.getMainMixer().closeLine(this);
829N/A }
829N/A
829N/A if (event != null)
829N/A sendEvent(event);
829N/A
829N/A }
829N/A
829N/A public boolean isOpen() {
829N/A return open;
829N/A }
829N/A
829N/A public void open() throws LineUnavailableException {
829N/A if (data == null) {
829N/A throw new IllegalArgumentException(
829N/A "Illegal call to open() in interface Clip");
829N/A }
829N/A open(format, data, offset, bufferSize);
829N/A }
829N/A
829N/A}