view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/SoftMixingClip.java @ 873:a1832ea486e9

Import Gervill fixes from CVS and add tests. 2008-05-23 Mark Wielaard <mark@klomp.org> * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound: Import Gervill fixes from CVS. See CHANGES.txt. Added: Software Mixing Mixer. Fix: AudioFloatFormatConverter.getTargetFormats() * overlays/openjdk/jdk/test/com/sun/media/sound: Imported all tests.
author Mark Wielaard <mark@klomp.org>
date Sun, 25 May 2008 21:21:11 +0200
parents
children
line wrap: on
line source

/*
 * Copyright 2008 Sun Microsystems, Inc.  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.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.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.io.InputStream;
import java.util.Arrays;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;

/**
 * Clip implemention for the SoftMixingMixer.
 * 
 * @version %I%, %E%
 * @author Karl Helgason
 */

public class SoftMixingClip extends SoftMixingDataLine implements Clip {

	private AudioFormat format;

	private int framesize;

	private byte[] data;

	private InputStream datastream = new InputStream() {

		public int read() throws IOException {
			byte[] b = new byte[1];
			int ret = read(b);
			if (ret < 0)
				return ret;
			return b[0] & 0xFF;
		}

		public int read(byte[] b, int off, int len) throws IOException {

			if (_loopcount != 0) {
				int bloopend = _loopend * framesize;
				int bloopstart = _loopstart * framesize;
				int pos = _frameposition * framesize;

				if (pos + len >= bloopend)
					if (pos < bloopend) {
						int offend = off + len;
						int o = off;
						while (off != offend) {
							if (pos == bloopend) {
								if (_loopcount == 0)
									break;
								pos = bloopstart;
								if (_loopcount != LOOP_CONTINUOUSLY)
									_loopcount--;
							}
							len = offend - off;
							int left = bloopend - pos;
							if (len > left)
								len = left;
							System.arraycopy(data, pos, b, off, len);
							off += len;
						}
						if (_loopcount == 0) {
							len = offend - off;
							int left = bloopend - pos;
							if (len > left)
								len = left;
							System.arraycopy(data, pos, b, off, len);
							off += len;
						}
						_frameposition = pos / framesize;
						return o - off;
					}
			}

			int pos = _frameposition * framesize;
			int left = bufferSize - pos;
			if (left == 0)
				return -1;
			if (len > left)
				len = left;
			System.arraycopy(data, pos, b, off, len);
			_frameposition += len / framesize;
			return len;
		}

	};

	private int offset;

	private int bufferSize;

	private float[] readbuffer;

	private boolean open = false;

	private AudioFormat outputformat;

	private int out_nrofchannels;

	private int in_nrofchannels;

	private int frameposition = 0;

	private boolean frameposition_sg = false;

	private boolean active_sg = false;

	private int loopstart = 0;

	private int loopend = -1;

	private boolean active = false;

	private int loopcount = 0;

	private boolean _active = false;

	private int _frameposition = 0;

	private boolean loop_sg = false;

	private int _loopcount = 0;

	private int _loopstart = 0;

	private int _loopend = -1;

	private float _rightgain;

	private float _leftgain;

	private float _eff1gain;

	private float _eff2gain;

	private AudioFloatInputStream afis;

	protected SoftMixingClip(SoftMixingMixer mixer, DataLine.Info info) {
		super(mixer, info);
	}

	protected void processControlLogic() {

		_rightgain = rightgain;
		_leftgain = leftgain;
		_eff1gain = eff1gain;
		_eff2gain = eff2gain;

		if (active_sg) {
			_active = active;
			active_sg = false;
		} else {
			active = _active;
		}

		if (frameposition_sg) {
			_frameposition = frameposition;
			frameposition_sg = false;
			afis = null;
		} else {
			frameposition = _frameposition;
		}
		if (loop_sg) {
			_loopcount = loopcount;
			_loopstart = loopstart;
			_loopend = loopend;
		}

		if (afis == null) {
			afis = AudioFloatInputStream.getInputStream(new AudioInputStream(
					datastream, format, AudioSystem.NOT_SPECIFIED));

			if (Math.abs(format.getSampleRate() - outputformat.getSampleRate()) > 0.000001)
				afis = new AudioFloatInputStreamResampler(afis, outputformat);
		}

	}

	protected void processAudioLogic(SoftAudioBuffer[] buffers) {
		if (_active) {
			float[] left = buffers[SoftMixingMainMixer.CHANNEL_LEFT].array();
			float[] right = buffers[SoftMixingMainMixer.CHANNEL_RIGHT].array();
			int bufferlen = buffers[SoftMixingMainMixer.CHANNEL_LEFT].getSize();

			int readlen = bufferlen * in_nrofchannels;
			if (readbuffer == null || readbuffer.length < readlen) {
				readbuffer = new float[readlen];
			}
			int ret = 0;
			try {
				ret = afis.read(readbuffer);
				if (ret == -1) {
					_active = false;
					return;
				}
				if (ret != in_nrofchannels)
					Arrays.fill(readbuffer, ret, readlen, 0);
			} catch (IOException e) {
			}

			int in_c = in_nrofchannels;
			for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
				left[i] += readbuffer[ix] * _leftgain;
			}

			if (out_nrofchannels != 1) {
				if (in_nrofchannels == 1) {
					for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
						right[i] += readbuffer[ix] * _rightgain;
					}
				} else {
					for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
						right[i] += readbuffer[ix] * _rightgain;
					}
				}

			}

			if (_eff1gain > 0.0002) {

				float[] eff1 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT1]
						.array();
				for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
					eff1[i] += readbuffer[ix] * _eff1gain;
				}
				if (in_nrofchannels == 2) {
					for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
						eff1[i] += readbuffer[ix] * _eff1gain;
					}
				}
			}

			if (_eff2gain > 0.0002) {
				float[] eff2 = buffers[SoftMixingMainMixer.CHANNEL_EFFECT2]
						.array();
				for (int i = 0, ix = 0; i < bufferlen; i++, ix += in_c) {
					eff2[i] += readbuffer[ix] * _eff2gain;
				}
				if (in_nrofchannels == 2) {
					for (int i = 0, ix = 1; i < bufferlen; i++, ix += in_c) {
						eff2[i] += readbuffer[ix] * _eff2gain;
					}
				}
			}

		}
	}

	public int getFrameLength() {
		return bufferSize / format.getFrameSize();
	}

	public long getMicrosecondLength() {
		return (long) (getFrameLength() * (1000000.0 / (double) getFormat()
				.getSampleRate()));
	}

	public void loop(int count) {
		LineEvent event = null;

		synchronized (control_mutex) {
			if (isOpen()) {
				if (active)
					return;
				active = true;
				active_sg = true;
				loopcount = count;
				event = new LineEvent(this, LineEvent.Type.START,
						getLongFramePosition());
			}
		}

		if (event != null)
			sendEvent(event);

	}

	public void open(AudioInputStream stream) throws LineUnavailableException,
			IOException {
		if (isOpen()) {
			throw new IllegalStateException("Clip is already open with format "
					+ getFormat() + " and frame lengh of " + getFrameLength());
		}
		if (AudioFloatConverter.getConverter(stream.getFormat()) == null)
			throw new IllegalArgumentException("Invalid format : "
					+ stream.getFormat().toString());

		if (stream.getFrameLength() != AudioSystem.NOT_SPECIFIED) {
			byte[] data = new byte[(int) stream.getFrameLength()
					* stream.getFormat().getFrameSize()];
			int readsize = 512 * stream.getFormat().getFrameSize();
			int len = 0;
			while (len != data.length) {
				if (readsize > data.length - len)
					readsize = data.length - len;
				int ret = stream.read(data, len, readsize);
				if (ret == -1)
					break;
				if (ret == 0)
					Thread.yield();
				len += ret;
			}
			open(stream.getFormat(), data, 0, len);
		} else {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			byte[] b = new byte[512 * stream.getFormat().getFrameSize()];
			int r = 0;
			while ((r = stream.read(b)) != -1) {
				if (r == 0)
					Thread.yield();
				baos.write(b, 0, r);
			}
			open(stream.getFormat(), baos.toByteArray(), 0, baos.size());
		}

	}

	public void open(AudioFormat format, byte[] data, int offset, int bufferSize)
			throws LineUnavailableException {
		synchronized (control_mutex) {
			if (isOpen()) {
				throw new IllegalStateException(
						"Clip is already open with format " + getFormat()
								+ " and frame lengh of " + getFrameLength());
			}
			if (AudioFloatConverter.getConverter(format) == null)
				throw new IllegalArgumentException("Invalid format : "
						+ format.toString());
			if (bufferSize % format.getFrameSize() != 0)
				throw new IllegalArgumentException(
						"Buffer size does not represent an integral number of sample frames!");

			this.data = data;
			this.offset = offset;
			this.bufferSize = bufferSize;
			this.format = format;
			this.framesize = format.getFrameSize();

			loopstart = 0;
			loopend = -1;
			loop_sg = true;

			if (!mixer.isOpen()) {
				mixer.open();
				mixer.implicitOpen = true;
			}

			outputformat = mixer.getFormat();
			out_nrofchannels = outputformat.getChannels();
			in_nrofchannels = format.getChannels();

			open = true;

			mixer.getMainMixer().openLine(this);
		}

	}

	public void setFramePosition(int frames) {
		synchronized (control_mutex) {
			frameposition_sg = true;
			frameposition = frames;
		}
	}

	public void setLoopPoints(int start, int end) {
		synchronized (control_mutex) {
			if (end != -1) {
				if (end < start)
					throw new IllegalArgumentException("Invalid loop points : "
							+ start + " - " + end);
				if (end * framesize > bufferSize)
					throw new IllegalArgumentException("Invalid loop points : "
							+ start + " - " + end);
			}
			if (start * framesize > bufferSize)
				throw new IllegalArgumentException("Invalid loop points : "
						+ start + " - " + end);
			if (0 < start)
				throw new IllegalArgumentException("Invalid loop points : "
						+ start + " - " + end);
			loopstart = start;
			loopend = end;
			loop_sg = true;
		}
	}

	public void setMicrosecondPosition(long microseconds) {
		setFramePosition((int) (microseconds * (((double) getFormat()
				.getSampleRate()) / 1000000.0)));
	}

	public int available() {
		return 0;
	}

	public void drain() {
	}

	public void flush() {
	}

	public int getBufferSize() {
		return bufferSize;
	}

	public AudioFormat getFormat() {
		return format;
	}

	public int getFramePosition() {
		synchronized (control_mutex) {
			return frameposition;
		}
	}

	public float getLevel() {
		return AudioSystem.NOT_SPECIFIED;
	}

	public long getLongFramePosition() {
		return getFramePosition();
	}

	public long getMicrosecondPosition() {
		return (long) (getFramePosition() * (1000000.0 / (double) getFormat()
				.getSampleRate()));
	}

	public boolean isActive() {
		synchronized (control_mutex) {
			return active;
		}
	}

	public boolean isRunning() {
		synchronized (control_mutex) {
			return active;
		}
	}

	public void start() {

		LineEvent event = null;

		synchronized (control_mutex) {
			if (isOpen()) {
				if (active)
					return;
				active = true;
				active_sg = true;
				loopcount = 0;
				event = new LineEvent(this, LineEvent.Type.START,
						getLongFramePosition());
			}
		}

		if (event != null)
			sendEvent(event);
	}

	public void stop() {
		LineEvent event = null;

		synchronized (control_mutex) {
			if (isOpen()) {
				if (!active)
					return;
				active = false;
				active_sg = true;
				event = new LineEvent(this, LineEvent.Type.STOP,
						getLongFramePosition());
			}
		}

		if (event != null)
			sendEvent(event);
	}

	public void close() {
		LineEvent event = null;

		synchronized (control_mutex) {
			if (!isOpen())
				return;
			stop();

			event = new LineEvent(this, LineEvent.Type.CLOSE,
					getLongFramePosition());

			open = false;
			mixer.getMainMixer().closeLine(this);
		}

		if (event != null)
			sendEvent(event);

	}

	public boolean isOpen() {
		return open;
	}

	public void open() throws LineUnavailableException {
		if (data == null) {
			throw new IllegalArgumentException(
					"Illegal call to open() in interface Clip");
		}
		open(format, data, offset, bufferSize);
	}

}