/* * BlendContext.java * Eisenkraut * * Copyright (c) 2004-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de */ package de.sciss.eisenkraut.io; import java.awt.geom.CubicCurve2D; import java.awt.geom.Point2D; /** * Object describing the * type of cross-fading used * in vector operations. Methods * for calculating the fades are provided. */ public class BlendContext { /* * The length of the * blending cross-fade in * sense rate frames */ private final long left, right; private final double[] eqn = new double[ 4 ]; // private final double yInA, yInB, yInD; private final double yOutA, yOutB, yOutD, yXInA, yXInB, yXInD; private final double wFact; private final double yIn0 = 0.0, yOut0 = 1.0; /** * Create a new BlendContext with * the given length */ public BlendContext(long left, long right, Point2D[] ctrlPt) { this.left = left; this.right = right; eqn[ 1 ] = 3 * ctrlPt[0].getX(); eqn[ 2 ] = -6 * ctrlPt[0].getX() + 3 * ctrlPt[1].getX(); eqn[ 3 ] = 3 * ctrlPt[0].getX() - 3 * ctrlPt[1].getX() + 1; // yInD = 3 * ctrlPt[0].getY() - 3 * ctrlPt[1].getY() + 1; // yInA = -6 * ctrlPt[0].getY() + 3 * ctrlPt[1].getY(); // yInB = 3 * ctrlPt[0].getY(); // y = t^3 (3 * ctrlPt1_y - 3 * ctrlPt2_y + 1) + t^2 (-6 * ctrlPt1_y + 3 * ctrlPt2_y) + t (3 * ctrlPt1_y) // D A B yXInD = 3 * ctrlPt[1].getY() - 3 * ctrlPt[0].getY() + 1; yXInA = -6 * ctrlPt[1].getY() + 3 * ctrlPt[0].getY(); yXInB = 3 * ctrlPt[1].getY(); yOutD = 3 * ctrlPt[0].getY() - 3 * ctrlPt[1].getY() - 1; yOutA = -6 * ctrlPt[0].getY() + 3 * ctrlPt[1].getY() + 3; yOutB = 3 * ctrlPt[0].getY() - 3; wFact = 1.0 / (left + right); } /* P(t) = B(3,0)*startPt + B(3,1)*ctrlPt1 + B(3,2)*ctrlPt2 + B(3,3)*endPt 0 <= t <= 1 B(n,m) = C(n,m) * t^(m) * (1 - t)^(n-m) C(n,m) = n! / (m! * (n-m)!) C(3,0) = 1; C(3,1) = 3; C(3,2) = 3; C(3,3) = 1 B(3,0) = C(3,0) * t^0 * (1-t)^3 = (1-t)^3 = -t^3 + 3t^2 - 3t + 1 B(3,1) = C(3,1) * t^1 * (1-t)^2 = 3 * t * (1-t)^2 = 3t^3 - 6t^2 + 3t B(3,2) = C(3,2) * t^2 * (1-t)^1 = 3 * t^2 * (1-t) = -3t^3 + 3t^2 B(3,3) = C(3,3) * t^3 * (1-t)^0 = t^3 = t^3 t^3 (3 * ctrlPt1_x - 3 * ctrlPt2_x + 1) + t^2 (-6 * ctrlPt1_x + 3 * ctrlPt2_x) + t (3 * ctrlPt1_x) - x = 0 D A B C y = t^3 (3 * ctrlPt1_y - 3 * ctrlPt2_y + 1) + t^2 (-6 * ctrlPt1_y + 3 * ctrlPt2_y) + t (3 * ctrlPt1_y) y = t^3 (-startPt_y + 3 * ctrlPt1_y - 3 * ctrlPt2_y + endPt_y) + t^2 (3 * startPt_y - 6 * ctrlPt1_y + 3 * ctrlPt2_y) + t (-3 * startPt_y + 3 * ctrlPt1_y) + 1 (startPt_y) um t aus x auszurechnen: eqn = {c, b, a, d}; eqn[0] = -x eqn[1] = 3 * ctrlPt1_x eqn[2] = -6 * ctrlPt1_x + 3 * ctrlPt2_x eqn[3] = 3 * ctrlPt1_x - 3 * ctrlPt2_x + 1 um y fuer fade-in auszurechnen: (startPt_y := 0, endPt_y := 1) y = t^3 (3 * ctrlPt1_y - 3 * ctrlPt2_y + 1) + t^2 (-6 * ctrlPt1_y + 3 * ctrlPt2_y) + t (3 * ctrlPt1_y) D A B um y fuer fade-out auszurechnen: (startPt_y := 1, endPt_y := 0) y = t^3 (3 * ctrlPt1_y - 3 * ctrlPt2_y - 1) + t^2 (-6 * ctrlPt1_y + 3 * ctrlPt2_y + 3) + t (3 * ctrlPt1_y - 3) + startPt_y D A B */ // public long getBlendLen() // { // return pre + post; // } public long getLen() { return left + right; } public long getLeftLen() { return left; } public long getRightLen() { return right; } /** * @param targetBuf single channels can be <code>null</code> */ public void fadeIn( long blendOff, float[][] sourceBuf, int sourceOff, float[][] targetBuf, int targetOff, int length ) { fade( blendOff, sourceBuf, sourceOff, targetBuf, targetOff, length, yXInD, yXInB, yXInA, yIn0 ); // yX !! } public void fadeOut( long blendOff, float[][] sourceBuf, int sourceOff, float[][] targetBuf, int targetOff, int length ) { fade( blendOff, sourceBuf, sourceOff, targetBuf, targetOff, length, yOutD, yOutB, yOutA, yOut0 ); } private void fade( long blendOff, float[][] sourceBuf, int sourceOff, float[][] targetBuf, int targetOff, int length, double yD, double yB, double yA, double y0 ) { final int numCh = sourceBuf.length; final double wOff = blendOff * wFact; final double[] res = new double[ 3 ]; double t, tt; int ch; float w; for( int i = 0; i < length; i++, sourceOff++, targetOff++ ) { eqn[ 0 ] = -(wOff + wFact * i); // C = -x CubicCurve2D.solveCubic( eqn, res ); t = res[ 0 ]; if( t < 0.0 || t > 1.0 ) { t = res[ 1 ]; if( t < 0.0 || t > 1.0 ) { t = res[ 2 ]; // if( t < 0.0 || t > 1.0 ) { // System.err.println( "oh dear. res[0] = "+res[0]+"; res[1] = "+res[1]+"; res[2] = "+res[2] ); // } } } tt = t * t; w = (float) (tt * t * yD + tt * yA + t * yB + y0); for( ch = 0; ch < numCh; ch++ ) { if( targetBuf[ ch ] != null ) { targetBuf[ ch ][ targetOff ] = sourceBuf[ ch ][ sourceOff ] * w; } } } } /** * @param sourceBufA fading out * @param sourceBufB fading in */ public void blend( long blendOff, float[][] sourceBufA, int sourceOffA, float[][] sourceBufB, int sourceOffB, float[][] targetBuf, int targetOff, int length ) { //System.err.println( "kieka! "+blendLen ); final int numCh = sourceBufA.length; final double wOff = blendOff * wFact; final double[] res = new double[ 3 ]; int len2; double t, tt, ttt; int i = 0; int ch; float wIn, wOut; // plain A len2 = (int) Math.min( length, -blendOff ); if( len2 > 0 ) { for( ch = 0; ch < numCh; ch++ ) { if( targetBuf[ ch ] != null ) { System.arraycopy( sourceBufA[ ch ], sourceOffA, targetBuf[ ch ], targetOff, len2 ); } } i += len2; sourceOffA += len2; sourceOffB += len2; targetOff += len2; } // xfade len2 = (int) Math.min( length, left + right - blendOff ); for( ; i < len2; i++, sourceOffA++, sourceOffB++, targetOff++ ) { eqn[ 0 ] = -(wOff + wFact * i); // C = -x CubicCurve2D.solveCubic( eqn, res ); t = res[ 0 ]; if( t < 0.0 || t > 1.0 ) { t = res[ 1 ]; if( t < 0.0 || t > 1.0 ) { t = res[ 2 ]; // if( t < 0.0 || t > 1.0 ) { // System.err.println( "oh dear. res[0] = "+res[0]+"; res[1] = "+res[1]+"; res[2] = "+res[2] ); // } } } tt = t * t; ttt = tt * t; wIn = (float) (ttt * yXInD + tt * yXInA + t * yXInB + yIn0); wOut = (float) (ttt * yOutD + tt * yOutA + t * yOutB + yOut0); for( ch = 0; ch < numCh; ch++ ) { if( targetBuf[ ch ] != null ) { targetBuf[ ch ][ targetOff ] = sourceBufB[ ch ][ sourceOffB ] * wIn + sourceBufA[ ch ][ sourceOffA ] * wOut; } } } // plain B len2 = length - i; if( len2 > 0 ) { for( ch = 0; ch < numCh; ch++ ) { if( targetBuf[ ch ] != null ) { System.arraycopy( sourceBufB[ ch ], sourceOffB, targetBuf[ ch ], targetOff, len2 ); } } } } }