f@0: /*
f@0: Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
f@0:
f@0: Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
f@0:
f@0: This program is free software: you can redistribute it and/or modify
f@0: it under the terms of the GNU General Public License as published by
f@0: the Free Software Foundation, either version 3 of the License, or
f@0: (at your option) any later version.
f@0:
f@0: This program is distributed in the hope that it will be useful,
f@0: but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0: GNU General Public License for more details.
f@0:
f@0: You should have received a copy of the GNU General Public License
f@0: along with this program. If not, see .
f@0: */
f@0: package uk.ac.qmul.eecs.depic.patterns;
f@0:
f@1: /** Utilities for math operations */
f@0: public class MathUtils {
f@0:
f@0: public static boolean equal(float a, float b){
f@0: return Float.compare(a, b) == 0;
f@0: }
f@0:
f@0: public static boolean equal(double a, double b){
f@0: return Double.compare(a, b) == 0 ;
f@0:
f@0: }
f@0:
f@0: public static boolean equal(float a, float b , float epsilon){
f@0: return Math.abs(a-b) < epsilon;
f@0: }
f@0:
f@0: public static boolean equal(double a, double b , double epsilon){
f@0: return Math.abs(a-b) < epsilon;
f@0: }
f@0:
f@0:
f@0: /**
f@0: *
f@0: * @param amp amplitude value in the normalized form (ranging from 0 to 1)
f@0: *
f@0: * @return the decibel conversion of {@code amp}
f@0: */
f@0: public static float toDb(float amp){
f@0: if(Float.compare(amp,0) < 0){
f@0: throw new RuntimeException();
f@0: }
f@0:
f@0: return (float)(20.0 * Math.log10(amp));
f@0: }
f@0:
f@0: public static float toAmp(float db){
f@0: return (float) (Math.pow(10, db/ 20.0));
f@0: }
f@0:
f@1: /**
f@1: *
f@1: * Scales a float from one range of values to another. The scaling can be either lineas or exponential.
f@1: *
f@1: */
f@0: public static class Scale {
f@0: private Range from;
f@0: private Range to;
f@0:
f@0: public Scale(Range rangeFrom, Range rangeTo) {
f@0: this.from = rangeFrom;
f@0: this.to = rangeTo;
f@0: }
f@0:
f@0: public Scale(float startFrom, float endFrom,float startTo, float endTo) {
f@0: this(new Range(startFrom,endFrom),new Range(startTo,endTo));
f@0: }
f@0:
f@0: public float linear(float num){
f@0: if(Float.compare(num, from.getStart()) < 0 || Float.compare(num,from.getEnd()) > 0)
f@0: throw new IllegalArgumentException("num must be in rangeFrom interval. num:"+num+" rangeFrom:"+from);
f@0:
f@0: float ratio = (num - from.getStart()) / from.lenght();
f@0:
f@0: return to.getStart() + (ratio * to.lenght());
f@0: }
f@0:
f@0: public float exponential(float num){
f@0: if(Float.compare(num, from.getStart()) < 0 || Float.compare(num,from.getEnd()) > 0)
f@0: throw new IllegalArgumentException("num must be in rangeFrom interval. num:"+num+" rangeFrom:"+from);
f@0:
f@0: if(Float.compare(to.getStart(),0) == 0 )
f@0: throw new IllegalArgumentException("Cannot call esponential when destination range starts from 0");
f@0:
f@0: num = (num - from.getStart())/ from.lenght();
f@0:
f@0: float toRatio = to.getEnd() / to.getStart();
f@0: return (float) (Math.pow(toRatio,num) * to.getStart());
f@0: }
f@0: }
f@0:
f@0: /**
f@0: *
f@0: * Calculates values at any time in a sequence by interpolating the sequence values.
f@0: *
f@0: * A sequence is a finite series of float values at given times. In order to get values
f@0: * at any times, interpolation is necessary. This class can be useful to build a graph out of
f@0: * the sequence values. Of course, as the interpolation changes, the graph changes as well, as
f@0: * values at times different from those specified by the sequence will be calculated differently.
f@0: *
f@0: */
f@0: public static class Interpolate {
f@0: private Sequence sequence;
f@0:
f@0: public Interpolate(Sequence sequence){
f@0: this.sequence = sequence;
f@0: }
f@0:
f@0: public float linear(float time){
f@0: /* initialise with start and end of the sequence */
f@0: float startValue = sequence.getBegin();
f@0: float endValue = sequence.getEnd();
f@0: float startTimePos = 0;
f@0: float endTimePos = sequence.getLen();
f@0:
f@0:
f@0: for(int i=0; i= 0){
f@0: startValue = sv.getValue();
f@0: startTimePos = sv.getTimePosition();
f@0: }
f@0:
f@0: if(Float.compare(time, sv.getTimePosition()) <= 0){
f@0: endValue = sv.getValue();
f@0: endTimePos = sv.getTimePosition();
f@0: break; // can break because values are ordered by time
f@0: }
f@0: }
f@0:
f@0: /* let D be the distance between time and start, then ratio *
f@0: * is the ratio between D and the start-end interval length *
f@0: * if startTimepos and endtimePos the ratio is going to be 0*/
f@0: float ratio = MathUtils.equal(startTimePos, endTimePos) ? 0.0f : ((time - startTimePos) / (endTimePos-startTimePos));
f@0:
f@0:
f@0: /* this comes from the following formula:
f@0: * valueAtTime = start.getValue + (ratio * (end.getValue() - start.getValue()) */
f@0: float valueAtTime = (1.0f - ratio) * startValue + ratio * endValue;
f@0:
f@0: return valueAtTime;
f@0: }
f@0:
f@0: }
f@0: }