view overlays/openjdk/jdk/src/share/classes/com/sun/media/sound/SoftAbstractResampler.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.util.Arrays;

import javax.sound.midi.MidiChannel;
import javax.sound.midi.VoiceStatus;

/**
 * 
 * Abstract resampler class. 
 * 
 * @version %I%, %E%
 * @author Karl Helgason
 */
public abstract class SoftAbstractResampler implements SoftResampler {

	private class ModelAbstractResamplerStream implements SoftResamplerStreamer {
		AudioFloatInputStream stream;

		boolean stream_eof = false;

		int loopmode;
		boolean loopdirection = true; // true = forward

		float loopstart;

		float looplen;

		float target_pitch;

		float[] current_pitch = new float[1];

		boolean started;

		boolean eof;

		boolean loopreadevent = false;

		int sector_pos = 0;

		int sector_size = 400;

		int sector_loopstart = -1;

		boolean markset = false;

		int marklimit = 0;

		int streampos = 0;

		int nrofchannels = 2;
		
		boolean noteOff_flag = false;

		float[][] ibuffer;
		boolean ibuffer_order = true;

		float[] sbuffer;

		int pad;

		int pad2;

		float[] ix = new float[1];
		
		int[] ox = new int[1];

		float samplerateconv = 1;

		float pitchcorrection = 0;

		boolean streammarked = true;

		public ModelAbstractResamplerStream() {
			pad = getPadding();
			pad2 = getPadding() * 2;
			ibuffer = new float[2][sector_size + pad2];
			ibuffer_order = true;		
		}

		public void noteOn(MidiChannel channel, VoiceStatus voice,
				int noteNumber, int velocity) {
		}

		public void noteOff(int velocity) {
			noteOff_flag = true;
		}

		public void open(ModelWavetable osc, float outputsamplerate)
				throws IOException {

			eof = false;
			nrofchannels = osc.getChannels();
			if (ibuffer.length < nrofchannels)
			{
				ibuffer = new float[nrofchannels][sector_size + pad2];
			}

			stream = osc.openStream();
			streampos = 0;
			stream_eof = false;
			pitchcorrection = osc.getPitchcorrection();
			samplerateconv = stream.getFormat().getSampleRate()
					/ outputsamplerate;
			looplen = osc.getLoopLength();
			loopstart = osc.getLoopStart();
			sector_loopstart = (int) (loopstart / sector_size);
			sector_loopstart = sector_loopstart - 1;

			sector_pos = 0;

			if (sector_loopstart < 0)
				sector_loopstart = 0;
			started = false;
			loopmode = osc.getLoopType();

			if (loopmode != 0) {
				markset = false;
				marklimit = nrofchannels * (int) (looplen + pad2 + 1);
			} else
				markset = true;
			// loopmode = 0;

			target_pitch = samplerateconv;
			current_pitch[0] = samplerateconv;
			
			ibuffer_order = true;				
			loopdirection = true;
			noteOff_flag = false;

			for (int i = 0; i < nrofchannels; i++)
				Arrays.fill(ibuffer[i], sector_size, sector_size + pad2, 0);
			ix[0] = pad;
			eof = false;
			loopreadevent = false;

			ix[0] = sector_size + pad;
			sector_pos = -1;
			streampos = -sector_size;

			nextBuffer();
		}

		public void setPitch(float pitch) {
			/*
			this.pitch = (float) Math.pow(2f,
					(pitchcorrection + pitch) / 1200.0f)
					* samplerateconv;
			*/
			this.target_pitch = (float) Math.exp(
					(pitchcorrection + pitch) * (Math.log(2.0) / 1200.0))
					* samplerateconv;
			
			if (!started)
				current_pitch[0] = this.target_pitch;
		}

		public void nextBuffer() throws IOException {
			if (ix[0] < pad) {
				if (markset) {
					// reset to target sector
					stream.reset();
					ix[0] += streampos - (sector_loopstart * sector_size);
					sector_pos = sector_loopstart;
					streampos = sector_pos * sector_size;
					;

					// and go one sector backward
					ix[0] += sector_size;
					sector_pos -= 1;
					streampos -= sector_size;
					stream_eof = false;
				}
			}

			if (ix[0] >= sector_size + pad)
				if (stream_eof) {
					eof = true;
					return;
				}
			
			if(ix[0] >= sector_size*4 + pad)
			{
				int skips = (int)( (ix[0] - sector_size*4 + pad)/sector_size );
				ix[0] -= sector_size*skips;
				sector_pos += skips;
				streampos += sector_size*skips;
				stream.skip(sector_size*skips);
			} 

			while (ix[0] >= sector_size + pad) {
				if (!markset)
					if (sector_pos + 1 == sector_loopstart) {
						stream.mark(marklimit);
						markset = true;
					}
				ix[0] -= sector_size;
				sector_pos++;
				streampos += sector_size;

				for (int c = 0; c < nrofchannels; c++) {
					float[] cbuffer = ibuffer[c];
					for (int i = 0; i < pad2; i++)
						cbuffer[i] = cbuffer[i + sector_size];
				}

				int ret;
				if (nrofchannels == 1)
					ret = stream.read(ibuffer[0], pad2, sector_size);
				else {
					int slen = sector_size * nrofchannels;
					if (sbuffer == null || sbuffer.length < slen)
						sbuffer = new float[slen];
					int sret = stream.read(sbuffer, 0, slen);
					if (sret == -1)
						ret = -1;
					else {
						ret = sret / nrofchannels;
						for (int i = 0; i < nrofchannels; i++) {
							float[] buff = ibuffer[i];
							int ix = i;
							int ix_step = nrofchannels;
							int ox = pad2;
							for (int j = 0; j < ret; j++, ix += ix_step, ox++)
								buff[ox] = sbuffer[ix];
						}
					}

				}

				if (ret == -1) {
					ret = 0;
					stream_eof = true;
					for (int i = 0; i < nrofchannels; i++)
						Arrays.fill(ibuffer[i], pad2, pad2 + sector_size, 0f);
					return;
				}
				if (ret != sector_size)
					for (int i = 0; i < nrofchannels; i++)
						Arrays.fill(ibuffer[i], pad2 + ret, pad2 + sector_size,
								0f);
								
				ibuffer_order = true;				

			}

		}
		
		public void reverseBuffers()
		{
			ibuffer_order = !ibuffer_order;
			for (int c = 0; c < nrofchannels; c++)
			{
				float[] cbuff = ibuffer[c];
				int len = cbuff.length-1;
				int len2 = cbuff.length/2; 
				for (int i = 0; i < len2; i++) {
					float x = cbuff[i];
					cbuff[i] = cbuff[len - i];
					cbuff[len - i] = x;
				}
			}
		}

		public int read(float[][] buffer, int offset, int len)
				throws IOException {

			if (eof)
				return -1;
			
			if(noteOff_flag)
				if((loopmode & 2) != 0)
					if(loopdirection)
						loopmode = 0;
			
			
			float pitchstep = (target_pitch - current_pitch[0]) / len;
			float[] current_pitch = this.current_pitch;
			started = true;

			int[] ox = this.ox;
			ox[0] = offset;
			int ox_end = len + offset;			
				
			float ixend = sector_size + pad;
			if(!loopdirection) ixend = pad;
			while (ox[0] != ox_end) {
				nextBuffer();
				if(!loopdirection)
				{
					// If we are in backward playing part of pingpong or reverse loop
					
					if (streampos < (loopstart + pad)) {
						ixend = loopstart - streampos + pad2;
						if(ix[0] <= ixend)
						{							
							if((loopmode & 4) != 0)
							{
								// Ping pong loop, change loopdirection
								loopdirection = true;
								ixend = sector_size + pad;
								continue;
							}
		
							ix[0] += looplen;			
							ixend = pad;
							continue;							
						}
					}
					
					if(ibuffer_order != loopdirection) reverseBuffers();
					
					ix[0] = (sector_size + pad2) - ix[0];
					ixend = (sector_size + pad2) - ixend;
					ixend++;
													
					float bak_ix = ix[0];
					int bak_ox = ox[0];
					float bak_pitch = current_pitch[0];
					for (int i = 0; i < nrofchannels; i++)
						if (buffer[i] != null) {
							ix[0] = bak_ix;
							ox[0] = bak_ox;
							current_pitch[0] = bak_pitch;
							interpolate(ibuffer[i], ix, ixend, current_pitch, pitchstep,
									buffer[i], ox, ox_end);
						}
					
					ix[0] = (sector_size + pad2) - ix[0];
					ixend--;
					ixend = (sector_size + pad2) - ixend;

					if (eof) {
						current_pitch[0] = this.target_pitch;
						return ox[0] - offset;
					}					
					
					continue;
				}
				if (loopmode != 0)					
					if (streampos + sector_size > (looplen + loopstart + pad)) {
						ixend = loopstart + looplen - streampos + pad2;
						if (ix[0] >= ixend) {							
							if((loopmode & 4) != 0 || (loopmode & 8) != 0 )
							{
								// Ping pong or revese loop, change loopdirection
								loopdirection = false;
								ixend = pad;
								continue;
							}
							ixend = sector_size + pad;
							ix[0] -= looplen;							
							continue;
						}
					}
				
				if(ibuffer_order != loopdirection) reverseBuffers();

				float bak_ix = ix[0];
				int bak_ox = ox[0];
				float bak_pitch = current_pitch[0];
				for (int i = 0; i < nrofchannels; i++)
					if (buffer[i] != null) {
						ix[0] = bak_ix;
						ox[0] = bak_ox;
						current_pitch[0] = bak_pitch;
						interpolate(ibuffer[i], ix, ixend, current_pitch, pitchstep,
								buffer[i], ox, ox_end);
					}

				if (eof) {
					current_pitch[0] = this.target_pitch;
					return ox[0] - offset;
				}
			}

			current_pitch[0] = this.target_pitch;
			return len;
		}

		public void close() throws IOException {
			stream.close();
		}
	}

	public abstract int getPadding();

	public abstract void interpolate(float[] in, float[] in_offset,
			float in_end, float[] pitch, float pitchstep, float[] out,
			int[] out_offset, int out_end);

	public SoftResamplerStreamer openStreamer() {
		return new ModelAbstractResamplerStream();
	}

}