view src/uk/ac/qmul/eecs/depic/patterns/MathUtils.java @ 4:473da40f3d39 tip

added html formatting to Daw/package-info.java
author Fiore Martin <f.martin@qmul.ac.uk>
date Thu, 25 Feb 2016 17:50:09 +0000
parents 629262395647
children
line wrap: on
line source
/*  
 Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.

 Copyright (C) 2015  Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
	
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program 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 for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.qmul.eecs.depic.patterns;

/** Utilities for math operations */
public class MathUtils {
	
	public static boolean equal(float a, float b){
		return Float.compare(a, b) == 0;
	}
	
	public static boolean equal(double a, double b){
		return Double.compare(a, b) == 0 ;
		
	}
	
	public static boolean equal(float a, float b , float epsilon){
		return Math.abs(a-b) < epsilon;
	}
	
	public static boolean equal(double a, double b , double epsilon){
		return Math.abs(a-b) < epsilon;
	}

	
	/**
	 * 
	 * @param amp amplitude value in the normalized form (ranging from 0 to 1)
	 * 
	 * @return the decibel conversion of {@code amp}
	 */
	public static float toDb(float amp){
		if(Float.compare(amp,0) < 0){
			throw new RuntimeException();
		}
		
		return (float)(20.0 * Math.log10(amp));
	}
	
	public static float toAmp(float db){
		return (float) (Math.pow(10, db/ 20.0));
	}
	
	/**
	 * 
	 * Scales a float from one range of values to another. The scaling can be either lineas or exponential. 
	 *
	 */
	public static class Scale {
		private Range<java.lang.Float> from;
		private Range<java.lang.Float> to;
	
		public Scale(Range<java.lang.Float> rangeFrom, Range<java.lang.Float> rangeTo) {
			this.from = rangeFrom;
			this.to = rangeTo;
		}
		
		public Scale(float startFrom, float endFrom,float startTo, float endTo) {
			this(new Range<java.lang.Float>(startFrom,endFrom),new Range<java.lang.Float>(startTo,endTo));
		}

		public float linear(float num){
			if(Float.compare(num, from.getStart()) < 0 || Float.compare(num,from.getEnd()) > 0)
				throw new IllegalArgumentException("num must be in rangeFrom interval. num:"+num+" rangeFrom:"+from);
			
			float ratio = (num - from.getStart()) / from.lenght();
			
			return to.getStart() + (ratio * to.lenght());
		}
		
		public float exponential(float num){
			if(Float.compare(num, from.getStart()) < 0 || Float.compare(num,from.getEnd()) > 0)
				throw new IllegalArgumentException("num must be in rangeFrom interval. num:"+num+" rangeFrom:"+from);
			
			if(Float.compare(to.getStart(),0) == 0 )
				throw new IllegalArgumentException("Cannot call esponential when destination range starts from 0");
			
			num = (num - from.getStart())/ from.lenght();
			
			float toRatio = to.getEnd() / to.getStart();
			return (float) (Math.pow(toRatio,num) * to.getStart());
		}
	}
	
	/**
	 * 
	 * Calculates values at any time in a sequence by interpolating the sequence values.
	 * 
	 * A sequence is a finite series of float values at given times. In order to get values 
	 * at any times, interpolation is necessary. This class can be useful to build a graph out of 
	 * the sequence values. Of course, as the interpolation changes, the graph changes as well, as 
	 * values at times different from those specified by the sequence will be calculated differently.
	 *
	 */
	public static class Interpolate {
		private Sequence sequence;
		
		public Interpolate(Sequence sequence){
			this.sequence = sequence;
		}
		
		public float linear(float time){
			/* initialise with start and end of the sequence */
			float startValue = sequence.getBegin();
			float endValue = sequence.getEnd();
			float startTimePos = 0;
			float endTimePos = sequence.getLen();
			
			
			for(int i=0; i<sequence.getValuesNum(); i++){
				Sequence.Value sv = sequence.getValueAt(i); 
				
				if(Float.compare(time, sv.getTimePosition())  >= 0){
					startValue = sv.getValue();
					startTimePos = sv.getTimePosition();
				}
				
				if(Float.compare(time, sv.getTimePosition()) <= 0){
					endValue = sv.getValue();
					endTimePos = sv.getTimePosition();
					break; // can break because values are ordered by time 
				}
			}
				
			/* let D be the distance between time and start, then ratio * 
			 * is the ratio between D and the start-end interval length *
			 * if startTimepos and endtimePos the ratio is going to be 0*/
			float ratio = MathUtils.equal(startTimePos, endTimePos) ? 0.0f : ((time - startTimePos) / (endTimePos-startTimePos));
			
			
			/* this comes from the following formula:
			 * valueAtTime = start.getValue + (ratio * (end.getValue() - start.getValue()) */
			float valueAtTime = (1.0f - ratio) * startValue + ratio * endValue;
			
			return valueAtTime;
		}
		
	}
}