Example #1
0
void Ambix_directional_loudnessAudioProcessor::calcParams()
{
    if (!_initialized)
    {
        
        sph_h.Init(AMBI_ORDER);
        
        const String t_design_txt (t_design::des_3_240_21_txt);
        
        // std::cout << t_design_txt << std::endl;
        
        String::CharPointerType lineChar = t_design_txt.getCharPointer();
        
        int n = 0; // how many characters been read
        int numsamples = 0;
        int i = 0;
        
        int curr_n = 0;
        int max_n = lineChar.length();
        
        while (curr_n < max_n) { // check how many coordinates we have
            double value;
            sscanf(lineChar, "%lf\n%n", &value, &n);
            lineChar += n;
            curr_n += n;
            numsamples++;
        } // end parse numbers
        
        numsamples = numsamples/3; // xyz
        
        Carth_coord.resize(numsamples,3); // positions in cartesian coordinates
        
        curr_n = 0;
        lineChar = t_design_txt.getCharPointer();
        
        // parse line for numbers again and copy to carth coordinate matrix
        while (i < numsamples) {
            
            double x,y,z;
            
            sscanf(lineChar, "%lf%lf%lf%n", &x, &y, &z, &n);
            
            Carth_coord(i,0) = x;
            Carth_coord(i,1) = y;
            Carth_coord(i,2) = z;
            
            lineChar += n;
            
            curr_n += n;
            i++;
            
        } // end parse numbers
        
        // std::cout << "Coordinate size: " << Carth_coord.rows() << " x " << Carth_coord.cols() << std::endl;
        // std::cout << Carth_coord << std::endl;
        
        Sph_coord.resize(numsamples,2); // positions in spherical coordinates
        
        
        Sh_matrix.setZero(numsamples,AMBI_CHANNELS);
        
        for (int i=0; i < numsamples; i++)
        {
            Eigen::VectorXd Ymn(AMBI_CHANNELS); // Ymn result
            
            Sph_coord(i,0) = atan2(Carth_coord(i,1),Carth_coord(i,0)); // azimuth
            Sph_coord(i,1) = atan2(Carth_coord(i,2),sqrt(Carth_coord(i,0)*Carth_coord(i,0) + Carth_coord(i,1)*Carth_coord(i,1))); // elevation
            
            sph_h.Calc(Sph_coord(i,0),Sph_coord(i,1)); // phi theta
            sph_h.Get(Ymn);
            
            // std::cout << "Size: " << Ymn.size() << ": " << Ymn << std::endl;
            
            //Sh_matrix.row(i) = Ymn;
            Sh_matrix.block(i,0,1,AMBI_CHANNELS) = Ymn.transpose();
            // std::cout << "Size: " << Sh_matrix.block(i,0,1,in_ambi_channels).size() << ": " << Sh_matrix.block(i,0,1,in_ambi_channels) << std::endl;
        }
        
        // Sh_matrix_inv.setZero();
        
        //Sh_matrix_inv = (Sh_matrix.transpose()*Sh_matrix).inverse()*Sh_matrix.transpose(); // not working for dynamic input order
        // if input order is different a better solving has to be used for the inverse:
        Sh_matrix_inv = (Sh_matrix.transpose()*Sh_matrix).colPivHouseholderQr().inverse()*Sh_matrix.transpose();
        
        // std::cout << "Size: " << Sh_matrix_inv.rows() << " x " << Sh_matrix_inv.cols() << std::endl;
        // std::cout << Sh_matrix_inv << std::endl;
        
        _initialized = true;
        
    }
    
    if (_param_changed)
    {
        // convert parameters to values for the filter
        // ArrayIntParam _shape = shape;
        
        ArrayParam _width = width * (float)M_PI; // 0...pi
        ArrayParam _height = height * (float)M_PI;
        
        ArrayParam _gain;
        
        for (int i=0; i < gain.rows();i++)
        {
            _gain(i) = ParamToRMS(gain(i));
        }
        
        SphCoordParam _center_sph = (center_sph - 0.5f)*2.f*(float)M_PI;
        
        // std::cout << _center_sph << std::endl;
        
        CarthCoordParam _center_carth;
        
        // convert center spherical coordinates to carthesian
        for (int i=0; i < _center_sph.rows(); i++)
        {
            _center_carth(i,0) = cos(_center_sph(i,0))*cos(_center_sph(i,1)); // x
            _center_carth(i,1) = sin(_center_sph(i,0))*cos(_center_sph(i,1)); // y
            _center_carth(i,2) = sin(_center_sph(i,1)); // z
        }
        
        // scale the SH_matrix and save as Sh_matrix_mod
        Sh_matrix_mod = Sh_matrix;
        
        // iterate over all sample points
        for (int i=0; i < Sh_matrix_mod.rows(); i++)
        {
            double multipl = 1.f;
            
            // iterate over all filters
            for (int k=0; k < NUM_FILTERS; k++)
            {
                Eigen::Vector2d Sph_coord_vec = Sph_coord.row(i);
                Eigen::Vector2d _center_sph_vec = _center_sph.row(k);
                
                multipl *= (double)sph_filter.GetWeight(&Sph_coord_vec, Carth_coord.row(i), &_center_sph_vec, _center_carth.row(k), (int)shape(k), _width(k), _height(k), _gain(k), (window(k) > 0.5f), transition(k));
            }
            
            Sh_matrix_mod.row(i) *= multipl;
            
        }
        
        // calculate new transformation matrix
        Sh_transf = Sh_matrix_inv * Sh_matrix_mod;
        
        // threshold coefficients
        for (int i = 0; i < Sh_transf.size(); i++)
        {
            if (abs(Sh_transf(i)) < 0.00001f)
                Sh_transf(i) = 0.f;
        }
        
        _param_changed = false;
    }
    
    
}
Example #2
0
void Ambix_vmicAudioProcessor::calcParams()
{
    if (!_initialized)
    {
        
        sph_h.Init(AMBI_ORDER);
        
        const String t_design_txt (t_design::des_3_240_21_txt);
        
        // std::cout << t_design_txt << std::endl;
        
        String::CharPointerType lineChar = t_design_txt.getCharPointer();
        
        int n = 0; // how many characters been read
        int numsamples = 0;
        int i = 0;
        
        int curr_n = 0;
        int max_n = lineChar.length();
        
        while (curr_n < max_n) { // check how many coordinates we have
            double value;
            sscanf(lineChar, "%lf\n%n", &value, &n);
            lineChar += n;
            curr_n += n;
            numsamples++;
        } // end parse numbers
        
        numsamples = numsamples/3; // xyz
        
        Carth_coord.resize(numsamples,3); // positions in cartesian coordinates
        
        curr_n = 0;
        lineChar = t_design_txt.getCharPointer();
        
        // parse line for numbers again and copy to carth coordinate matrix
        while (i < numsamples) {
            
            double x,y,z;
            
            sscanf(lineChar, "%lf%lf%lf%n", &x, &y, &z, &n);
            
            Carth_coord(i,0) = x;
            Carth_coord(i,1) = y;
            Carth_coord(i,2) = z;
            
            lineChar += n;
            
            curr_n += n;
            i++;
            
        } // end parse numbers
        
        // std::cout << "Coordinate size: " << Carth_coord.rows() << " x " << Carth_coord.cols() << std::endl;
        // std::cout << Carth_coord << std::endl;
        
        Sph_coord.resize(numsamples,2); // positions in spherical coordinates
        
        
        Sh_matrix.setZero(numsamples,AMBI_CHANNELS);
        
        for (int i=0; i < numsamples; i++)
        {
            Eigen::VectorXd Ymn(AMBI_CHANNELS); // Ymn result
            
            Sph_coord(i,0) = atan2(Carth_coord(i,1),Carth_coord(i,0)); // azimuth
            Sph_coord(i,1) = atan2(Carth_coord(i,2),sqrt(Carth_coord(i,0)*Carth_coord(i,0) + Carth_coord(i,1)*Carth_coord(i,1))); // elevation
            
            sph_h.Calc(Sph_coord(i,0),Sph_coord(i,1)); // phi theta
            sph_h.Get(Ymn);
            
            Sh_matrix.block(i,0,1,AMBI_CHANNELS) = Ymn.transpose();
        }
        
        
        _initialized = true;
        
    }
    
    if (_param_changed)
    {
        // convert parameters to values for the filter
        // ArrayIntParam _shape = shape;
        
        ArrayParam _width = width * (float)M_PI; // 0...pi
        ArrayParam _height = height * (float)M_PI;
        
        ArrayParam _gain;
        
        for (int i=0; i < gain.rows();i++)
        {
            _gain(i) = ParamToRMS(gain(i));
        }
        
        SphCoordParam _center_sph = (center_sph - 0.5f)*2.f*M_PI;
        
        // std::cout << _center_sph << std::endl;
        
        CarthCoordParam _center_carth;
        
        // convert center spherical coordinates to carthesian
        for (int i=0; i < _center_sph.rows(); i++)
        {
            _center_carth(i,0) = cos(_center_sph(i,0))*cos(_center_sph(i,1)); // x
            _center_carth(i,1) = sin(_center_sph(i,0))*cos(_center_sph(i,1)); // y
            _center_carth(i,2) = sin(_center_sph(i,1)); // z
        }
        
        Sh_matrix_mod.setZero();
        
        // iterate over all filters
        for (int k=0; k < NUM_FILTERS_VMIC; k++)
        {
            // copy the SH_matrix to 
            Eigen::MatrixXd Sh_matrix_temp = Sh_matrix;
            
            // iterate over all sample points
            for (int i=0; i < Sh_matrix_temp.rows(); i++)
            {
                
                Eigen::Vector2d Sph_coord_vec = Sph_coord.row(i);
                Eigen::Vector2d _center_sph_vec = _center_sph.row(k);
                
                
                double multipl = sph_filter.GetWeight(&Sph_coord_vec, Carth_coord.row(i), &_center_sph_vec, _center_carth.row(k), (int)floor(shape(k)+0.5f), _width(k), _height(k), 1, true, transition(k));
                
                if (multipl < 0.f) // -1.f in case of outside region
                    multipl = 0.f;
                
                Sh_matrix_temp.row(i) *= multipl;
            }
            
            // Sh_matrix_mod row is the sum over the columns
            Sh_matrix_mod.row(k) = Sh_matrix_temp.colwise().sum();
            
            // normalize and apply gain
            if (Sh_matrix_mod.row(k)(0) > 0.f)
                Sh_matrix_mod.row(k) *= 1/Sh_matrix_mod.row(k)(0)*_gain(k); 
        }
        
        // std::cout << "Size Sh_matrix_mod : " << Sh_matrix_mod.rows() << " x " << Sh_matrix_mod.cols() << std::endl;
        
        // std::cout << Sh_matrix_mod << std::endl;
        
        
        // this is the new transformation matrix
        Sh_transf = Sh_matrix_mod;
        
        // threshold coefficients
        for (int i = 0; i < Sh_transf.size(); i++)
        {
            if (abs(Sh_transf(i)) < 0.00001f)
                Sh_transf(i) = 0.f;
        }
        
        _param_changed = false;
    }
    
    
}
Example #3
0
void Ambix_warpAudioProcessor::calcParams()
{
    if (!_initialized)
    {
    
        sph_h_in.Init(in_order);
        
        const String t_design_txt (t_design::des_3_240_21_txt);
        
        // std::cout << t_design_txt << std::endl;
        
        String::CharPointerType lineChar = t_design_txt.getCharPointer();
        
        int n = 0; // how many characters been read
        int numsamples = 0;
        int i = 0;
        
        int curr_n = 0;
        int max_n = lineChar.length();
        
        while (curr_n < max_n) { // check how many coordinates we have
            double value;
            sscanf(lineChar, "%lf\n%n", &value, &n);
            lineChar += n;            
            curr_n += n;
            numsamples++;            
        } // end parse numbers
        
        numsamples = numsamples/3; // xyz
        
        Carth_coord.resize(numsamples,3); // positions in cartesian coordinates
        
        curr_n = 0;
        lineChar = t_design_txt.getCharPointer();
        
        // parse line for numbers again and copy to carth coordinate matrix
        while (i < numsamples) {
            
            double x,y,z;
            
            sscanf(lineChar, "%lf%lf%lf%n", &x, &y, &z, &n);
            
            Carth_coord(i,0) = x;
            Carth_coord(i,1) = y;
            Carth_coord(i,2) = z;
            
            lineChar += n;
            
            curr_n += n;
            i++;
            
        } // end parse numbers
        
        // std::cout << "Coordinate size: " << Carth_coord.rows() << " x " << Carth_coord.cols() << std::endl;
        // std::cout << Carth_coord << std::endl;
        
        Sph_coord.resize(numsamples,2); // positions in spherical coordinates
        
        Eigen::MatrixXd Sh_matrix;
        Sh_matrix.setZero(numsamples,AMBI_CHANNELS);
        
        int in_ambi_channels = (in_order+1)*(in_order+1);
        for (int i=0; i < numsamples; i++)
        {
            Eigen::VectorXd Ymn(in_ambi_channels); // Ymn result
            
            Sph_coord(i,0) = atan2(Carth_coord(i,1),Carth_coord(i,0)); // azimuth
            Sph_coord(i,1) = atan2(Carth_coord(i,2),sqrt(Carth_coord(i,0)*Carth_coord(i,0) + Carth_coord(i,1)*Carth_coord(i,1))); // elevation
            
            sph_h_in.Calc(Sph_coord(i,0),Sph_coord(i,1)); // phi theta
            sph_h_in.Get(Ymn);
            
            // std::cout << "Ymn Size: " << Ymn.size() << std::endl;
            // std::cout << "Sh_matrix Size: " << Sh_matrix.size() << std::endl;
            
            //Sh_matrix.row(i) = Ymn;
            Sh_matrix.block(i,0,1,in_ambi_channels) = Ymn.transpose();
            // std::cout << "Size: " << Sh_matrix.block(i,0,1,in_ambi_channels).size() << ": " << Sh_matrix.block(i,0,1,in_ambi_channels) << std::endl;
        }
        
        // Sh_matrix_inv.setZero();
        
        //Sh_matrix_inv = (Sh_matrix.transpose()*Sh_matrix).inverse()*Sh_matrix.transpose(); // not working for dynamic input order
        // if input order is different a better solving has to be used for the inverse:
        Sh_matrix_inv = (Sh_matrix.transpose()*Sh_matrix).colPivHouseholderQr().inverse()*Sh_matrix.transpose();
        
        // std::cout << "Size Sh_matrix: " << Sh_matrix.rows() << " x " << Sh_matrix.cols() << std::endl;
        
        // std::cout << "Size Inverse: " << Sh_matrix_inv.rows() << " x " << Sh_matrix_inv.cols() << std::endl;
        
        // std::cout << Sh_matrix_inv << std::endl;
    
        _initialized = true;
    }
    
    if ( (phi_param != _phi_param) ||
        (floor(phi_curve_param+0.5f) != floor(_phi_curve_param+0.5f)) ||
        (theta_param != _theta_param) ||
        (floor(theta_curve_param+0.5f) != floor(_theta_curve_param+0.5f)) ||
        (in_order != _in_order) ||
        (out_order != _out_order) ||
        (floor(preemp_param+0.5f) != floor(_preemp_param+0.5f))
        )
    {
        
        if (_out_order != out_order)
        {
            // init or reinit spherical harmonics
            sph_h_out.Init(out_order);
        }
        
        Eigen::MatrixXd Sh_matrix_mod;
        Sh_matrix_mod.setZero(Sph_coord.rows(),AMBI_CHANNELS);
        
        Eigen::VectorXd pre_emphasis;
        pre_emphasis.setOnes(Sph_coord.rows());
        
        Eigen::MatrixXd Sph_coord_mod = Sph_coord;
        
        // warping parameters - use inverse parameters! (see paper)
        double phi_alpha = -((double)phi_param*1.8f - 0.9f);
        double theta_alpha = -((double)theta_param*1.8f - 0.9f);
        
        int out_ambi_channels = (out_order+1)*(out_order+1);
        
        for (int i=0; i < Sph_coord_mod.rows(); i++)
        {
            if (theta_alpha != 0.)
            {
                // warp elevation
                // as we are using elevation from -pi/2....pi/2
                double mu = sin(Sph_coord(i,1));
                
                if (theta_curve_param <= 0.5f)
                {
                    // warp towards a pole
                    
                    Sph_coord_mod(i,1) = asin((mu+theta_alpha)/(1+theta_alpha*mu));
                    
                    // pre emphasis
                    pre_emphasis(i) = (1+theta_alpha*mu)/sqrt(1-theta_alpha*theta_alpha);
                } else {
                    //warp towards equator (< 0.)or away from equator (> 0.)
                    // in the paper this parameter is called beta
                    
                    // pre emphasis
                    pre_emphasis(i) = sqrt((1-abs(theta_alpha))*(1+abs(theta_alpha)*mu*mu))/(1-abs(theta_alpha)*mu*mu);
                  
                    if (theta_alpha > 0.) {
                        
                        Sph_coord_mod(i,1) = asin((theta_alpha-1+sqrt(pow(theta_alpha-1,2)+4*theta_alpha*pow(mu,2)))/(2*theta_alpha*mu));
                        
                    } else {
                        Sph_coord_mod(i,1) = asin(((1+theta_alpha)*mu)/(1+theta_alpha*pow(mu,2)));
                        pre_emphasis(i) = 1/pre_emphasis(i); // this is ^sgn(beta)
                    }
                    
                }
            }
            /*
             // this is not fully working yet
            if (phi_alpha != 0.)
            {
                // warp azimuth
                // as we are using azimuth from -pi....pi
                double phi_temp = Sph_coord(i,0) * 0.5f;
                
                double mu = sin(phi_temp);
                
                if (phi_curve_param <= 0.5f)
                {
                    // warp towards a pole
                    
                    Sph_coord_mod(i,0) = asin((mu+phi_alpha)/(1+phi_alpha*mu));
                    
                    // pre emphasis
                    pre_emphasis(i) *= (1+phi_alpha*mu)/sqrt(1-phi_alpha*phi_alpha);
                } else {
                    //warp towards equator (< 0.)or away from equator (> 0.)
                    // in the paper this parameter is called beta
                    
                    // pre emphasis
                    double pre_emph_temp = sqrt((1-abs(phi_alpha))*(1+abs(phi_alpha)*mu*mu))/(1-abs(phi_alpha)*mu*mu);
                    
                    if (phi_alpha > 0.) {
                        
                        Sph_coord_mod(i,0) = asin((phi_alpha-1+sqrt(pow(phi_alpha-1,2)+4*phi_alpha*pow(mu,2)))/(2*phi_alpha*mu));
                        pre_emphasis(i) *= pre_emph_temp;
                        
                    } else {
                        Sph_coord_mod(i,0) = asin(((1+phi_alpha)*mu)/(1+phi_alpha*pow(mu,2)));
                        pre_emphasis(i) *= 1/pre_emph_temp; // this is ^sgn(beta)
                    }
                    
                }
                Sph_coord_mod(i,0) *= 2;
            }
            */
            
            if (phi_alpha != 0.f)
            {
                // warp azimuth
                // as we are using azimuth from -pi....pi
                double phi_temp = Sph_coord(i,0) * 0.5f;
                
                if (phi_curve_param <= 0.5f)
                {
                    // warp towards poles
                    Sph_coord_mod(i,0) = asin((sin(phi_temp)+phi_alpha)/(1.f+phi_alpha*sin(phi_temp)));
                } else {
                    //warp towards equator
                    
                    if (phi_temp > 0)
                    {
                        Sph_coord_mod(i,0) = acos((cos(phi_temp*2)+phi_alpha)/(1+phi_alpha*cos(phi_temp*2))) / 2;
                    } else {
                        Sph_coord_mod(i,0) = -acos((cos(M_PI-(phi_temp-M_PI/2)*2)+phi_alpha)/(1+phi_alpha*cos(M_PI-(phi_temp-M_PI/2)*2))) / 2;
                    }
                }
                
                Sph_coord_mod(i,0) *= 2;
                
            }
            
            // rotate
            // Sph_mod(i,0) = Sph(i,0);
            
            Eigen::VectorXd Ymn(out_ambi_channels); // Ymn result
            
            sph_h_out.Calc(Sph_coord_mod(i,0),Sph_coord_mod(i,1)); // phi theta
            sph_h_out.Get(Ymn);
            
            // Sh_matrix_mod.row(i) = Ymn;
            Sh_matrix_mod.block(i,0,1,out_ambi_channels) = Ymn.transpose();
        }
        
        // apply preemphasis if wanted
        if (preemp_param > 0.5f)
        {
            Sh_matrix_mod = Sh_matrix_mod * pre_emphasis.asDiagonal();
        }
        
        // calculate new transformation matrix
        
        Sh_transf = Sh_matrix_inv * Sh_matrix_mod;
        
        // threshold coefficients
        for (int i = 0; i < Sh_transf.size(); i++)
        {
            if (abs(Sh_transf(i)) < 0.00001f)
                Sh_transf(i) = 0.f;
        }
        
        _phi_param = phi_param;
        _phi_curve_param = phi_curve_param;
        _theta_param = theta_param;
        _theta_curve_param = theta_curve_param;
        _in_order = in_order;
        _out_order = out_order;
        _preemp_param = preemp_param;
    }
    
    
    
}
Example #4
0
void Ambix_rotatorAudioProcessor::calcParams()
{
// use old sampling method for generating rotation matrix...
#if 0
    if (!_initialized)
    {
    
        sph_h.Init(AMBI_ORDER);        
        
        const String t_design_txt (t_design::des_3_240_21_txt);
        
        // std::cout << t_design_txt << std::endl;
        
        String::CharPointerType lineChar = t_design_txt.getCharPointer();
        
        int n = 0; // how many characters been read
        int numsamples = 0;
        int i = 0;
        
        int curr_n = 0;
        int max_n = lineChar.length();
        
        while (curr_n < max_n) { // check how many coordinates we have
            double value;
            sscanf(lineChar, "%lf\n%n", &value, &n);
            lineChar += n;            
            curr_n += n;
            numsamples++;            
        } // end parse numbers
        
        numsamples = numsamples/3; // xyz
        
        Carth_coord.resize(numsamples,3); // positions in cartesian coordinates
        
        curr_n = 0;
        lineChar = t_design_txt.getCharPointer();
        
        // parse line for numbers again and copy to carth coordinate matrix
        while (i < numsamples) {
            
            double x,y,z;
            
            sscanf(lineChar, "%lf%lf%lf%n", &x, &y, &z, &n);
            
            Carth_coord(i,0) = x;
            Carth_coord(i,1) = y;
            Carth_coord(i,2) = z;
            
            lineChar += n;
            
            curr_n += n;
            i++;
            
        } // end parse numbers
        
        Sph_coord.resize(numsamples,2); // positions in spherical coordinates
        
        Eigen::MatrixXd Sh_matrix(numsamples,AMBI_CHANNELS);
        
        for (int i=0; i < numsamples; i++)
        {
            Eigen::VectorXd Ymn(AMBI_CHANNELS); // Ymn result
            Sph_coord(i,0) = atan2(Carth_coord(i,1),Carth_coord(i,0)); // azimuth
            Sph_coord(i,1) = atan2(Carth_coord(i,2),sqrt(Carth_coord(i,0)*Carth_coord(i,0) + Carth_coord(i,1)*Carth_coord(i,1))); // elevation
            
            sph_h.Calc(Sph_coord(i,0),Sph_coord(i,1)); // phi theta
            sph_h.Get(Ymn);
            
            Sh_matrix.row(i) = Ymn;
            
        }
      
      // inversion would not be necessary because of t-design -> transpose is enough..
      
        Sh_matrix_inv = (Sh_matrix.transpose()*Sh_matrix).inverse()*Sh_matrix.transpose();
        
        _initialized = true;
    }
    
    Eigen::MatrixXd Sh_matrix_mod(Sph_coord.rows(),AMBI_CHANNELS);
    
    // rotation parameters in radiants
    // use mathematical negative angles for yaw
    
    double yaw = -((double)yaw_param*2*M_PI - M_PI); // z
    double pitch = (double)pitch_param*2*M_PI - M_PI; // y
    double roll = (double)roll_param*2*M_PI - M_PI; // x

    Eigen::Matrix3d RotX, RotY, RotZ, Rot;
    
    RotX = RotY = RotZ = Eigen::Matrix3d::Zero(3,3);
    
    RotX(0,0) = 1.f;
    RotX(1,1) = RotX(2,2) = cos(roll);
    RotX(1,2) = -sin(roll);
    RotX(2,1) = -RotX(1,2);
    
    RotY(0,0) = RotY(2,2) = cos(pitch);
    RotY(0,2) = sin(pitch);
    RotY(2,0) = -RotY(0,2);
    RotY(1,1) = 1.f;
    
    RotZ(0,0) = RotZ(1,1) = cos(yaw);
    RotZ(0,1) = -sin(yaw);
    RotZ(1,0) = -RotZ(0,1);
    RotZ(2,2) = 1.f;
    
    // multiply individual rotation matrices
    if (rot_order_param < 0.5f)
    {
        // ypr order zyx -> mutliply inverse!
        Rot = RotX * RotY * RotZ;
    } else {
        // rpy order xyz -> mutliply inverse!
        Rot = RotZ * RotY * RotX;
    }
    
    // combined roll-pitch-yaw rotation matrix would be here
    // http://planning.cs.uiuc.edu/node102.html

    for (int i=0; i < Carth_coord.rows(); i++)
    {
        // rotate carthesian coordinates
        Eigen::Vector3d Carth_coord_mod = Carth_coord.row(i)*Rot;
        
        Eigen::Vector2d Sph_coord_mod;
        
        // convert to spherical coordinates
        Sph_coord_mod(0) = atan2(Carth_coord_mod(1),Carth_coord_mod(0)); // azimuth
        Sph_coord_mod(1) = atan2(Carth_coord_mod(2),sqrt(Carth_coord_mod(0)*Carth_coord_mod(0) + Carth_coord_mod(1)*Carth_coord_mod(1))); // elevation
        
        Eigen::VectorXd Ymn(AMBI_CHANNELS); // Ymn result
        
        // calc spherical harmonic
        sph_h.Calc(Sph_coord_mod(0),Sph_coord_mod(1)); // phi theta
        sph_h.Get(Ymn);
        
        // save to sh matrix
        Sh_matrix_mod.row(i) = Ymn;
    }
    
    // calculate new transformation matrix
    
    Sh_transf = Sh_matrix_inv * Sh_matrix_mod;
    
#else
  // use
  // Ivanic, J., Ruedenberg, K. (1996). Rotation Matrices for Real
  // Spherical Harmonics. Direct Determination by Recursion.
  // The Journal of Physical Chemistry
  
  // rotation parameters in radiants
  // use mathematical negative angles for yaw
  
  double yaw = -((double)yaw_param*2*M_PI - M_PI); // z
  double pitch = (double)pitch_param*2*M_PI - M_PI; // y
  double roll = (double)roll_param*2*M_PI - M_PI; // x
  
  Eigen::Matrix3d RotX, RotY, RotZ, Rot;
  
  RotX = RotY = RotZ = Eigen::Matrix3d::Zero(3,3);
  
  RotX(0,0) = 1.f;
  RotX(1,1) = RotX(2,2) = cos(roll);
  RotX(1,2) = sin(roll);
  RotX(2,1) = -RotX(1,2);
  
  RotY(0,0) = RotY(2,2) = cos(pitch);
  RotY(0,2) = sin(pitch);
  RotY(2,0) = -RotY(0,2);
  RotY(1,1) = 1.f;
  
  RotZ(0,0) = RotZ(1,1) = cos(yaw);
  RotZ(0,1) = sin(yaw);
  RotZ(1,0) = -RotZ(0,1);
  RotZ(2,2) = 1.f;
  
  // multiply individual rotation matrices
  if (rot_order_param < 0.5f)
  {
    // ypr order zyx -> mutliply inverse!
    Rot = RotX * RotY * RotZ;
  } else {
    // rpy order xyz -> mutliply inverse!
    Rot = RotZ * RotY * RotX;
  }
  
  // first order initialization - prototype matrix
  Eigen::Matrix3d R_1;
  R_1(0,0) = Rot(1,1);
  R_1(0,1) = Rot(1,2);
  R_1(0,2) = Rot(1,0);
  R_1(1,0) = Rot(2,1);
  R_1(1,1) = Rot(2,2);
  R_1(1,2) = Rot(2,0);
  R_1(2,0) = Rot(0,1);
  R_1(2,1) = Rot(0,2);
  R_1(2,2) = Rot(0,0);
  // zeroth order is invariant
  Sh_transf(0,0) = 1.;
  // set first order
  Sh_transf.block(1, 1, 3, 3) = R_1;
  
  Eigen::MatrixXd R_lm1 = R_1;
  
  // recursivly generate higher orders
  for (int l=2; l<=AMBI_ORDER; l++)
  {
    Eigen::MatrixXd R_l = Eigen::MatrixXd::Zero(2*l+1, 2*l+1);
    for (int m=-l;m <= l; m++)
    {
      for (int n=-l;n <= l; n++)
      {
        // Table I
        int d = (m==0) ? 1 : 0;
        double denom = 0.;
        if (abs(n) == l)
          denom = (2*l)*(2*l-1);
        else
          denom = (l*l-n*n);
        
        double u = sqrt((l*l-m*m)/denom);
        double v = sqrt((1.+d)*(l+abs(m)-1.)*(l+abs(m))/denom)*(1.-2.*d)*0.5;
        double w = sqrt((l-abs(m)-1.)*(l-abs(m))/denom)*(1.-d)*(-0.5);
        
        if (u != 0.)
          u *= U(l,m,n,R_1,R_lm1);
        if (v != 0.)
          v *= V(l,m,n,R_1,R_lm1);
        if (w != 0.)
          w *= W(l,m,n,R_1,R_lm1);
        
        R_l(m+l,n+l) = u + v + w;
      }
    }
    Sh_transf.block(l*l, l*l, 2*l+1, 2*l+1) = R_l;
    R_lm1 = R_l;
  }
  
#endif
  
    // threshold coefficients
    // maybe not needed here...
    for (int i = 0; i < Sh_transf.size(); i++)
    {
        if (abs(Sh_transf(i)) < 0.00001f)
            Sh_transf(i) = 0.f;
    }
  
}