view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/SoftJitterCorrector.java @ 843:bcba163568ac

Integrate Gervill. 2008-04-30 Mark Wielaard <mark@klomp.org> * Makefile.am (ICEDTEA_PATCHES): Add patches/icedtea-gervill.patch. * Makefile.in: Regenerated. * patches/icedtea-gervill.patch: New patch. * overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/*: New Gervill files.
author Mark Wielaard <mark@klomp.org>
date Wed, 30 Apr 2008 22:09:08 +0200
parents
children
line wrap: on
line source

/*
 * Copyright 2007 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.IOException;
import java.io.InputStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;

/**
 * A jitter corrector to be used with SoftAudioPusher.
 * 
 * @version %I%, %E%
 * @author Karl Helgason
 */
public class SoftJitterCorrector extends AudioInputStream {
	private static class JitterStream extends InputStream {
		
		static int MAX_BUFFER_SIZE = 1048576;
		
		boolean active = true;
		Thread thread;
		AudioInputStream stream;	
		
		// Cyclic buffer
		int writepos = 0;
		int readpos = 0;
		byte[][] buffers;		
		byte[] nullbuff;

		// Adapative Drift Statistics
		int w_count = 1000;
		int w_min_tol = 2;
		int w_max_tol = 10;		
		int w = 0;
		int w_min = -1;
				
		// Current read buffer
		int bbuffer_pos = 0;
		int bbuffer_max = 0;
		byte[] bbuffer = null; 
		
		public byte[] nextReadBuffer()
		{
			synchronized(buffers)
			{
				if(writepos > readpos)
				{
					int w_m = writepos - readpos;
					if(w_m < w_min) w_min = w_m;
					
					int buffpos = readpos;				
					readpos++;
					return buffers[buffpos % buffers.length];
				}
				w_min = -1;
				w = w_count-1;
			}
			while(true)
			{
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
					return null;
				}
				synchronized(buffers)
				{
					if(writepos > readpos)
					{
						w = 0;
						w_min = -1;
						w = w_count-1;
						int buffpos = readpos;				
						readpos++;
						return buffers[buffpos % buffers.length];
					}						
				}
			}
		}
		
		public byte[] nextWriteBuffer()
		{
			synchronized(buffers)
			{			
				return buffers[writepos % buffers.length];			
			}
		}
		
		public void commit()
		{
			synchronized(buffers)
			{			
				writepos++;
				if((writepos - readpos) > buffers.length)
				{
					int newsize = (writepos - readpos) + 10;
					newsize = Math.max(buffers.length*2, newsize);
					buffers = new byte[newsize][buffers[0].length];
				}
			}		
		}
		
		public JitterStream(AudioInputStream s, int buffersize, int smallbuffersize)
		{
			this.w_count = 10 * (buffersize/smallbuffersize);
			if(w_count < 100) w_count = 100;
			this.buffers = new byte[(buffersize/smallbuffersize)+10][smallbuffersize];
			this.bbuffer_max = MAX_BUFFER_SIZE / smallbuffersize;
			this.nullbuff = new byte[smallbuffersize];
			this.stream = s;
			

			Runnable runnable = new Runnable()
			{
				public void run()
				{				
					AudioFormat format = stream.getFormat();
					int bufflen = buffers[0].length;
					int frames = bufflen / format.getFrameSize();
					long nanos = (long) ( frames * 1000000000.0 / format.getSampleRate() );					
					long now = System.nanoTime();
					long next = now + nanos;
					int correction = 0;
					while(true)
					{
						synchronized (JitterStream.this) {
							if(!active) break;
						}				
						int curbuffsize;
						synchronized (buffers) {
							curbuffsize = writepos - readpos;
							if(correction == 0)
							{
								w++;
								if(w_min != Integer.MAX_VALUE)
								if(w == w_count)
								{					
									correction = 0;														
									if(w_min < w_min_tol) correction = (w_min_tol+ w_max_tol)/2 - w_min;
									if(w_min > w_max_tol) correction = (w_min_tol+ w_max_tol)/2 - w_min;
									w = 0;
									w_min = Integer.MAX_VALUE;
								}				
							}
						}
						while(curbuffsize > bbuffer_max)
						{
							synchronized (buffers) {
								curbuffsize = writepos - readpos;
							}
							synchronized (JitterStream.this) {
								if(!active) break;
							}				
							try {
								Thread.sleep(1);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						
						if(correction < 0)
						{
							correction++;
						}
						else
						{
							byte[] buff = nextWriteBuffer();
							try {
								stream.read(buff, 0, buff.length);
							} catch (IOException e1) {
								e1.printStackTrace();
							}
							commit();
						}
						
						if(correction > 0)
						{
							correction--;
							next = System.nanoTime() + nanos;
							continue;
						}
						long wait = next - System.nanoTime(); 
						if(wait > 0)
						{
							try {
								Thread.sleep(wait / 1000000L);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}
						next += nanos;
					}
				}
			};
			
			thread = new Thread(runnable);
			thread.setPriority(Thread.MAX_PRIORITY);
			thread.start();
		}

		public void close() throws IOException {
			synchronized (this) {
				active = false;
			}			
			try {
				thread.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			stream.close();
		}		
		
		public int read() throws IOException {
			byte[] b = new byte[1];
			if (read(b) == -1)
				return -1;
			return b[0] & 0xFF;
		}
		
		public void fillBuffer() {
			bbuffer = nextReadBuffer();
			bbuffer_pos = 0;
		}

		public int read(byte[] b, int off, int len) {
			if(bbuffer == null) fillBuffer();
			int bbuffer_len = bbuffer.length;
			int offlen = off + len;
			while (off < offlen)
				if (available() == 0)
					fillBuffer();
				else {
					byte[] bbuffer = this.bbuffer;
					int bbuffer_pos = this.bbuffer_pos;
					while (off < offlen && bbuffer_pos < bbuffer_len)
						b[off++] = bbuffer[bbuffer_pos++];
					this.bbuffer_pos = bbuffer_pos;
				}
			return len;
		}
		
		public int available() {
			return bbuffer.length - bbuffer_pos;
		}	

	};

	public SoftJitterCorrector(AudioInputStream stream, int buffersize, int smallbuffersize) {		
		super(new JitterStream(stream, buffersize, smallbuffersize), stream.getFormat(), stream
				.getFrameLength());
	}

}