829N/A/*
6321N/A * Copyright (c) 2007, 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.EOFException;
829N/Aimport java.io.IOException;
829N/Aimport java.io.InputStream;
829N/A
829N/Aimport javax.sound.sampled.AudioFormat;
829N/Aimport javax.sound.sampled.AudioInputStream;
829N/A
829N/A/**
829N/A * A jitter corrector to be used with SoftAudioPusher.
829N/A *
829N/A * @author Karl Helgason
829N/A */
6321N/Apublic final class SoftJitterCorrector extends AudioInputStream {
829N/A
829N/A private static class JitterStream extends InputStream {
829N/A
829N/A static int MAX_BUFFER_SIZE = 1048576;
829N/A boolean active = true;
829N/A Thread thread;
829N/A AudioInputStream stream;
829N/A // Cyclic buffer
829N/A int writepos = 0;
829N/A int readpos = 0;
829N/A byte[][] buffers;
6321N/A private final Object buffers_mutex = new Object();
829N/A
829N/A // Adapative Drift Statistics
829N/A int w_count = 1000;
829N/A int w_min_tol = 2;
829N/A int w_max_tol = 10;
829N/A int w = 0;
829N/A int w_min = -1;
829N/A // Current read buffer
829N/A int bbuffer_pos = 0;
829N/A int bbuffer_max = 0;
829N/A byte[] bbuffer = null;
829N/A
829N/A public byte[] nextReadBuffer() {
829N/A synchronized (buffers_mutex) {
829N/A if (writepos > readpos) {
829N/A int w_m = writepos - readpos;
829N/A if (w_m < w_min)
829N/A w_min = w_m;
829N/A
829N/A int buffpos = readpos;
829N/A readpos++;
829N/A return buffers[buffpos % buffers.length];
829N/A }
829N/A w_min = -1;
829N/A w = w_count - 1;
829N/A }
829N/A while (true) {
829N/A try {
829N/A Thread.sleep(1);
829N/A } catch (InterruptedException e) {
829N/A //e.printStackTrace();
829N/A return null;
829N/A }
829N/A synchronized (buffers_mutex) {
829N/A if (writepos > readpos) {
829N/A w = 0;
829N/A w_min = -1;
829N/A w = w_count - 1;
829N/A int buffpos = readpos;
829N/A readpos++;
829N/A return buffers[buffpos % buffers.length];
829N/A }
829N/A }
829N/A }
829N/A }
829N/A
829N/A public byte[] nextWriteBuffer() {
829N/A synchronized (buffers_mutex) {
829N/A return buffers[writepos % buffers.length];
829N/A }
829N/A }
829N/A
829N/A public void commit() {
829N/A synchronized (buffers_mutex) {
829N/A writepos++;
829N/A if ((writepos - readpos) > buffers.length) {
829N/A int newsize = (writepos - readpos) + 10;
829N/A newsize = Math.max(buffers.length * 2, newsize);
829N/A buffers = new byte[newsize][buffers[0].length];
829N/A }
829N/A }
829N/A }
829N/A
6321N/A JitterStream(AudioInputStream s, int buffersize,
829N/A int smallbuffersize) {
829N/A this.w_count = 10 * (buffersize / smallbuffersize);
829N/A if (w_count < 100)
829N/A w_count = 100;
829N/A this.buffers
829N/A = new byte[(buffersize/smallbuffersize)+10][smallbuffersize];
829N/A this.bbuffer_max = MAX_BUFFER_SIZE / smallbuffersize;
829N/A this.stream = s;
829N/A
829N/A
829N/A Runnable runnable = new Runnable() {
829N/A
829N/A public void run() {
829N/A AudioFormat format = stream.getFormat();
829N/A int bufflen = buffers[0].length;
829N/A int frames = bufflen / format.getFrameSize();
829N/A long nanos = (long) (frames * 1000000000.0
829N/A / format.getSampleRate());
829N/A long now = System.nanoTime();
829N/A long next = now + nanos;
829N/A int correction = 0;
829N/A while (true) {
829N/A synchronized (JitterStream.this) {
829N/A if (!active)
829N/A break;
829N/A }
829N/A int curbuffsize;
829N/A synchronized (buffers) {
829N/A curbuffsize = writepos - readpos;
829N/A if (correction == 0) {
829N/A w++;
829N/A if (w_min != Integer.MAX_VALUE) {
829N/A if (w == w_count) {
829N/A correction = 0;
829N/A if (w_min < w_min_tol) {
829N/A correction = (w_min_tol + w_max_tol)
829N/A / 2 - w_min;
829N/A }
829N/A if (w_min > w_max_tol) {
829N/A correction = (w_min_tol + w_max_tol)
829N/A / 2 - w_min;
829N/A }
829N/A w = 0;
829N/A w_min = Integer.MAX_VALUE;
829N/A }
829N/A }
829N/A }
829N/A }
829N/A while (curbuffsize > bbuffer_max) {
829N/A synchronized (buffers) {
829N/A curbuffsize = writepos - readpos;
829N/A }
829N/A synchronized (JitterStream.this) {
829N/A if (!active)
829N/A break;
829N/A }
829N/A try {
829N/A Thread.sleep(1);
829N/A } catch (InterruptedException e) {
829N/A //e.printStackTrace();
829N/A }
829N/A }
829N/A
829N/A if (correction < 0)
829N/A correction++;
829N/A else {
829N/A byte[] buff = nextWriteBuffer();
829N/A try {
829N/A int n = 0;
829N/A while (n != buff.length) {
829N/A int s = stream.read(buff, n, buff.length
829N/A - n);
829N/A if (s < 0)
829N/A throw new EOFException();
829N/A if (s == 0)
829N/A Thread.yield();
829N/A n += s;
829N/A }
829N/A } catch (IOException e1) {
829N/A //e1.printStackTrace();
829N/A }
829N/A commit();
829N/A }
829N/A
829N/A if (correction > 0) {
829N/A correction--;
829N/A next = System.nanoTime() + nanos;
829N/A continue;
829N/A }
829N/A long wait = next - System.nanoTime();
829N/A if (wait > 0) {
829N/A try {
829N/A Thread.sleep(wait / 1000000L);
829N/A } catch (InterruptedException e) {
829N/A //e.printStackTrace();
829N/A }
829N/A }
829N/A next += nanos;
829N/A }
829N/A }
829N/A };
829N/A
829N/A thread = new Thread(runnable);
1170N/A thread.setDaemon(true);
829N/A thread.setPriority(Thread.MAX_PRIORITY);
829N/A thread.start();
829N/A }
829N/A
829N/A public void close() throws IOException {
829N/A synchronized (this) {
829N/A active = false;
829N/A }
829N/A try {
829N/A thread.join();
829N/A } catch (InterruptedException e) {
829N/A //e.printStackTrace();
829N/A }
829N/A stream.close();
829N/A }
829N/A
829N/A public int read() throws IOException {
829N/A byte[] b = new byte[1];
829N/A if (read(b) == -1)
829N/A return -1;
829N/A return b[0] & 0xFF;
829N/A }
829N/A
829N/A public void fillBuffer() {
829N/A bbuffer = nextReadBuffer();
829N/A bbuffer_pos = 0;
829N/A }
829N/A
829N/A public int read(byte[] b, int off, int len) {
829N/A if (bbuffer == null)
829N/A fillBuffer();
829N/A int bbuffer_len = bbuffer.length;
829N/A int offlen = off + len;
829N/A while (off < offlen) {
829N/A if (available() == 0)
829N/A fillBuffer();
829N/A else {
829N/A byte[] bbuffer = this.bbuffer;
829N/A int bbuffer_pos = this.bbuffer_pos;
829N/A while (off < offlen && bbuffer_pos < bbuffer_len)
829N/A b[off++] = bbuffer[bbuffer_pos++];
829N/A this.bbuffer_pos = bbuffer_pos;
829N/A }
829N/A }
829N/A return len;
829N/A }
829N/A
829N/A public int available() {
829N/A return bbuffer.length - bbuffer_pos;
829N/A }
829N/A }
829N/A
829N/A public SoftJitterCorrector(AudioInputStream stream, int buffersize,
829N/A int smallbuffersize) {
829N/A super(new JitterStream(stream, buffersize, smallbuffersize),
829N/A stream.getFormat(), stream.getFrameLength());
829N/A }
829N/A}