From 80127f9ccade3292a22377173d44a72619497950 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 22:59:25 +0100 Subject: [PATCH 01/31] VBAP backend re-work (part one): * fix azimuth, don't clamp but map to [0,1] * prepare elevation (10+ speakers) --- libs/panners/vbap/vbap.cc | 43 +++++++++++++++++++++++++-------------- libs/panners/vbap/vbap.h | 1 + 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc index 2241ab96e3..0b01c2d9d3 100644 --- a/libs/panners/vbap/vbap.cc +++ b/libs/panners/vbap/vbap.cc @@ -75,6 +75,7 @@ VBAPanner::VBAPanner (boost::shared_ptr p, boost::shared_ptr , _speakers (new VBAPSpeakers (s)) { _pannable->pan_azimuth_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); + _pannable->pan_elevation_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); _pannable->pan_width_control->Changed.connect_same_thread (*this, boost::bind (&VBAPanner::update, this)); update (); @@ -119,6 +120,7 @@ VBAPanner::update () /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees */ double center = _pannable->pan_azimuth_control->get_value() * 360.0; + double elevation = _pannable->pan_elevation_control->get_value() * 90.0; if (_signals.size() > 1) { @@ -156,8 +158,8 @@ VBAPanner::update () for (vector::iterator s = _signals.begin(); s != _signals.end(); ++s) { Signal* signal = *s; - - signal->direction = AngularVector (signal_direction, 0.0); + + signal->direction = AngularVector (signal_direction, elevation); compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); signal_direction += degree_step_per_signal; } @@ -168,8 +170,8 @@ VBAPanner::update () for (vector::reverse_iterator s = _signals.rbegin(); s != _signals.rend(); ++s) { Signal* signal = *s; - - signal->direction = AngularVector (signal_direction, 0.0); + + signal->direction = AngularVector (signal_direction, elevation); compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); signal_direction += degree_step_per_signal; } @@ -180,7 +182,7 @@ VBAPanner::update () /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */ Signal* s = _signals.front(); - s->direction = AngularVector (center, 0); + s->direction = AngularVector (center, elevation); compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele); } } @@ -428,6 +430,9 @@ VBAPanner::what_can_be_automated() const if (_signals.size() > 1) { s.insert (Evoral::Parameter (PanWidthAutomation)); } + if (_speakers->dimension() == 3) { + s.insert (Evoral::Parameter (PanElevationAutomation)); + } return s; } @@ -439,6 +444,8 @@ VBAPanner::describe_parameter (Evoral::Parameter p) return _("Direction"); case PanWidthAutomation: return _("Diffusion"); + case PanElevationAutomation: + return _("Elevation"); default: return _pannable->describe_parameter (p); } @@ -452,10 +459,13 @@ VBAPanner::value_as_string (boost::shared_ptr ac) const switch (ac->parameter().type()) { case PanAzimuthAutomation: /* direction */ - return string_compose (_("%1"), int (rint (val * 360.0))); + return string_compose (_("%1\u00B0"), int (rint (val * 360.0))); case PanWidthAutomation: /* diffusion */ return string_compose (_("%1%%"), (int) floor (100.0 * fabs(val))); + + case PanElevationAutomation: /* elevation */ + return string_compose (_("%1\u00B0"), (int) floor (90.0 * fabs(val))); default: return _pannable->value_as_string (ac); @@ -481,15 +491,11 @@ VBAPanner::get_speakers () const void VBAPanner::set_position (double p) { - if (p < 0.0) { - p = 1.0 + p; - } - - if (p > 1.0) { - p = fmod (p, 1.0); - } - - _pannable->pan_azimuth_control->set_value (p); + /* map into 0..1 range */ + int over = p; + over -= (p >= 0) ? 0 : 1; + p -= (double)over; + _pannable->pan_azimuth_control->set_value (p); } void @@ -498,11 +504,18 @@ VBAPanner::set_width (double w) _pannable->pan_width_control->set_value (min (1.0, max (-1.0, w))); } +void +VBAPanner::set_elevation (double e) +{ + _pannable->pan_elevation_control->set_value (min (1.0, max (0.0, e))); +} + void VBAPanner::reset () { set_position (0); set_width (1); + set_elevation (0); update (); } diff --git a/libs/panners/vbap/vbap.h b/libs/panners/vbap/vbap.h index e90bbc6fb1..ee9d456627 100644 --- a/libs/panners/vbap/vbap.h +++ b/libs/panners/vbap/vbap.h @@ -46,6 +46,7 @@ public: void set_position (double); void set_width (double); + void set_elevation (double); std::set what_can_be_automated() const; From d3e1d542803922250937be16b598f2c052948cba Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:29:23 +0100 Subject: [PATCH 02/31] VBAP backend re-work (part two): speaker positioning * clean up source (whitespace) * fix speaker 3x3 matrix iteration * update math to go along with Ardour Cartesian -- fixes rounding errors * fix division by zero in cross_prod() * disable old debug output (NB PBD::spherical_to_cartesian() returns 3.7494e-33, 6.12323e-17, 1 for azimuth 90 elevation 90 distance 1 while it should return 0.000000, 0.000000, 1 for azimuth 90 elevation 90 distance 1 IOW cos(90.0 * 2.0 * M_PI / 360.0) != 0 Cause unknown. This is currently worked around check in vec_length() ) --- libs/panners/vbap/vbap_speakers.cc | 195 ++++++++++++++++------------- libs/panners/vbap/vbap_speakers.h | 10 +- 2 files changed, 113 insertions(+), 92 deletions(-) diff --git a/libs/panners/vbap/vbap_speakers.cc b/libs/panners/vbap/vbap_speakers.cc index b84698bbd9..313fe7a5cd 100644 --- a/libs/panners/vbap/vbap_speakers.cc +++ b/libs/panners/vbap/vbap_speakers.cc @@ -1,4 +1,4 @@ -/* +/* This software is being provided to you, the licensee, by Ville Pulkki, under the following license. By obtaining, using and/or copying this software, you agree that you have read, understood, and will comply @@ -10,15 +10,15 @@ the disclaimer, and that the same appear on ALL copies of the software and documentation, including modifications that you make for internal use or for distribution: - + Copyright 1998 by Ville Pulkki, Helsinki University of Technology. All - rights reserved. - + rights reserved. + The software may be used, distributed, and included to commercial products without any charges. When included to a commercial product, the method "Vector Base Amplitude Panning" and its developer Ville Pulkki must be referred to in documentation. - + This software is provided "as is", and Ville Pulkki or Helsinki University of Technology make no representations or warranties, expressed or implied. By way of example, but not limitation, Helsinki @@ -90,18 +90,29 @@ VBAPSpeakers::update () } } -void -VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) +void +VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) { /* Selects the loudspeaker triplets, and calculates the inversion matrices for each selected triplet. A line (connection) is drawn between each loudspeaker. The lines - denote the sides of the triangles. The triangles should not be - intersecting. All crossing connections are searched and the + denote the sides of the triangles. The triangles should not be + intersecting. All crossing connections are searched and the longer connection is erased. This yields non-intesecting triangles, which can be used in panning. */ +#if 0 // DEVEL/DEBUG + for (vector::iterator i = _speakers.begin(); i != _speakers.end(); ++i) { + cout << "Speaker " << (*i).id << " @ " + << (*i).coords().x << ", " << (*i).coords().y << ", " << (*i).coords().z + << " azimuth " << (*i).angles().azi + << " elevation " << (*i).angles().ele + << " distance " << (*i).angles().length + << endl; + } +#endif + int i,j,k,l,table_size; int n_speakers = _speakers.size (); int connections[n_speakers][n_speakers]; @@ -117,8 +128,8 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) for (i = 0; i < n_speakers; i++) { for (j = i+1; j < n_speakers; j++) { - for(k=j+1;k MIN_VOL_P_SIDE_LGTH){ + for(k = j+1; k < n_speakers; k++) { + if (vol_p_side_lgth(i, j, k, _speakers) > MIN_VOL_P_SIDE_LGTH) { connections[i][j]=1; connections[j][i]=1; connections[i][k]=1; @@ -132,13 +143,13 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) } /*calculate distancies between all speakers and sorting them*/ - table_size =(((n_speakers - 1) * (n_speakers)) / 2); + table_size =(((n_speakers - 1) * (n_speakers)) / 2); for (i = 0; i < table_size; i++) { distance_table[i] = 100000.0; } - for (i = 0;i < n_speakers; i++) { - for (j = i+1; j < n_speakers; j++) { + for (i = 0;i < n_speakers; i++) { + for (j = i+1; j < n_speakers; j++) { if (connections[i][j] == 1) { distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords())); k=0; @@ -167,8 +178,8 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) if (connections[fst_ls][sec_ls] == 1) { for (j = 0; j < n_speakers; j++) { for (k = j+1; k < n_speakers; k++) { - if ((j!=fst_ls) && (k != sec_ls) && (k!=fst_ls) && (j != sec_ls)){ - if (lines_intersect(fst_ls, sec_ls, j,k) == 1){ + if ((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) { + if (lines_intersect(fst_ls, sec_ls, j, k) == 1){ connections[j][k] = 0; connections[k][j] = 0; } @@ -186,8 +197,8 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) i = trip_ptr->ls_nos[0]; j = trip_ptr->ls_nos[1]; k = trip_ptr->ls_nos[2]; - if (connections[i][j] == 0 || - connections[i][k] == 0 || + if (connections[i][j] == 0 || + connections[i][k] == 0 || connections[j][k] == 0 || any_ls_inside_triplet(i,j,k) == 1 ){ if (prev != 0) { @@ -209,7 +220,7 @@ VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets) } } -int +int VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c) { /* returns 1 if there is loudspeaker(s) inside given ls triplet */ @@ -227,12 +238,12 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c) lp1 = &(_speakers[a].coords()); lp2 = &(_speakers[b].coords()); lp3 = &(_speakers[c].coords()); - + /* matrix inversion */ invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y)) - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x)) + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x))); - + invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet; invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet; invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet; @@ -242,7 +253,7 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c) invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet; invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet; invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet; - + any_ls_inside = false; for (i = 0; i < n_speakers; i++) { if (i != a && i!=b && i != c) { @@ -265,7 +276,7 @@ VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c) } -void +void VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets) { /* adds i,j,k triplet to triplet chain*/ @@ -273,7 +284,7 @@ VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls struct ls_triplet_chain *trip_ptr, *prev; trip_ptr = *ls_triplets; prev = 0; - + while (trip_ptr != 0){ prev = trip_ptr; trip_ptr = trip_ptr->next; @@ -293,51 +304,51 @@ VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls trip_ptr->ls_nos[2] = k; } -float +double VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2) { - float inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/ + double inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/ (vec_length(v1) * vec_length(v2))); if (inner > 1.0) { - inner= 1.0; + inner = 1.0; } if (inner < -1.0) { inner = -1.0; } - return fabsf((float) acos((double) inner)); + return fabs(acos(inner)); } -float +double VBAPSpeakers::vec_length(CartesianVector v1) { - return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z)); + double rv = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z); + if (rv > 1e-14) return rv; + return 0; } -float +double VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2) { return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); } -float -VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector& speakers) +double +VBAPSpeakers::vol_p_side_lgth(int i, int j, int k, const vector& speakers) { /* calculate volume of the parallelepiped defined by the loudspeaker - direction vectors and divide it with total length of the triangle sides. + direction vectors and divide it with total length of the triangle sides. This is used when removing too narrow triangles. */ - - float volper, lgth; + + double volper, lgth; CartesianVector xprod; - cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod); - volper = fabsf (vec_prod(xprod, speakers[k].coords())); - lgth = (fabsf (vec_angle(speakers[i].coords(), speakers[j].coords())) - + fabsf (vec_angle(speakers[i].coords(), speakers[k].coords())) - + fabsf (vec_angle(speakers[j].coords(), speakers[k].coords()))); - + volper = fabs (vec_prod(xprod, speakers[k].coords())); + lgth = ( fabs (vec_angle(speakers[i].coords(), speakers[j].coords())) + + fabs (vec_angle(speakers[i].coords(), speakers[k].coords())) + + fabs (vec_angle(speakers[j].coords(), speakers[k].coords()))); if (lgth > 0.00001) { return volper / lgth; } else { @@ -345,28 +356,34 @@ VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector& speaker } } -void -VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res) +void +VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res) { - float length; + double length; + + res->x = (v1.y * v2.z) - (v1.z * v2.y); + res->y = (v1.z * v2.x) - (v1.x * v2.z); + res->z = (v1.x * v2.y) - (v1.y * v2.x); - res->x = (v1.y * v2.z ) - (v1.z * v2.y); - res->y = (v1.z * v2.x ) - (v1.x * v2.z); - res->z = (v1.x * v2.y ) - (v1.y * v2.x); - length = vec_length(*res); - res->x /= length; - res->y /= length; - res->z /= length; + if (length > 0) { + res->x /= length; + res->y /= length; + res->z /= length; + } else { + res->x = 0; + res->y = 0; + res->z = 0; + } } -int +int VBAPSpeakers::lines_intersect (int i, int j, int k, int l) { - /* checks if two lines intersect on 3D sphere + /* checks if two lines intersect on 3D sphere see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays with Multiple Loudspeakers Using VBAP: A Case Study with - DIVA Project" in International Conference on + DIVA Project" in International Conference on Auditory Displays -98. E-mail Ville.Pulkki@hut.fi if you want to have that paper. */ @@ -376,11 +393,11 @@ VBAPSpeakers::lines_intersect (int i, int j, int k, int l) CartesianVector v3, neg_v3; float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3; float dist_kv3,dist_lv3,dist_knv3,dist_lnv3; - + cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1); cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2); cross_prod(v1,v2,&v3); - + neg_v3.x= 0.0 - v3.x; neg_v3.y= 0.0 - v3.y; neg_v3.z= 0.0 - v3.z; @@ -397,15 +414,14 @@ VBAPSpeakers::lines_intersect (int i, int j, int k, int l) dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords())); /* if one of loudspeakers is close to crossing point, don't do anything*/ - - - if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 || + if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 || fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 || - fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 || + fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 || fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) { return(0); } + /* if crossing point is on line between both loudspeakers return 1 */ if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) && (fabsf(dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) || ((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) && @@ -416,9 +432,9 @@ VBAPSpeakers::lines_intersect (int i, int j, int k, int l) } } -void +void VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) -{ +{ /* Calculates the inverse matrices for 3D */ float invdet; const CartesianVector* lp1; @@ -430,7 +446,7 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) int triplet; assert (tr_ptr); - + /* counting triplet amount */ while (tr_ptr != 0) { @@ -438,7 +454,9 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) tr_ptr = tr_ptr->next; } - cerr << "@@@ triplets generate " << triplet_count << " of speaker tuples\n"; +#if 0 // DEVEL/DEBUG + cerr << "@@@ VBAP triplets generate " << triplet_count << " of speaker tuples\n"; +#endif triplet = 0; @@ -450,17 +468,18 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) _speaker_tuples.push_back (tmatrix()); } + tr_ptr = ls_triplets; while (tr_ptr != 0) { lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords()); lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords()); lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords()); - + /* matrix inversion */ invmx = tr_ptr->inv_mx; invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y)) - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x)) + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x))); - + invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet; invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet; invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet; @@ -470,7 +489,7 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet; invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet; invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet; - + /* copy the matrix */ _matrices[triplet][0] = invmx[0]; @@ -487,10 +506,12 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1]; _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2]; - cerr << "Triplet[" << triplet << "] = " - << tr_ptr->ls_nos[0] << " + " - << tr_ptr->ls_nos[1] << " + " +#if 0 // DEVEL/DEBUG + cerr << "Triplet[" << triplet << "] = " + << tr_ptr->ls_nos[0] << " + " + << tr_ptr->ls_nos[1] << " + " << tr_ptr->ls_nos[2] << endl; +#endif triplet++; @@ -498,7 +519,7 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) } } -void +void VBAPSpeakers::choose_speaker_pairs (){ /* selects the loudspeaker pairs, calculates the inversion @@ -508,7 +529,7 @@ VBAPSpeakers::choose_speaker_pairs (){ const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175); int sorted_speakers[n_speakers]; bool exists[n_speakers]; - double inverse_matrix[n_speakers][4]; + double inverse_matrix[n_speakers][4]; int expected_pairs = 0; int pair; int speaker; @@ -524,29 +545,29 @@ VBAPSpeakers::choose_speaker_pairs (){ /* sort loudspeakers according their aximuth angle */ sort_2D_lss (sorted_speakers); - + /* adjacent loudspeakers are the loudspeaker pairs to be used.*/ for (speaker = 0; speaker < n_speakers-1; speaker++) { - if ((_speakers[sorted_speakers[speaker+1]].angles().azi - + if ((_speakers[sorted_speakers[speaker+1]].angles().azi - _speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) { - if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi, - _speakers[sorted_speakers[speaker+1]].angles().azi, + if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi, + _speakers[sorted_speakers[speaker+1]].angles().azi, inverse_matrix[speaker]) != 0){ exists[speaker] = true; expected_pairs++; } } } - - if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi) + + if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi) +_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) { - if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi, - _speakers[sorted_speakers[0]].angles().azi, - inverse_matrix[n_speakers-1]) != 0) { + if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi, + _speakers[sorted_speakers[0]].angles().azi, + inverse_matrix[n_speakers-1]) != 0) { exists[n_speakers-1] = true; expected_pairs++; - } + } } pair = 0; @@ -572,7 +593,7 @@ VBAPSpeakers::choose_speaker_pairs (){ pair++; } } - + if (exists[n_speakers-1]) { _matrices[pair][0] = inverse_matrix[speaker][0]; _matrices[pair][1] = inverse_matrix[speaker][1]; @@ -584,7 +605,7 @@ VBAPSpeakers::choose_speaker_pairs (){ } } -void +void VBAPSpeakers::sort_2D_lss (int* sorted_speakers) { vector tmp = _speakers; @@ -599,7 +620,7 @@ VBAPSpeakers::sort_2D_lss (int* sorted_speakers) } } -int +int VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix) { double x1,x2,x3,x4; @@ -612,7 +633,7 @@ VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_mat det = (x1 * x4) - ( x3 * x2 ); if (fabs(det) <= 0.001) { - + inverse_matrix[0] = 0.0; inverse_matrix[1] = 0.0; inverse_matrix[2] = 0.0; diff --git a/libs/panners/vbap/vbap_speakers.h b/libs/panners/vbap/vbap_speakers.h index b2f8b3c9dd..c3e90ce646 100644 --- a/libs/panners/vbap/vbap_speakers.h +++ b/libs/panners/vbap/vbap_speakers.h @@ -84,11 +84,11 @@ private: struct ls_triplet_chain *next; }; - static float vec_angle(PBD::CartesianVector v1, PBD::CartesianVector v2); - static float vec_length(PBD::CartesianVector v1); - static float vec_prod(PBD::CartesianVector v1, PBD::CartesianVector v2); - static float vol_p_side_lgth(int i, int j,int k, const std::vector&); - static void cross_prod(PBD::CartesianVector v1,PBD::CartesianVector v2, PBD::CartesianVector *res); + static double vec_angle(PBD::CartesianVector v1, PBD::CartesianVector v2); + static double vec_length(PBD::CartesianVector v1); + static double vec_prod(PBD::CartesianVector v1, PBD::CartesianVector v2); + static double vol_p_side_lgth(int i, int j,int k, const std::vector&); + static void cross_prod(PBD::CartesianVector v1,PBD::CartesianVector v2, PBD::CartesianVector *res); void update (); int any_ls_inside_triplet (int a, int b, int c); From 0669bb455b72bb5333534941787a889d828ea443 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:29:36 +0100 Subject: [PATCH 03/31] VBAP GUI depends on signal-position (not parameter changes) --- gtk2_ardour/panner2d.cc | 5 +++-- gtk2_ardour/panner2d.h | 1 + libs/ardour/ardour/panner.h | 3 +++ libs/panners/vbap/vbap.cc | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 64d83ab162..b11e05e0d4 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -77,8 +77,7 @@ Panner2d::Panner2d (boost::shared_ptr p, int32_t h) { panner_shell->Changed.connect (connections, invalidator (*this), boost::bind (&Panner2d::handle_state_change, this), gui_context()); - panner_shell->pannable()->pan_azimuth_control->Changed.connect (connections, invalidator(*this), boost::bind (&Panner2d::handle_position_change, this), gui_context()); - panner_shell->pannable()->pan_width_control->Changed.connect (connections, invalidator(*this), boost::bind (&Panner2d::handle_position_change, this), gui_context()); + panner_shell->panner()->SignalPositionChanged.connect (panconnect, invalidator(*this), boost::bind (&Panner2d::handle_position_change, this), gui_context()); drag_target = 0; set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK); @@ -199,6 +198,8 @@ Panner2d::add_speaker (const AngularVector& a) void Panner2d::handle_state_change () { + panconnect.drop_connections(); + panner_shell->panner()->SignalPositionChanged.connect (panconnect, invalidator(*this), boost::bind (&Panner2d::handle_position_change, this), gui_context()); queue_draw (); } diff --git a/gtk2_ardour/panner2d.h b/gtk2_ardour/panner2d.h index 881638856f..474d7e1b10 100644 --- a/gtk2_ardour/panner2d.h +++ b/gtk2_ardour/panner2d.h @@ -136,6 +136,7 @@ class Panner2d : public Gtk::DrawingArea void label_signals (); PBD::ScopedConnectionList connections; + PBD::ScopedConnectionList panconnect; /* cartesian coordinates in GTK units ; adjust to same but on a circle of radius 1.0 and centered in the middle of our area diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h index 0c025b0ca2..18e3e8045a 100644 --- a/libs/ardour/ardour/panner.h +++ b/libs/ardour/ardour/panner.h @@ -87,6 +87,9 @@ public: virtual void reset () = 0; + /* azimut, width or elevation updated -> recalc signal_position -> emit Changed */ + PBD::Signal0 SignalPositionChanged; + void set_automation_state (AutoState); AutoState automation_state() const; void set_automation_style (AutoStyle); diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc index 0b01c2d9d3..a56a5a720d 100644 --- a/libs/panners/vbap/vbap.cc +++ b/libs/panners/vbap/vbap.cc @@ -185,6 +185,8 @@ VBAPanner::update () s->direction = AngularVector (center, elevation); compute_gains (s->desired_gains, s->desired_outputs, s->direction.azi, s->direction.ele); } + + SignalPositionChanged(); /* emit */ } void From 7ac05ccfa562b4732070864360fab19da26854d7 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:29:46 +0100 Subject: [PATCH 04/31] update default speaker position list --- libs/ardour/speakers.cc | 65 +++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/libs/ardour/speakers.cc b/libs/ardour/speakers.cc index 51bae16585..2acc9659ef 100644 --- a/libs/ardour/speakers.cc +++ b/libs/ardour/speakers.cc @@ -149,32 +149,73 @@ Speakers::move_speaker (int id, const AngularVector& new_position) void Speakers::setup_default_speakers (uint32_t n) { + double o = 90.0; + /* default assignment of speaker position for n speakers */ assert (n>0); switch (n) { case 1: - add_speaker (AngularVector (0.0, 0.0)); + add_speaker (AngularVector (o +0.0, 0.0)); break; case 2: - add_speaker (AngularVector (0.0, 0.0)); - add_speaker (AngularVector (180.0, 0,0)); + add_speaker (AngularVector (o +60.0, 0.0)); + add_speaker (AngularVector (o -60.0, 0.0)); break; case 3: - /* top, bottom kind-of-left & bottom kind-of-right */ - add_speaker (AngularVector (90.0, 0.0)); - add_speaker (AngularVector (215.0, 0,0)); - add_speaker (AngularVector (335.0, 0,0)); + add_speaker (AngularVector (o +60.0, 0.0)); + add_speaker (AngularVector (o -60.0, 0.0)); + add_speaker (AngularVector (o +180.0, 0.0)); break; case 4: - /* clockwise from top left */ - add_speaker (AngularVector (135.0, 0.0)); - add_speaker (AngularVector (45.0, 0.0)); - add_speaker (AngularVector (335.0, 0.0)); - add_speaker (AngularVector (215.0, 0.0)); + /* 4.0 with regular spacing */ + add_speaker (AngularVector (o +45.0, 0.0)); + add_speaker (AngularVector (o -45.0, 0.0)); + add_speaker (AngularVector (o +135.0, 0.0)); + add_speaker (AngularVector (o -135.0, 0.0)); + break; + case 5: + /* 5.0 with regular spacing */ + add_speaker (AngularVector (o +72.0, 0.0)); + add_speaker (AngularVector (o -72.0, 0.0)); + add_speaker (AngularVector (o +0.0, 0.0)); + add_speaker (AngularVector (o +144.0, 0.0)); + add_speaker (AngularVector (o -144.0, 0.0)); + break; + case 6: + /* 6.0 with regular spacing */ + add_speaker (AngularVector (o +60.0, 0.0)); + add_speaker (AngularVector (o -60.0, 0.0)); + add_speaker (AngularVector (o +0.0, 0.0)); + add_speaker (AngularVector (o +120.0, 0.0)); + add_speaker (AngularVector (o -120.0, 0.0)); + add_speaker (AngularVector (o +180.0, 0.0)); + break; + case 7: + /* 7.0 with regular front spacing */ + add_speaker (AngularVector (o +45.0, 0.0)); + add_speaker (AngularVector (o -45.0, 0.0)); + add_speaker (AngularVector (o +0.0, 0.0)); + add_speaker (AngularVector (o +90.0, 0.0)); + add_speaker (AngularVector (o -90.0, 0.0)); + add_speaker (AngularVector (o +150.0, 0.0)); + add_speaker (AngularVector (o -150.0, 0.0)); + break; + case 10: + /* 5+4 with 45°/90° spacing */ + add_speaker (AngularVector (o +45.0, 0.0)); + add_speaker (AngularVector (o -45.0, 0.0)); + add_speaker (AngularVector (o +0.0, 0.0)); + add_speaker (AngularVector (o +135.0, 0.0)); + add_speaker (AngularVector (o -135.0, 0.0)); + add_speaker (AngularVector (o +45.0, 60.0)); + add_speaker (AngularVector (o -45.0, 60.0)); + add_speaker (AngularVector (o +135.0, 60.0)); + add_speaker (AngularVector (o -135.0, 60.0)); + add_speaker (AngularVector (o +0.0, 90.0)); break; default: From 72cec05d46e033d1e33d46ef69b85ef65f991bdd Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:30:01 +0100 Subject: [PATCH 05/31] VBAP rework (part III): fix position computation backend & GUI --- gtk2_ardour/panner2d.cc | 78 ++++++++++++++++++++++++-------------- gtk2_ardour/panner2d.h | 1 + libs/panners/vbap/vbap.cc | 79 ++++++++++----------------------------- 3 files changed, 71 insertions(+), 87 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index b11e05e0d4..59e70acd03 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -82,7 +82,7 @@ Panner2d::Panner2d (boost::shared_ptr p, int32_t h) drag_target = 0; set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK); - handle_position_change (); + handle_position_change (); } Panner2d::~Panner2d() @@ -206,7 +206,6 @@ Panner2d::handle_state_change () void Panner2d::label_signals () { - double w = panner_shell->pannable()->pan_width_control->get_value(); uint32_t sz = signals.size(); switch (sz) { @@ -218,23 +217,14 @@ Panner2d::label_signals () break; case 2: - if (w >= 0.0) { - signals[0]->set_text ("R"); - signals[1]->set_text ("L"); - } else { - signals[0]->set_text ("L"); - signals[1]->set_text ("R"); - } + signals[0]->set_text ("L"); + signals[1]->set_text ("R"); break; default: for (uint32_t i = 0; i < sz; ++i) { char buf[64]; - if (w >= 0.0) { - snprintf (buf, sizeof (buf), "%" PRIu32, i + 1); - } else { - snprintf (buf, sizeof (buf), "%" PRIu32, sz - i); - } + snprintf (buf, sizeof (buf), "%" PRIu32, i + 1); signals[i]->set_text (buf); } break; @@ -247,7 +237,8 @@ Panner2d::handle_position_change () uint32_t n; double w = panner_shell->pannable()->pan_width_control->get_value(); - position.position = AngularVector (panner_shell->pannable()->pan_azimuth_control->get_value() * 360.0, 0.0); + position.position = AngularVector (panner_shell->pannable()->pan_azimuth_control->get_value() * 360.0, + panner_shell->pannable()->pan_elevation_control->get_value() * 90.0); for (uint32_t i = 0; i < signals.size(); ++i) { signals[i]->position = panner_shell->panner()->signal_position (i); @@ -693,22 +684,39 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state) } if (need_move) { - CartesianVector cp (evx, evy, 0.0); - AngularVector av; - - /* canonicalize position and then clamp to the circle */ + set params = panner_shell->panner()->what_can_be_automated(); + set::iterator p = params.find(PanElevationAutomation); + double y0 = 4.0; + if (height > large_size_threshold) { + y0 = 12.0; + } + CartesianVector cp (evx - 12.0, evy - y0, 0.0); + AngularVector av; gtk_to_cart (cp); - clamp_to_circle (cp.x, cp.y); - /* generate an angular representation of the current mouse position */ + if (p == params.end()) { + clamp_to_circle (cp.x, cp.y); + cp.angular (av); + if (drag_target == &position) { + double degree_fract = av.azi / 360.0; + panner_shell->panner()->set_position (degree_fract); + } + } else { + /* sphere projection */ + sphere_project (cp.x, cp.y, cp.z); - cp.angular (av); + double r2d = 180.0 / M_PI; + av.azi = r2d * atan2(cp.y, cp.x); + av.ele = r2d * asin(cp.z); - if (drag_target == &position) { - double degree_fract = av.azi / 360.0; - panner_shell->panner()->set_position (degree_fract); - } + if (drag_target == &position) { + double azi_fract = av.azi / 360.0; + double ele_fract = av.ele / 90.0; + panner_shell->panner()->set_position (azi_fract); + panner_shell->panner()->set_elevation (ele_fract); + } + } } } @@ -761,13 +769,27 @@ Panner2d::gtk_to_cart (CartesianVector& c) const c.y = (((diameter - c.y) / diameter) * 2.0) - 1.0; } +void +Panner2d::sphere_project (double& x, double& y, double& z) +{ + double r, r2; + r2 = x * x + y * y; + if (r2 < 1.0) { + z = sqrt (1.0 - r2); + } else { + r = sqrt (r2); + x = x / r; + y = y / r; + z = 0.0; + } +} + void Panner2d::clamp_to_circle (double& x, double& y) { double azi, ele; double z = 0.0; - double l; - + double l; PBD::cartesian_to_spherical (x, y, z, azi, ele, l); PBD::spherical_to_cartesian (azi, ele, 1.0, x, y, z); } diff --git a/gtk2_ardour/panner2d.h b/gtk2_ardour/panner2d.h index 474d7e1b10..9df0355518 100644 --- a/gtk2_ardour/panner2d.h +++ b/gtk2_ardour/panner2d.h @@ -142,6 +142,7 @@ class Panner2d : public Gtk::DrawingArea and centered in the middle of our area */ void clamp_to_circle (double& x, double& y); + void sphere_project (double& x, double& y, double& z); }; class Panner2dWindow : public ArdourWindow diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc index a56a5a720d..d0202d4466 100644 --- a/libs/panners/vbap/vbap.cc +++ b/libs/panners/vbap/vbap.cc @@ -114,70 +114,27 @@ VBAPanner::configure_io (ChanCount in, ChanCount /* ignored - we use Speakers */ void VBAPanner::update () { - /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) parameters) - */ - - /* panner azimuth control is [0 .. 1.0] which we interpret as [0 .. 360] degrees - */ - double center = _pannable->pan_azimuth_control->get_value() * 360.0; + /* recompute signal directions based on panner azimuth and, if relevant, width (diffusion) and elevation parameters */ double elevation = _pannable->pan_elevation_control->get_value() * 90.0; if (_signals.size() > 1) { - - /* panner width control is [-1.0 .. 1.0]; we ignore sign, and map to [0 .. 360] degrees - so that a width of 1 corresponds to a signal equally present from all directions, - and a width of zero corresponds to a point source from the "center" (above) point - on the perimeter of the speaker array. - */ - - double w = fabs (_pannable->pan_width_control->get_value()) * 360.0; + double w = (_pannable->pan_width_control->get_value()); + double signal_direction = _pannable->pan_azimuth_control->get_value() - (w/2); + double grd_step_per_signal = w / (_signals.size() - 1); + for (vector::iterator s = _signals.begin(); s != _signals.end(); ++s) { - double min_dir = center - (w/2.0); - if (min_dir < 0) { - min_dir = 360.0 + min_dir; // its already negative + Signal* signal = *s; + + int over = signal_direction; + over -= (signal_direction >= 0) ? 0 : 1; + signal_direction -= (double)over; + + signal->direction = AngularVector (signal_direction * 360.0, elevation); + compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); + signal_direction += grd_step_per_signal; } - min_dir = max (min (min_dir, 360.0), 0.0); - - double max_dir = center + (w/2.0); - if (max_dir > 360.0) { - max_dir = max_dir - 360.0; - } - max_dir = max (min (max_dir, 360.0), 0.0); - - if (max_dir < min_dir) { - swap (max_dir, min_dir); - } - - double degree_step_per_signal = (max_dir - min_dir) / (_signals.size() - 1); - double signal_direction = min_dir; - - if (w >= 0.0) { - - /* positive width - normal order of signal spread */ - - for (vector::iterator s = _signals.begin(); s != _signals.end(); ++s) { - - Signal* signal = *s; - - signal->direction = AngularVector (signal_direction, elevation); - compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); - signal_direction += degree_step_per_signal; - } - } else { - - /* inverted width - reverse order of signal spread */ - - for (vector::reverse_iterator s = _signals.rbegin(); s != _signals.rend(); ++s) { - - Signal* signal = *s; - - signal->direction = AngularVector (signal_direction, elevation); - compute_gains (signal->desired_gains, signal->desired_outputs, signal->direction.azi, signal->direction.ele); - signal_direction += degree_step_per_signal; - } - } - } else if (_signals.size() == 1) { + double center = _pannable->pan_azimuth_control->get_value() * 360.0; /* width has no role to play if there is only 1 signal: VBAP does not do "diffusion" of a single channel */ @@ -516,7 +473,11 @@ void VBAPanner::reset () { set_position (0); - set_width (1); + if (_signals.size() > 1) { + set_width (1.0 - (1.0 / (double)_signals.size())); + } else { + set_width (0); + } set_elevation (0); update (); From 407eba04cf080166fd3adf319424300567e3e755 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:30:13 +0100 Subject: [PATCH 06/31] add spinbox to control width in VBAP GUI,.. --- gtk2_ardour/panner2d.cc | 70 +++++++++++++++++++++++++++++++---------- gtk2_ardour/panner2d.h | 11 ++++--- 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 59e70acd03..d1bf858173 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -804,6 +804,8 @@ Panner2dWindow::Panner2dWindow (boost::shared_ptr p, int32_t h, uin : ArdourWindow (_("Panner (2D)")) , widget (p, h) , bypass_button (_("Bypass")) + , width_adjustment (0, -100, 100, 1, 5, 0) + , width_spinner (width_adjustment) { widget.set_name ("MixerPanZone"); @@ -811,18 +813,29 @@ Panner2dWindow::Panner2dWindow (boost::shared_ptr p, int32_t h, uin widget.set_size_request (h, h); bypass_button.signal_toggled().connect (sigc::mem_fun (*this, &Panner2dWindow::bypass_toggled)); + width_spinner.signal_changed().connect (sigc::mem_fun (*this, &Panner2dWindow::width_changed)); + + p->pannable()->pan_width_control->Changed.connect (connections, invalidator(*this), boost::bind (&Panner2dWindow::set_width, this), gui_context()); + p->Changed.connect (connections, invalidator (*this), boost::bind (&Panner2dWindow::set_bypassed, this), gui_context()); button_box.set_spacing (6); button_box.pack_start (bypass_button, false, false); - spinner_box.set_spacing (6); left_side.set_spacing (6); left_side.pack_start (button_box, false, false); + + Gtk::Label* l = manage (new Label ( + p->panner()->describe_parameter(PanWidthAutomation), + Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false)); + spinner_box.pack_start (*l, false, false); + spinner_box.pack_start (width_spinner, false, false); left_side.pack_start (spinner_box, false, false); + l->show (); bypass_button.show (); button_box.show (); + width_spinner.show (); spinner_box.show (); left_side.show (); @@ -834,6 +847,8 @@ Panner2dWindow::Panner2dWindow (boost::shared_ptr p, int32_t h, uin add (hpacker); reset (inputs); + set_width(); + set_bypassed(); widget.show (); } @@ -841,21 +856,6 @@ void Panner2dWindow::reset (uint32_t n_inputs) { widget.reset (n_inputs); - -#if 0 - while (spinners.size() < n_inputs) { - // spinners.push_back (new Gtk::SpinButton (widget.azimuth (spinners.size()))); - //spinner_box.pack_start (*spinners.back(), false, false); - //spinners.back()->set_digits (4); - spinners.back()->show (); - } - - while (spinners.size() > n_inputs) { - spinner_box.remove (*spinners.back()); - delete spinners.back(); - spinners.erase (--spinners.end()); - } -#endif } void @@ -868,6 +868,44 @@ Panner2dWindow::bypass_toggled () widget.get_panner_shell()->set_bypassed (view); } } +void +Panner2dWindow::width_changed () +{ + float model = widget.get_panner_shell()->pannable()->pan_width_control->get_value(); + float view = width_spinner.get_value() / 100.0; + if (model != view) { + widget.get_panner_shell()->panner()->set_width (view); + } +} + +void +Panner2dWindow::set_bypassed () +{ + bool view = bypass_button.get_active (); + bool model = widget.get_panner_shell()->bypassed (); + if (model != view) { + bypass_button.set_active(model); + } + + set params = widget.get_panner_shell()->panner()->what_can_be_automated(); + set::iterator p = params.find(PanWidthAutomation); + if (p == params.end()) { + spinner_box.set_sensitive(false); + } else { + spinner_box.set_sensitive(true); + } +} + +void +Panner2dWindow::set_width () +{ + // rounding of spinbox is different from slider -- TODO use slider + float model = (widget.get_panner_shell()->pannable()->pan_width_control->get_value() * 100.0); + float view = (width_spinner.get_value()); + if (model != view) { + width_spinner.set_value (model); + } +} bool Panner2dWindow::on_key_press_event (GdkEventKey* event) diff --git a/gtk2_ardour/panner2d.h b/gtk2_ardour/panner2d.h index 9df0355518..033b5711bc 100644 --- a/gtk2_ardour/panner2d.h +++ b/gtk2_ardour/panner2d.h @@ -66,9 +66,6 @@ class Panner2d : public Gtk::DrawingArea boost::shared_ptr get_panner_shell() const { return panner_shell; } - sigc::signal PuckMoved; - sigc::signal TargetMoved; - void cart_to_gtk (PBD::CartesianVector&) const; void gtk_to_cart (PBD::CartesianVector&) const; @@ -161,9 +158,15 @@ class Panner2dWindow : public ArdourWindow Gtk::VBox spinner_box; Gtk::VBox left_side; - std::vector spinners; + Gtk::Adjustment width_adjustment; + Gtk::SpinButton width_spinner; + + PBD::ScopedConnectionList connections; + void set_bypassed(); + void set_width(); void bypass_toggled (); + void width_changed (); bool on_key_press_event (GdkEventKey*); bool on_key_release_event (GdkEventKey*); }; From 60960280d10f21cdf25e38eaec86af21d14483b1 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:30:22 +0100 Subject: [PATCH 07/31] redefine Pi :) --- libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp b/libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp index f1391b5bbd..c2d2add53b 100644 --- a/libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp +++ b/libs/vamp-sdk/src/vamp-hostsdk/PluginInputDomainAdapter.cpp @@ -248,7 +248,7 @@ PluginInputDomainAdapter::Impl::~Impl() // for some visual studii apparently #ifndef M_PI -#define M_PI 3.14159265358979232846 +#define M_PI 3.14159265358979323846 #endif bool From b54a231035e49a4496a18dc7f56574515caad3be Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sat, 11 Jan 2014 23:34:19 +0100 Subject: [PATCH 08/31] hide speaker-config in menu (it's not implemented yet) --- gtk2_ardour/ardour.menus.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index 6068055324..a354df603d 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -502,7 +502,9 @@ +#if 0 +#endif From 4a0bcd658c3447aa321429fe7d108dd17f4b70e7 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 00:57:27 +0100 Subject: [PATCH 09/31] VBAP GUI object-grab & position --- gtk2_ardour/panner2d.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index d1bf858173..163a729267 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -595,8 +595,8 @@ Panner2d::on_button_press_event (GdkEventButton *ev) switch (ev->button) { case 1: case 2: - x = ev->x - border; - y = ev->y - border; + x = ev->x - hoffset; + y = ev->y - voffset; if ((drag_target = find_closest_object (x, y, is_signal)) != 0) { if (!is_signal) { @@ -671,6 +671,8 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state) return false; } + evx -= hoffset; + evy -= voffset; if (state & GDK_BUTTON1_MASK && !(state & GDK_BUTTON2_MASK)) { CartesianVector c; @@ -687,11 +689,7 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state) set params = panner_shell->panner()->what_can_be_automated(); set::iterator p = params.find(PanElevationAutomation); - double y0 = 4.0; - if (height > large_size_threshold) { - y0 = 12.0; - } - CartesianVector cp (evx - 12.0, evy - y0, 0.0); + CartesianVector cp (evx, evy, 0.0); AngularVector av; gtk_to_cart (cp); From bf7c6c07bf58affd7b9d966025fb81cd0c75cb4f Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 02:05:01 +0100 Subject: [PATCH 10/31] credit where credit is due for speaker-config and VBAP fixes inspiration --- gtk2_ardour/about.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/gtk2_ardour/about.cc b/gtk2_ardour/about.cc index 280f72a8dc..e1fd748f99 100644 --- a/gtk2_ardour/about.cc +++ b/gtk2_ardour/about.cc @@ -126,6 +126,7 @@ static const char* authors[] = { N_("Hans Baier"), N_("Ben Bell"), N_("Sakari Bergen"), + N_("Christian Borss"), N_("Chris Cannam"), N_("Jesse Chappell"), N_("Thomas Charbonnel"), From f9b8300ec68bbc1b6304d6b5f54367819dcb7dd1 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 03:36:20 +0100 Subject: [PATCH 11/31] swap channel VBAP channel --- gtk2_ardour/panner2d.cc | 4 ++-- libs/panners/vbap/vbap.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 163a729267..941b371827 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -217,8 +217,8 @@ Panner2d::label_signals () break; case 2: - signals[0]->set_text ("L"); - signals[1]->set_text ("R"); + signals[0]->set_text (_("L")); + signals[1]->set_text (_("R")); break; default: diff --git a/libs/panners/vbap/vbap.cc b/libs/panners/vbap/vbap.cc index d0202d4466..fc349eb795 100644 --- a/libs/panners/vbap/vbap.cc +++ b/libs/panners/vbap/vbap.cc @@ -118,7 +118,7 @@ VBAPanner::update () double elevation = _pannable->pan_elevation_control->get_value() * 90.0; if (_signals.size() > 1) { - double w = (_pannable->pan_width_control->get_value()); + double w = - (_pannable->pan_width_control->get_value()); double signal_direction = _pannable->pan_azimuth_control->get_value() - (w/2); double grd_step_per_signal = w / (_signals.size() - 1); for (vector::iterator s = _signals.begin(); s != _signals.end(); ++s) { From 261638a5a0befa5ebac64f0cfb4fe285b5d946d7 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 03:56:35 +0100 Subject: [PATCH 12/31] don't display elevation if panner does not support it. --- gtk2_ardour/panner2d.cc | 32 +++++++++++++++++++++----------- gtk2_ardour/panner2d.h | 7 +------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 941b371827..1be1371ab8 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -74,6 +74,7 @@ Panner2d::Panner2d (boost::shared_ptr p, int32_t h) , width (0) , height (h) , last_width (0) + , have_elevation (false) { panner_shell->Changed.connect (connections, invalidator (*this), boost::bind (&Panner2d::handle_state_change, this), gui_context()); @@ -82,6 +83,7 @@ Panner2d::Panner2d (boost::shared_ptr p, int32_t h) drag_target = 0; set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK); + handle_state_change (); handle_position_change (); } @@ -200,6 +202,14 @@ Panner2d::handle_state_change () { panconnect.drop_connections(); panner_shell->panner()->SignalPositionChanged.connect (panconnect, invalidator(*this), boost::bind (&Panner2d::handle_position_change, this), gui_context()); + + set params = panner_shell->panner()->what_can_be_automated(); + set::iterator p = params.find(PanElevationAutomation); + bool elev = have_elevation; + have_elevation = (p == params.end()) ? false : true; + if (elev != have_elevation) { + handle_position_change(); + } queue_draw (); } @@ -237,8 +247,12 @@ Panner2d::handle_position_change () uint32_t n; double w = panner_shell->pannable()->pan_width_control->get_value(); - position.position = AngularVector (panner_shell->pannable()->pan_azimuth_control->get_value() * 360.0, - panner_shell->pannable()->pan_elevation_control->get_value() * 90.0); + if (have_elevation) { + position.position = AngularVector (panner_shell->pannable()->pan_azimuth_control->get_value() * 360.0, + panner_shell->pannable()->pan_elevation_control->get_value() * 90.0); + } else { + position.position = AngularVector (panner_shell->pannable()->pan_azimuth_control->get_value() * 360.0, 0); + } for (uint32_t i = 0; i < signals.size(); ++i) { signals[i]->position = panner_shell->panner()->signal_position (i); @@ -480,7 +494,9 @@ Panner2d::on_expose_event (GdkEventExpose *event) if (signal->visible) { - signal->position.cartesian (c); + PBD::AngularVector sp = signal->position; + if (!have_elevation) sp.ele = 0; + sp.cartesian (c); cart_to_gtk (c); cairo_new_path (cr); @@ -607,11 +623,8 @@ Panner2d::on_button_press_event (GdkEventButton *ev) } } - drag_x = ev->x; - drag_y = ev->y; state = (GdkModifierType) ev->state; - - return handle_motion (drag_x, drag_y, state); + return handle_motion (ev->x, ev->y, state); break; default: @@ -686,14 +699,11 @@ Panner2d::handle_motion (gint evx, gint evy, GdkModifierType state) } if (need_move) { - set params = panner_shell->panner()->what_can_be_automated(); - set::iterator p = params.find(PanElevationAutomation); - CartesianVector cp (evx, evy, 0.0); AngularVector av; gtk_to_cart (cp); - if (p == params.end()) { + if (!have_elevation) { clamp_to_circle (cp.x, cp.y); cp.angular (av); if (drag_target == &position) { diff --git a/gtk2_ardour/panner2d.h b/gtk2_ardour/panner2d.h index 033b5711bc..cd84ad9e2c 100644 --- a/gtk2_ardour/panner2d.h +++ b/gtk2_ardour/panner2d.h @@ -108,9 +108,6 @@ class Panner2d : public Gtk::DrawingArea Target position; Target *drag_target; - int drag_x; - int drag_y; - bool allow_speaker_motion; int width; int height; double radius; @@ -119,9 +116,7 @@ class Panner2d : public Gtk::DrawingArea double voffset; double last_width; bool did_move; - - gint compute_x (float); - gint compute_y (float); + bool have_elevation; Target *find_closest_object (gdouble x, gdouble y, bool& is_signal); From 58f82b52e584c48c90cc98c575d085aef165ebc0 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 17:14:14 +0100 Subject: [PATCH 13/31] cosmetic change - properly use lilv API --- libs/ardour/lv2_plugin.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index dc7805c273..58060f372f 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -1094,10 +1094,12 @@ LV2Plugin::do_save_preset(string name) lilv_state_free(state); std::string uri = Glib::filename_to_uri(Glib::build_filename(bundle, file_name)); - LilvNode *node = lilv_new_uri(_world.world, uri.c_str()); - lilv_world_load_bundle(_world.world, node); - lilv_world_load_resource(_world.world, node); - lilv_node_free(node); + LilvNode *node_bundle = lilv_new_uri(_world.world, Glib::filename_to_uri(Glib::build_filename(bundle, "/")).c_str()); + LilvNode *node_preset = lilv_new_uri(_world.world, uri.c_str()); + lilv_world_load_bundle(_world.world, node_bundle); + lilv_world_load_resource(_world.world, node_preset); + lilv_node_free(node_bundle); + lilv_node_free(node_preset); return uri; } From 1eaa30b7257f006878c9b2c88ecbaea5bc36b174 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 18:11:14 +0100 Subject: [PATCH 14/31] aubio 3+4 compat --- libs/ardour/wscript | 4 +- libs/vamp-plugins/Onset.cpp | 125 ++++++++++++++++++++++++++++++++++-- libs/vamp-plugins/Onset.h | 26 ++++++-- libs/vamp-plugins/wscript | 6 +- 4 files changed, 147 insertions(+), 14 deletions(-) diff --git a/libs/ardour/wscript b/libs/ardour/wscript index dbc0335ecd..962ff6dbe3 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -244,7 +244,9 @@ def configure(conf): 'libardour3', conf.env['MAJOR'], conf.env['MINOR'], 0) autowaf.configure(conf) autowaf.check_pkg(conf, 'aubio', uselib_store='AUBIO', - exact_version='0.3.2') + atleast_version='0.3.2') + autowaf.check_pkg(conf, 'aubio', uselib_store='AUBIO4', + atleast_version='0.4.0', mandatory=False) autowaf.check_pkg(conf, 'libxml-2.0', uselib_store='XML') autowaf.check_pkg(conf, 'lrdf', uselib_store='LRDF', atleast_version='0.4.0') diff --git a/libs/vamp-plugins/Onset.cpp b/libs/vamp-plugins/Onset.cpp index d475b11be9..196ca29dac 100644 --- a/libs/vamp-plugins/Onset.cpp +++ b/libs/vamp-plugins/Onset.cpp @@ -22,29 +22,51 @@ using std::vector; using std::cerr; using std::endl; +#ifdef HAVE_AUBIO4 +const char *getAubioNameForOnsetType(OnsetType t) +{ + // In the same order as the enum elements in the header + static const char *const names[] = { + "energy", "specdiff", "hfc", "complex", "phase", "kl", "mkl", "specflux" + }; + return names[(int)t]; +} +#endif + Onset::Onset(float inputSampleRate) : Plugin(inputSampleRate), m_ibuf(0), - m_fftgrain(0), m_onset(0), +#ifdef HAVE_AUBIO4 + m_onsetdet(0), + m_onsettype(OnsetComplex), + m_minioi(4), + m_silence(-70), +#else + m_fftgrain(0), m_pv(0), m_peakpick(0), m_onsetdet(0), m_onsettype(aubio_onset_complex), - m_threshold(0.3), + m_channelCount(1), m_silence(-90), - m_channelCount(1) +#endif + m_threshold(0.3) { } Onset::~Onset() { +#ifdef HAVE_AUBIO4 + if (m_onsetdet) del_aubio_onset(m_onsetdet); +#else if (m_onsetdet) aubio_onsetdetection_free(m_onsetdet); - if (m_ibuf) del_fvec(m_ibuf); - if (m_onset) del_fvec(m_onset); if (m_fftgrain) del_cvec(m_fftgrain); if (m_pv) del_aubio_pvoc(m_pv); if (m_peakpick) del_aubio_peakpicker(m_peakpick); +#endif + if (m_ibuf) del_fvec(m_ibuf); + if (m_onset) del_fvec(m_onset); } string @@ -74,7 +96,11 @@ Onset::getMaker() const int Onset::getPluginVersion() const { +#ifdef HAVE_AUBIO4 + return 2; +#else return 1; +#endif } string @@ -86,10 +112,20 @@ Onset::getCopyright() const bool Onset::initialise(size_t channels, size_t stepSize, size_t blockSize) { - m_channelCount = channels; m_stepSize = stepSize; m_blockSize = blockSize; +#ifdef HAVE_AUBIO4 + if (channels != 1) { + std::cerr << "Onset::initialise: channels must be 1" << std::endl; + return false; + } + m_ibuf = new_fvec(stepSize); + m_onset = new_fvec(1); + reset(); +#else + m_channelCount = channels; + m_ibuf = new_fvec(stepSize, channels); m_onset = new_fvec(1, channels); m_fftgrain = new_cvec(blockSize, channels); @@ -103,13 +139,32 @@ Onset::initialise(size_t channels, size_t stepSize, size_t blockSize) m_lastOnset = Vamp::RealTime::zeroTime - m_delay - m_delay; +#endif return true; } +#ifdef HAVE_AUBIO4 void Onset::reset() { + if (m_onsetdet) del_aubio_onset(m_onsetdet); + + m_onsetdet = new_aubio_onset + (const_cast(getAubioNameForOnsetType(m_onsettype)), + m_blockSize, + m_stepSize, + lrintf(m_inputSampleRate)); + + aubio_onset_set_threshold(m_onsetdet, m_threshold); + aubio_onset_set_silence(m_onsetdet, m_silence); + aubio_onset_set_minioi(m_onsetdet, m_minioi); + + m_delay = Vamp::RealTime::frame2RealTime(4 * m_stepSize, + lrintf(m_inputSampleRate)); + + m_lastOnset = Vamp::RealTime::zeroTime - m_delay - m_delay; } +#endif size_t Onset::getPreferredStepSize() const @@ -132,8 +187,13 @@ Onset::getParameterDescriptors() const desc.identifier = "onsettype"; desc.name = "Onset Detection Function Type"; desc.minValue = 0; +#ifdef HAVE_AUBIO4 + desc.maxValue = 7; + desc.defaultValue = (int)OnsetComplex; +#else desc.maxValue = 6; desc.defaultValue = (int)aubio_onset_complex; +#endif desc.isQuantized = true; desc.quantizeStep = 1; desc.valueNames.push_back("Energy Based"); @@ -143,6 +203,9 @@ Onset::getParameterDescriptors() const desc.valueNames.push_back("Phase Deviation"); desc.valueNames.push_back("Kullback-Liebler"); desc.valueNames.push_back("Modified Kullback-Liebler"); +#ifdef HAVE_AUBIO4 + desc.valueNames.push_back("Spectral Flux"); +#endif list.push_back(desc); desc = ParameterDescriptor(); @@ -159,11 +222,27 @@ Onset::getParameterDescriptors() const desc.name = "Silence Threshold"; desc.minValue = -120; desc.maxValue = 0; +#ifdef HAVE_AUBIO4 + desc.defaultValue = -70; +#else desc.defaultValue = -90; +#endif desc.unit = "dB"; desc.isQuantized = false; list.push_back(desc); +#ifdef HAVE_AUBIO4 + desc = ParameterDescriptor(); + desc.identifier = "minioi"; + desc.name = "Minimum Inter-Onset Interval"; + desc.minValue = 0; + desc.maxValue = 40; + desc.defaultValue = 4; + desc.unit = "ms"; + desc.isQuantized = true; + desc.quantizeStep = 1; + list.push_back(desc); +#endif return list; } @@ -176,6 +255,10 @@ Onset::getParameter(std::string param) const return m_threshold; } else if (param == "silencethreshold") { return m_silence; +#ifdef HAVE_AUBIO4 + } else if (param == "minioi") { + return m_minioi; +#endif } else { return 0.0; } @@ -186,6 +269,16 @@ Onset::setParameter(std::string param, float value) { if (param == "onsettype") { switch (lrintf(value)) { +#ifdef HAVE_AUBIO4 + case 0: m_onsettype = OnsetEnergy; break; + case 1: m_onsettype = OnsetSpecDiff; break; + case 2: m_onsettype = OnsetHFC; break; + case 3: m_onsettype = OnsetComplex; break; + case 4: m_onsettype = OnsetPhase; break; + case 5: m_onsettype = OnsetKL; break; + case 6: m_onsettype = OnsetMKL; break; + case 7: m_onsettype = OnsetSpecFlux; break; +#else case 0: m_onsettype = aubio_onset_energy; break; case 1: m_onsettype = aubio_onset_specdiff; break; case 2: m_onsettype = aubio_onset_hfc; break; @@ -193,11 +286,16 @@ Onset::setParameter(std::string param, float value) case 4: m_onsettype = aubio_onset_phase; break; case 5: m_onsettype = aubio_onset_kl; break; case 6: m_onsettype = aubio_onset_mkl; break; +#endif } } else if (param == "peakpickthreshold") { m_threshold = value; } else if (param == "silencethreshold") { m_silence = value; +#ifdef HAVE_AUBIO4 + } else if (param == "minioi") { + m_minioi = value; +#endif } } @@ -216,6 +314,7 @@ Onset::getOutputDescriptors() const d.sampleRate = 0; list.push_back(d); +#ifndef HAVE_AUBIO4 d = OutputDescriptor(); d.identifier = "detectionfunction"; d.name = "Onset Detection Function"; @@ -226,7 +325,7 @@ Onset::getOutputDescriptors() const d.isQuantized = false; d.sampleType = OutputDescriptor::OneSamplePerStep; list.push_back(d); - +#endif return list; } @@ -234,6 +333,15 @@ Onset::FeatureSet Onset::process(const float *const *inputBuffers, Vamp::RealTime timestamp) { +#ifdef HAVE_AUBIO4 + for (size_t i = 0; i < m_stepSize; ++i) { + fvec_set_sample(m_ibuf, inputBuffers[0][i], i); + } + + aubio_onset_do(m_onsetdet, m_ibuf, m_onset); + + bool isonset = m_onset->data[0]; +#else for (size_t i = 0; i < m_stepSize; ++i) { for (size_t j = 0; j < m_channelCount; ++j) { fvec_write_sample(m_ibuf, inputBuffers[j][i], j, i); @@ -250,6 +358,7 @@ Onset::process(const float *const *inputBuffers, isonset = false; } } +#endif FeatureSet returnFeatures; @@ -263,11 +372,13 @@ Onset::process(const float *const *inputBuffers, m_lastOnset = timestamp; } } +#ifndef HAVE_AUBIO4 Feature feature; for (size_t j = 0; j < m_channelCount; ++j) { feature.values.push_back(m_onset->data[j][0]); } returnFeatures[1].push_back(feature); +#endif return returnFeatures; } diff --git a/libs/vamp-plugins/Onset.h b/libs/vamp-plugins/Onset.h index 314e107308..bba95e0d2b 100644 --- a/libs/vamp-plugins/Onset.h +++ b/libs/vamp-plugins/Onset.h @@ -20,6 +20,19 @@ #include #include +#ifdef HAVE_AUBIO4 +enum OnsetType { + OnsetEnergy, + OnsetSpecDiff, + OnsetHFC, + OnsetComplex, + OnsetPhase, + OnsetKL, + OnsetMKL, + OnsetSpecFlux // new in 0.4! +}; +#endif + class Onset : public Vamp::Plugin { public: @@ -54,20 +67,25 @@ public: protected: fvec_t *m_ibuf; - cvec_t *m_fftgrain; fvec_t *m_onset; +#ifdef HAVE_AUBIO4 + aubio_onset_t *m_onsetdet; + OnsetType m_onsettype; + float m_minioi; +#else + cvec_t *m_fftgrain; aubio_pvoc_t *m_pv; aubio_pickpeak_t *m_peakpick; aubio_onsetdetection_t *m_onsetdet; aubio_onsetdetection_type m_onsettype; - float m_threshold; + size_t m_channelCount; +#endif float m_silence; + float m_threshold; size_t m_stepSize; size_t m_blockSize; - size_t m_channelCount; Vamp::RealTime m_delay; Vamp::RealTime m_lastOnset; }; - #endif diff --git a/libs/vamp-plugins/wscript b/libs/vamp-plugins/wscript index 9568e96367..24a9529ba2 100644 --- a/libs/vamp-plugins/wscript +++ b/libs/vamp-plugins/wscript @@ -26,8 +26,10 @@ def configure(conf): conf.load('compiler_cxx') autowaf.configure(conf) autowaf.check_pkg(conf, 'fftw3f', uselib_store='FFTW3F', mandatory=True) - autowaf.check_pkg(conf, 'aubio', uselib_store='AUBIO', mandatory=False, - exact_version='0.3.2') + autowaf.check_pkg(conf, 'aubio', uselib_store='AUBIO', + atleast_version='0.3.2') + autowaf.check_pkg(conf, 'aubio', uselib_store='AUBIO4', + atleast_version='0.4.0', mandatory=False) conf.write_config_header('libvampplugins-config.h', remove=False) def build(bld): From 0e77e578cc6001a8b5634f6eaad30f565906a472 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 21:41:02 +0100 Subject: [PATCH 15/31] VBAP panner UI tweaks: * fix mouse-grab of sentinel * make GUI more hemisphere like (circles at latitude) * change alpha slightly to show signal overlap --- gtk2_ardour/panner2d.cc | 118 +++++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 56 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 1be1371ab8..c59523abe3 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -170,8 +170,8 @@ Panner2d::on_size_allocate (Gtk::Allocation& alloc) hoffset = max ((double) (width - height), border); voffset = max ((double) (height - width), border); - hoffset /= 2.0; - voffset /= 2.0; + hoffset = rint(hoffset / 2.0); + voffset = rint(voffset / 2.0); DrawingArea::on_size_allocate (alloc); } @@ -294,8 +294,7 @@ Panner2d::find_closest_object (gdouble x, gdouble y, bool& is_signal) float best_distance = FLT_MAX; CartesianVector c; - /* start with the position itself - */ + /* start with the position itself */ position.position.cartesian (c); cart_to_gtk (c); @@ -303,6 +302,7 @@ Panner2d::find_closest_object (gdouble x, gdouble y, bool& is_signal) (c.y - y) * (c.y - y)); closest = &position; +#if 0 // TODO signal grab -> change width, not position for (Targets::const_iterator i = signals.begin(); i != signals.end(); ++i) { candidate = *i; @@ -317,6 +317,7 @@ Panner2d::find_closest_object (gdouble x, gdouble y, bool& is_signal) best_distance = distance; } } +#endif is_signal = true; @@ -421,60 +422,60 @@ Panner2d::on_expose_event (GdkEventExpose *event) /* horizontal line of "crosshairs" */ cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 1.0); - cairo_move_to (cr, 0.0, radius); - cairo_line_to (cr, diameter, radius); + cairo_move_to (cr, 0.0, rint(radius) - .5); + cairo_line_to (cr, diameter, rint(radius) - .5); cairo_stroke (cr); /* vertical line of "crosshairs" */ - cairo_move_to (cr, radius, 0); - cairo_line_to (cr, radius, diameter); + cairo_move_to (cr, rint(radius) - .5, 0); + cairo_line_to (cr, rint(radius) - .5, diameter); cairo_stroke (cr); /* the circle on which signals live */ - cairo_set_line_width (cr, 2.0); + cairo_set_line_width (cr, 1.5); cairo_set_source_rgba (cr, 0.517, 0.772, 0.882, 1.0); cairo_arc (cr, radius, radius, radius, 0.0, 2.0 * M_PI); cairo_stroke (cr); - /* 3 other circles of smaller diameter circle on which signals live */ - - cairo_set_line_width (cr, 1.0); - cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 1.0); - cairo_arc (cr, radius, radius, radius * 0.75, 0, 2.0 * M_PI); - cairo_stroke (cr); - cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 0.85); - cairo_arc (cr, radius, radius, radius * 0.50, 0, 2.0 * M_PI); - cairo_stroke (cr); - cairo_arc (cr, radius, radius, radius * 0.25, 0, 2.0 * M_PI); - cairo_stroke (cr); - - if (signals.size() > 1) { - /* arc to show "diffusion" */ - - double width_angle = fabs (panner_shell->pannable()->pan_width_control->get_value()) * 2 * M_PI; - double position_angle = (2 * M_PI) - panner_shell->pannable()->pan_azimuth_control->get_value() * 2 * M_PI; - - cairo_save (cr); - cairo_translate (cr, radius, radius); - cairo_rotate (cr, position_angle - (width_angle/2.0)); - cairo_move_to (cr, 0, 0); - cairo_arc_negative (cr, 0, 0, radius, width_angle, 0.0); - cairo_close_path (cr); - if (panner_shell->pannable()->pan_width_control->get_value() >= 0.0) { - /* normal width */ - cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 0.45); + for (uint32_t rad = 15; rad < 90; rad += 15) { + cairo_set_line_width (cr, .5 + (float)rad / 150.0); + if (rad == 45) { + cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 1.0); } else { - /* inverse width */ - cairo_set_source_rgba (cr, 1.0, 0.419, 0.419, 0.45); + cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 0.8); } - cairo_fill (cr); - cairo_restore (cr); + cairo_new_path (cr); + cairo_arc (cr, radius, radius, radius * sin(M_PI * (float) rad / 180.0), 0, 2.0 * M_PI); + cairo_stroke (cr); } if (!panner_shell->bypassed()) { + if (signals.size() > 1) { + /* arc to show "diffusion" */ + + double width_angle = fabs (panner_shell->pannable()->pan_width_control->get_value()) * 2 * M_PI; + double position_angle = (2 * M_PI) - panner_shell->pannable()->pan_azimuth_control->get_value() * 2 * M_PI; + + cairo_save (cr); + cairo_translate (cr, radius, radius); + cairo_rotate (cr, position_angle - (width_angle/2.0)); + cairo_move_to (cr, 0, 0); + cairo_arc_negative (cr, 0, 0, radius, width_angle, 0.0); + cairo_close_path (cr); + if (panner_shell->pannable()->pan_width_control->get_value() >= 0.0) { + /* normal width */ + cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 0.45); + } else { + /* inverse width */ + cairo_set_source_rgba (cr, 1.0, 0.419, 0.419, 0.45); + } + cairo_fill (cr); + cairo_restore (cr); + } + double arc_radius; cairo_select_font_face (cr, "sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); @@ -486,6 +487,18 @@ Panner2d::on_expose_event (GdkEventExpose *event) arc_radius = 12.0; } + /* draw position */ + + position.position.cartesian (c); + cart_to_gtk (c); + + cairo_new_path (cr); + cairo_arc (cr, c.x, c.y, arc_radius + 1.0, 0, 2.0 * M_PI); + cairo_set_source_rgba (cr, 1.0, 0.419, 0.419, 0.85); + cairo_fill_preserve (cr); + cairo_set_source_rgba (cr, 1.0, 0.905, 0.905, 0.85); + cairo_stroke (cr); + /* signals */ if (signals.size() > 1) { @@ -494,6 +507,9 @@ Panner2d::on_expose_event (GdkEventExpose *event) if (signal->visible) { + /* TODO check for overlap - multiple src at same position + * -> visualize it properly + */ PBD::AngularVector sp = signal->position; if (!have_elevation) sp.ele = 0; sp.cartesian (c); @@ -501,14 +517,16 @@ Panner2d::on_expose_event (GdkEventExpose *event) cairo_new_path (cr); cairo_arc (cr, c.x, c.y, arc_radius, 0, 2.0 * M_PI); - cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 0.85); + cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 0.75); cairo_fill_preserve (cr); - cairo_set_source_rgba (cr, 0.517, 0.772, 0.882, 1.0); + cairo_set_source_rgba (cr, 0.517, 0.772, 0.882, 0.8); cairo_stroke (cr); if (!small && !signal->text.empty()) { - cairo_set_source_rgb (cr, 0.517, 0.772, 0.882); - /* the +/- adjustments are a hack to try to center the text in the circle */ + cairo_set_source_rgba (cr, 0.517, 0.772, 0.882, .9); + /* the +/- adjustments are a hack to try to center the text in the circle + * TODO use pango get_pixel_size() -- see mono_panner.cc + */ if (small) { cairo_move_to (cr, c.x - 1, c.y + 1); } else { @@ -575,18 +593,6 @@ Panner2d::on_expose_event (GdkEventExpose *event) } } - - /* draw position */ - - position.position.cartesian (c); - cart_to_gtk (c); - - cairo_new_path (cr); - cairo_arc (cr, c.x, c.y, arc_radius, 0, 2.0 * M_PI); - cairo_set_source_rgba (cr, 1.0, 0.419, 0.419, 0.85); - cairo_fill_preserve (cr); - cairo_set_source_rgba (cr, 1.0, 0.905, 0.905, 0.85); - cairo_stroke (cr); } cairo_destroy (cr); From 7e3282486d9638b6cfa1cd727a793df949270091 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 21:47:15 +0100 Subject: [PATCH 16/31] take process lock when adding processors: fixes possible crash if a processor modifies port-count 1. a processor is inserted and activated with processor-lock held 2. only after that the process_lock() is taken, configure_processors() is called which reconfigures-IO BUT if the processor that is inserted changes the channel count AND audio is processed before IOs are reconfigured -> possible crash (invalid port-buffers) To reproduce: Bus1 (2in, 3out), Bus2 (2in, 3out) - add a send from Bus1 to Bus2, - then add a processor to Bus1, just before the send which increases the channel-count to 4 -> occasional crash or assert. --- libs/ardour/route.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 6f23e920d4..e819666a3a 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -947,6 +947,7 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< { Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); boost::shared_ptr pi; boost::shared_ptr porti; @@ -986,8 +987,6 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< // configure redirect ports properly, etc. { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - if (configure_processors_unlocked (err)) { pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... @@ -1117,6 +1116,7 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr { Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { @@ -1137,8 +1137,8 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr (*i)->activate (); } + /* Think: does this really need to be called for every processor in the loop? */ { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); if (configure_processors_unlocked (err)) { pstate.restore (); configure_processors_unlocked (0); // it worked before we tried to add it ... From a08e7c00165d478387b279dcb219c3113d58d8df Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 12 Jan 2014 23:13:35 +0100 Subject: [PATCH 17/31] fix processor -> reconfigure I/O || process concurrency Add a ReaderLock to Route::process_output_buffers(). But process_output_buffers() is always called with processor-lock held. To avoid deadlocks, a processor WriterLock must always imply a process-lock (IFF reconfigure-I/O is called with _processor_lock). Otherwise: e.g. * add_processor() -> takes processor-lock. set up and activate processor. * simult. audio-engine process, process-lock -> call process_output_buffers() -> wait for processor-lock * add_processor() continues -> calls reconfigure-io -> take process-lock -> deadlock. --- libs/ardour/route.cc | 93 +++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 57 deletions(-) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index e819666a3a..842ce5e7e1 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -180,8 +180,7 @@ Route::init () Metering::Meter.connect_same_thread (*this, (boost::bind (&Route::meter, this))); { - /* run a configure so that the invisible processors get set up */ - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); configure_processors (0); } @@ -420,6 +419,9 @@ Route::process_output_buffers (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick, bool gain_automation_ok) { + Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK); + assert(lm.locked()); + /* figure out if we're going to use gain automation */ if (gain_automation_ok) { _amp->set_gain_automation_buffer (_session.gain_automation_buffer ()); @@ -945,9 +947,9 @@ Route::add_processor (boost::shared_ptr processor, boost::shared_ptr< } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); boost::shared_ptr pi; boost::shared_ptr porti; @@ -1114,9 +1116,9 @@ Route::add_processors (const ProcessorList& others, boost::shared_ptr } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) { @@ -1314,6 +1316,7 @@ Route::clear_processors (Placement p) } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorList new_list; ProcessorStreams err; @@ -1358,11 +1361,7 @@ Route::clear_processors (Placement p) } _processors = new_list; - - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - configure_processors_unlocked (&err); // this can't fail - } + configure_processors_unlocked (&err); // this can't fail } processor_max_streams.reset(); @@ -1378,7 +1377,7 @@ Route::clear_processors (Placement p) } int -Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err, bool need_process_lock) +Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err, bool) { // TODO once the export point can be configured properly, do something smarter here if (processor == _capturing_processor) { @@ -1398,6 +1397,7 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream processor_max_streams.reset(); { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1438,22 +1438,11 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream return 1; } - if (need_process_lock) { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - - if (configure_processors_unlocked (err)) { - pstate.restore (); - /* we know this will work, because it worked before :) */ - configure_processors_unlocked (0); - return -1; - } - } else { - if (configure_processors_unlocked (err)) { - pstate.restore (); - /* we know this will work, because it worked before :) */ - configure_processors_unlocked (0); - return -1; - } + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; } _have_internal_generator = false; @@ -1490,6 +1479,7 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* processor_max_streams.reset(); { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1536,16 +1526,13 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* _output->set_user_latency (0); - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - - if (configure_processors_unlocked (err)) { - pstate.restore (); - /* we know this will work, because it worked before :) */ - configure_processors_unlocked (0); - return -1; - } + if (configure_processors_unlocked (err)) { + pstate.restore (); + /* we know this will work, because it worked before :) */ + configure_processors_unlocked (0); + return -1; } + //lx.unlock(); _have_internal_generator = false; @@ -1592,8 +1579,8 @@ Route::set_custom_panner_uri (std::string const panner_uri) /* reconfigure I/O -- re-initialize panner modules */ { - Glib::Threads::RWLock::WriterLock lm (_processor_lock); Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + Glib::Threads::RWLock::WriterLock lm (_processor_lock); for (ProcessorList::iterator p = _processors.begin(); p != _processors.end(); ++p) { boost::shared_ptr dl; @@ -1640,7 +1627,6 @@ int Route::configure_processors (ProcessorStreams* err) { assert (!AudioEngine::instance()->process_lock().trylock()); - if (!_in_configure_processors) { Glib::Threads::RWLock::WriterLock lm (_processor_lock); return configure_processors_unlocked (err); @@ -1811,6 +1797,7 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err */ { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1872,13 +1859,9 @@ Route::reorder_processors (const ProcessorList& new_order, ProcessorStreams* err /* If the meter is in a custom position, find it and make a rough note of its position */ maybe_note_meter_position (); - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - - if (configure_processors_unlocked (err)) { - pstate.restore (); - return -1; - } + if (configure_processors_unlocked (err)) { + pstate.restore (); + return -1; } } @@ -2605,11 +2588,11 @@ Route::set_processor_state (const XMLNode& node) } { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); _processors = new_order; if (must_configure) { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); configure_processors_unlocked (0); } @@ -3190,17 +3173,14 @@ void Route::listen_position_changed () { { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - - if (configure_processors_unlocked (0)) { - pstate.restore (); - configure_processors_unlocked (0); // it worked before we tried to add it ... - return; - } + if (configure_processors_unlocked (0)) { + pstate.restore (); + configure_processors_unlocked (0); // it worked before we tried to add it ... + return; } } @@ -3216,10 +3196,7 @@ Route::add_export_point() _capturing_processor.reset (new CapturingProcessor (_session)); _capturing_processor->activate (); - { - Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ()); - configure_processors (0); - } + configure_processors (0); } @@ -4170,6 +4147,7 @@ Route::has_external_redirects () const boost::shared_ptr Route::the_instrument () const { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); return the_instrument_unlocked (); } @@ -4198,6 +4176,7 @@ Route::non_realtime_locate (framepos_t pos) } { + //Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); Glib::Threads::RWLock::WriterLock lm (_processor_lock); for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { From 35ab234e5e1aa6bbdcc095c87dfcc316e3aeb3c4 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 10:46:11 +0100 Subject: [PATCH 18/31] fix deadlock when removing monitoring-section --- libs/ardour/route.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 842ce5e7e1..9f18d8da00 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1377,7 +1377,7 @@ Route::clear_processors (Placement p) } int -Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err, bool) +Route::remove_processor (boost::shared_ptr processor, ProcessorStreams* err, bool need_process_lock) { // TODO once the export point can be configured properly, do something smarter here if (processor == _capturing_processor) { @@ -1397,7 +1397,10 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream processor_max_streams.reset(); { - Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock (), Glib::Threads::NOT_LOCK); + if (need_process_lock) { + lx.acquire(); + } Glib::Threads::RWLock::WriterLock lm (_processor_lock); ProcessorState pstate (this); @@ -1457,6 +1460,9 @@ Route::remove_processor (boost::shared_ptr processor, ProcessorStream } } } + if (need_process_lock) { + lx.release(); + } } reset_instrument_info (); @@ -4147,8 +4153,7 @@ Route::has_external_redirects () const boost::shared_ptr Route::the_instrument () const { - Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); - Glib::Threads::RWLock::WriterLock lm (_processor_lock); + Glib::Threads::RWLock::ReaderLock lm (_processor_lock); return the_instrument_unlocked (); } From 5e2a145cdd79890ffe656e32ea012fb41f6424fe Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 10:47:17 +0100 Subject: [PATCH 19/31] ignore additional channels for AFL, PFL: If the monitor-section has fewer-channels than the solo-listen point: ignore additional channels. --- libs/ardour/internal_send.cc | 14 +++++++++++++- libs/ardour/route.cc | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc index 8136985e1f..9716cf002c 100644 --- a/libs/ardour/internal_send.cc +++ b/libs/ardour/internal_send.cc @@ -128,7 +128,19 @@ InternalSend::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame uint32_t const bufs_audio = bufs.count().get (DataType::AUDIO); uint32_t const mixbufs_audio = mixbufs.count().get (DataType::AUDIO); - assert (mixbufs.available().get (DataType::AUDIO) >= bufs_audio); + /* monitor-section has same number of channels as master-bus (on creation). + * + * There is no clear answer what should happen when trying to PFL or AFL + * a track that has more channels (bufs_audio from source-track is + * larger than mixbufs). + * + * There are two options: + * 1: discard additional channels (current) + * OR + * 2: require the monitor-section to have at least as many channels + * as the largest count of any route + */ + //assert (mixbufs.available().get (DataType::AUDIO) >= bufs_audio); /* Copy bufs into mixbufs, going round bufs more than once if necessary to ensure that every mixbuf gets some data. diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 9f18d8da00..a8b2eac691 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -2712,6 +2712,7 @@ Route::enable_monitor_send () /* master never sends to monitor section via the normal mechanism */ assert (!is_master ()); + assert (!is_monitor ()); /* make sure we have one */ if (!_monitor_send) { From 0c384b7c219872322a4462f62e5e67b4119caa6d Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 10:48:23 +0100 Subject: [PATCH 20/31] update internal-send port-count when target port-count changes --- libs/ardour/ardour/internal_send.h | 1 + libs/ardour/delivery.cc | 2 +- libs/ardour/internal_send.cc | 12 +++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libs/ardour/ardour/internal_send.h b/libs/ardour/ardour/internal_send.h index a7f0f73e6e..09b26d57d5 100644 --- a/libs/ardour/ardour/internal_send.h +++ b/libs/ardour/ardour/internal_send.h @@ -70,6 +70,7 @@ class InternalSend : public Send int connect_when_legal (); void init_gain (); int use_target (boost::shared_ptr); + void target_io_changed (); }; } // namespace ARDOUR diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 79c44ce94a..575d26acdd 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -388,7 +388,7 @@ Delivery::reset_panner () if (_panshell) { _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, pan_outs())); - if (_role == Main) { + if (_role == Main || _role == Aux) { _panshell->pannable()->set_panner (_panshell->panner()); } } diff --git a/libs/ardour/internal_send.cc b/libs/ardour/internal_send.cc index 9716cf002c..dac1839a5e 100644 --- a/libs/ardour/internal_send.cc +++ b/libs/ardour/internal_send.cc @@ -95,11 +95,21 @@ InternalSend::use_target (boost::shared_ptr sendto) target_connections.drop_connections (); _send_to->DropReferences.connect_same_thread (target_connections, boost::bind (&InternalSend::send_to_going_away, this)); - _send_to->PropertyChanged.connect_same_thread (target_connections, boost::bind (&InternalSend::send_to_property_changed, this, _1));; + _send_to->PropertyChanged.connect_same_thread (target_connections, boost::bind (&InternalSend::send_to_property_changed, this, _1)); + _send_to->io_changed.connect_same_thread (target_connections, boost::bind (&InternalSend::target_io_changed, this)); return 0; } +void +InternalSend::target_io_changed () +{ + assert (_send_to); + mixbufs.ensure_buffers (_send_to->internal_return()->input_streams(), _session.get_block_size()); + mixbufs.set_count (_send_to->internal_return()->input_streams()); + reset_panner(); +} + void InternalSend::send_to_going_away () { From 0559c1babb7d2fe0f884f8639df7a7b265ed6bad Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 10:48:37 +0100 Subject: [PATCH 21/31] add independent panner for internal (Aux) sends --- gtk2_ardour/mixer_strip.cc | 10 +--------- gtk2_ardour/processor_box.cc | 4 +++- libs/ardour/delivery.cc | 8 ++++++++ libs/ardour/route.cc | 6 ++++-- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index e7c3f138b8..3a3c39ea17 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -1893,16 +1893,8 @@ MixerStrip::show_send (boost::shared_ptr send) panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner()); panner_ui().set_available_panners(boost::shared_ptr(), std::map()); - panner_ui().setup_pan (); - - /* make sure the send has audio output */ - - if (_current_delivery->output() && _current_delivery->output()->n_ports().n_audio() > 0) { - panners.show_all (); - } else { - panners.hide_all (); - } + panners.show_all (); input_button.set_sensitive (false); group_button.set_sensitive (false); diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index cd91589391..b0e5e55250 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -46,6 +46,7 @@ #include "ardour/internal_return.h" #include "ardour/internal_send.h" #include "ardour/plugin_insert.h" +#include "ardour/pannable.h" #include "ardour/port_insert.h" #include "ardour/profile.h" #include "ardour/return.h" @@ -2051,8 +2052,9 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr continue; } + boost::shared_ptr sendpan(new Pannable (*_session)); XMLNode n (**niter); - InternalSend* s = new InternalSend (*_session, _route->pannable(), _route->mute_master(), + InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(), boost::shared_ptr(), Delivery::Aux); IOProcessor::prepare_for_reset (n, s->name()); diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 575d26acdd..26d2cf3acd 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -329,6 +329,9 @@ Delivery::state (bool full_state) if (_panshell) { node.add_child_nocopy (_panshell->get_state ()); + if (_panshell->pannable()) { + node.add_child_nocopy (_panshell->pannable()->get_state ()); + } } return node; @@ -358,6 +361,11 @@ Delivery::set_state (const XMLNode& node, int version) reset_panner (); + XMLNode* pannnode = node.child (X_("Pannable")); + if (_panshell->panner() && pannnode) { + _panshell->pannable()->set_state (*pannnode, version); + } + return 0; } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index a8b2eac691..ea67fe6073 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -2547,7 +2547,8 @@ Route::set_processor_state (const XMLNode& node) if (prop->value() == "intsend") { - processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr(), Delivery::Role (0))); + boost::shared_ptr sendpan (new Pannable (_session)); + processor.reset (new InternalSend (_session, sendpan, _mute_master, boost::shared_ptr(), Delivery::Role (0))); } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || @@ -2753,7 +2754,8 @@ Route::add_aux_send (boost::shared_ptr route, boost::shared_ptrprocess_lock ()); - listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); + boost::shared_ptr sendpan (new Pannable (_session)); + listener.reset (new InternalSend (_session, sendpan, _mute_master, route, Delivery::Aux)); } add_processor (listener, before); From d9cf6880b6d65665a55aed212503de0820db6f40 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 10:50:22 +0100 Subject: [PATCH 22/31] fix output metering for Sends (Aux and External) --- libs/ardour/ardour/send.h | 1 + libs/ardour/send.cc | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/ardour/ardour/send.h b/libs/ardour/ardour/send.h index 1a21d1d050..118110f05a 100644 --- a/libs/ardour/ardour/send.h +++ b/libs/ardour/ardour/send.h @@ -77,6 +77,7 @@ class Send : public Delivery private: /* disallow copy construction */ Send (const Send&); + void panshell_changed (); int set_state_2X (XMLNode const &, int); diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc index e74fd7f8ce..2c3d8f23ae 100644 --- a/libs/ardour/send.cc +++ b/libs/ardour/send.cc @@ -29,6 +29,7 @@ #include "ardour/buffer_set.h" #include "ardour/meter.h" #include "ardour/io.h" +#include "ardour/panner_shell.h" #include "i18n.h" @@ -86,6 +87,10 @@ Send::Send (Session& s, boost::shared_ptr p, boost::shared_ptrgain_control ()); + + if (panner_shell()) { + panner_shell()->Changed.connect_same_thread (*this, boost::bind (&Send::panshell_changed, this)); + } } Send::~Send () @@ -284,7 +289,7 @@ Send::can_support_io_configuration (const ChanCount& in, ChanCount& out) bool Send::configure_io (ChanCount in, ChanCount out) { - if (!_amp->configure_io (in, out) || !_meter->configure_io (in, out)) { + if (!_amp->configure_io (in, out)) { return false; } @@ -292,11 +297,21 @@ Send::configure_io (ChanCount in, ChanCount out) return false; } + if (!_meter->configure_io (ChanCount (DataType::AUDIO, pan_outs()), ChanCount (DataType::AUDIO, pan_outs()))) { + return false; + } + reset_panner (); return true; } +void +Send::panshell_changed () +{ + _meter->configure_io (ChanCount (DataType::AUDIO, pan_outs()), ChanCount (DataType::AUDIO, pan_outs())); +} + bool Send::set_name (const string& new_name) { From bc88203ef5f343f87016a28104f6977675f2c050 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 10:50:49 +0100 Subject: [PATCH 23/31] independent panning for external sends --- gtk2_ardour/processor_box.cc | 6 ++++-- gtk2_ardour/send_ui.cc | 5 +---- libs/ardour/delivery.cc | 2 +- libs/ardour/route.cc | 6 ++++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index b0e5e55250..98bb3f13cd 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -1403,7 +1403,8 @@ ProcessorBox::choose_insert () void ProcessorBox::choose_send () { - boost::shared_ptr send (new Send (*_session, _route->pannable(), _route->mute_master())); + boost::shared_ptr sendpan(new Pannable (*_session)); + boost::shared_ptr send (new Send (*_session, sendpan, _route->mute_master())); /* make an educated guess at the initial number of outputs for the send */ ChanCount outs = (_session->master_out()) @@ -2068,8 +2069,9 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr } else if (type->value() == "send") { + boost::shared_ptr sendpan(new Pannable (*_session)); XMLNode n (**niter); - Send* s = new Send (*_session, _route->pannable(), _route->mute_master()); + Send* s = new Send (*_session, sendpan, _route->mute_master()); IOProcessor::prepare_for_reset (n, s->name()); diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc index 1fead73084..54663dbe0a 100644 --- a/gtk2_ardour/send_ui.cc +++ b/gtk2_ardour/send_ui.cc @@ -52,10 +52,7 @@ SendUI::SendUI (Gtk::Window* parent, boost::shared_ptr s, Session* session _vbox.set_border_width (5); _vbox.pack_start (_hbox, false, false, false); - // until sends have their own Pannable, don't show this - // because it controls the Route Pannable which confuses - // users (among others) - // _vbox.pack_start (_panners, false, false); + _vbox.pack_start (_panners, false, false); io = manage (new IOSelector (parent, session, s->output())); diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 26d2cf3acd..e4c39271f5 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -396,7 +396,7 @@ Delivery::reset_panner () if (_panshell) { _panshell->configure_io (ChanCount (DataType::AUDIO, pans_required()), ChanCount (DataType::AUDIO, pan_outs())); - if (_role == Main || _role == Aux) { + if (_role == Main || _role == Aux || _role == Send) { _panshell->pannable()->set_panner (_panshell->panner()); } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index ea67fe6073..a036c8feb0 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1069,7 +1069,8 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) } else if (node.name() == "Send") { - processor.reset (new Send (_session, _pannable, _mute_master)); + boost::shared_ptr sendpan (new Pannable (_session)); + processor.reset (new Send (_session, sendpan, _mute_master)); } else { @@ -2564,7 +2565,8 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "send") { - processor.reset (new Send (_session, _pannable, _mute_master)); + boost::shared_ptr sendpan (new Pannable (_session)); + processor.reset (new Send (_session, sendpan, _mute_master)); } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; From 1146d58d212423f67f19a490b6832d0496fa9e77 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 11:08:37 +0100 Subject: [PATCH 24/31] misc panning related UI tweaks: * connect 2D panner "edit" to big window * disconnect 2D-panner GUI when it's visible but panner-type changes * ignore mixer-strip level-meter context-menu for Aux-sends --- gtk2_ardour/mixer_strip.cc | 3 +++ gtk2_ardour/panner2d.cc | 8 ++++++++ gtk2_ardour/panner_ui.cc | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 3a3c39ea17..780e8078c6 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -2149,6 +2149,9 @@ MixerStrip::ab_plugins () bool MixerStrip::level_meter_button_press (GdkEventButton* ev) { + if (_current_delivery && boost::dynamic_pointer_cast(_current_delivery)) { + return false; + } if (ev->button == 3) { popup_level_meter_menu (ev); return true; diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index c59523abe3..9de8123ea8 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -201,6 +201,14 @@ void Panner2d::handle_state_change () { panconnect.drop_connections(); + if (!panner_shell->panner()) { + /* we should really self-destruct the UI here + * -> * PannerUI::set_panner() -> delete + */ + queue_draw (); + return; + } + panner_shell->panner()->SignalPositionChanged.connect (panconnect, invalidator(*this), boost::bind (&Panner2d::handle_position_change, this), gui_context()); set params = panner_shell->panner()->what_can_be_automated(); diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc index 3ee794e578..3602225c88 100644 --- a/gtk2_ardour/panner_ui.cc +++ b/gtk2_ardour/panner_ui.cc @@ -417,6 +417,11 @@ PannerUI::pan_edit () _mono_panner->edit (); } else if (_stereo_panner) { _stereo_panner->edit (); + } else if (twod_panner) { + if (!big_window) { + big_window = new Panner2dWindow (_panshell, 400, _panner->in().n_audio()); + } + big_window->show (); } } From 35aed0efab378af00031c6a75187c962f05d7bd7 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 11:08:51 +0100 Subject: [PATCH 25/31] ProcessorWindowProxy::processor_going_away takes care of deletion --- gtk2_ardour/port_insert_ui.cc | 11 ----------- gtk2_ardour/port_insert_ui.h | 1 - gtk2_ardour/return_ui.cc | 10 ---------- gtk2_ardour/return_ui.h | 1 - gtk2_ardour/send_ui.cc | 10 ---------- gtk2_ardour/send_ui.h | 1 - 6 files changed, 34 deletions(-) diff --git a/gtk2_ardour/port_insert_ui.cc b/gtk2_ardour/port_insert_ui.cc index 3c495f0e79..2a982eabc1 100644 --- a/gtk2_ardour/port_insert_ui.cc +++ b/gtk2_ardour/port_insert_ui.cc @@ -176,8 +176,6 @@ PortInsertWindow::PortInsertWindow (ARDOUR::Session* sess, boost::shared_ptrsignal_clicked().connect (sigc::mem_fun (*this, &PortInsertWindow::accept)); signal_delete_event().connect (sigc::mem_fun (*this, &PortInsertWindow::wm_delete), false); - - pi->DropReferences.connect (going_away_connection, invalidator (*this), boost::bind (&PortInsertWindow::plugin_going_away, this), gui_context()); } bool @@ -187,15 +185,6 @@ PortInsertWindow::wm_delete (GdkEventAny* /*event*/) return false; } -void -PortInsertWindow::plugin_going_away () -{ - ENSURE_GUI_THREAD (*this, &PortInsertWindow::plugin_going_away) - - going_away_connection.disconnect (); - delete_when_idle (this); -} - void PortInsertWindow::on_map () { diff --git a/gtk2_ardour/port_insert_ui.h b/gtk2_ardour/port_insert_ui.h index d130ea16be..75a4ad17c0 100644 --- a/gtk2_ardour/port_insert_ui.h +++ b/gtk2_ardour/port_insert_ui.h @@ -67,7 +67,6 @@ class PortInsertWindow : public ArdourDialog void cancel (); void accept (); - void plugin_going_away (); PBD::ScopedConnection going_away_connection; bool wm_delete (GdkEventAny*); diff --git a/gtk2_ardour/return_ui.cc b/gtk2_ardour/return_ui.cc index 92846af469..29a8ddc16c 100644 --- a/gtk2_ardour/return_ui.cc +++ b/gtk2_ardour/return_ui.cc @@ -110,19 +110,9 @@ ReturnUIWindow::ReturnUIWindow (boost::shared_ptr r, ARDOUR::Session* s) set_name ("ReturnUIWindow"); - r->DropReferences.connect (going_away_connection, invalidator (*this), boost::bind (&ReturnUIWindow::return_going_away, this), gui_context()); } ReturnUIWindow::~ReturnUIWindow () { delete ui; } - -void -ReturnUIWindow::return_going_away () -{ - ENSURE_GUI_THREAD (*this, &ReturnUIWindow::return_going_away) - going_away_connection.disconnect (); - delete_when_idle (this); -} - diff --git a/gtk2_ardour/return_ui.h b/gtk2_ardour/return_ui.h index ab10e1f566..353985110d 100644 --- a/gtk2_ardour/return_ui.h +++ b/gtk2_ardour/return_ui.h @@ -68,7 +68,6 @@ class ReturnUIWindow : public ArdourWindow private: Gtk::HBox hpacker; - void return_going_away (); PBD::ScopedConnection going_away_connection; }; diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc index 54663dbe0a..90ee52b3ad 100644 --- a/gtk2_ardour/send_ui.cc +++ b/gtk2_ardour/send_ui.cc @@ -133,19 +133,9 @@ SendUIWindow::SendUIWindow (boost::shared_ptr s, Session* session) ui->show (); hpacker.show (); - s->DropReferences.connect (going_away_connection, invalidator (*this), boost::bind (&SendUIWindow::send_going_away, this), gui_context()); } SendUIWindow::~SendUIWindow () { delete ui; } - -void -SendUIWindow::send_going_away () -{ - ENSURE_GUI_THREAD (*this, &SendUIWindow::send_going_away) - going_away_connection.disconnect (); - delete_when_idle (this); -} - diff --git a/gtk2_ardour/send_ui.h b/gtk2_ardour/send_ui.h index 9f423bd6fe..731f079790 100644 --- a/gtk2_ardour/send_ui.h +++ b/gtk2_ardour/send_ui.h @@ -69,7 +69,6 @@ class SendUIWindow : public ArdourWindow private: Gtk::HBox hpacker; - void send_going_away (); PBD::ScopedConnection going_away_connection; }; From 346310c60b2cb9bca85fa04575dc56717762733e Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 11:49:09 +0100 Subject: [PATCH 26/31] fix delivery: check if panshell exists on session-load --- libs/ardour/delivery.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index e4c39271f5..11500b4b10 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -362,7 +362,7 @@ Delivery::set_state (const XMLNode& node, int version) reset_panner (); XMLNode* pannnode = node.child (X_("Pannable")); - if (_panshell->panner() && pannnode) { + if (_panshell && _panshell->panner() && pannnode) { _panshell->pannable()->set_state (*pannnode, version); } From 9c595f8131967541808ae77bae842bd905db9caa Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 12:02:00 +0100 Subject: [PATCH 27/31] delete 'big' 2d panner window, if panner changes to mono|stereo --- gtk2_ardour/panner2d.cc | 4 ---- gtk2_ardour/panner_ui.cc | 7 +++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 9de8123ea8..6c37de164b 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -202,10 +202,6 @@ Panner2d::handle_state_change () { panconnect.drop_connections(); if (!panner_shell->panner()) { - /* we should really self-destruct the UI here - * -> * PannerUI::set_panner() -> delete - */ - queue_draw (); return; } diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc index 3602225c88..ab4279a154 100644 --- a/gtk2_ardour/panner_ui.cc +++ b/gtk2_ardour/panner_ui.cc @@ -231,11 +231,16 @@ PannerUI::setup_pan () _mono_panner = 0; if (!_panner) { + delete big_window; + big_window = 0; return; } if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_2in2out#ui") { + delete big_window; + big_window = 0; + boost::shared_ptr pannable = _panner->pannable(); _stereo_panner = new StereoPanner (_panshell); @@ -260,6 +265,8 @@ PannerUI::setup_pan () else if (_panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_1in2out#ui" || _panshell->panner_gui_uri() == "http://ardour.org/plugin/panner_balance#ui") { + delete big_window; + big_window = 0; boost::shared_ptr pannable = _panner->pannable(); boost::shared_ptr ac = pannable->pan_azimuth_control; From 291618fe7116424448da5f7029e567d655526521 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 15:13:37 +0100 Subject: [PATCH 28/31] allow to custom select panner-type for each delivery. --- gtk2_ardour/mixer_strip.cc | 29 ++++++++--------------------- gtk2_ardour/panner_ui.cc | 9 +++------ gtk2_ardour/panner_ui.h | 4 ++-- gtk2_ardour/send_ui.cc | 12 ++++++++++++ libs/ardour/ardour/panner_manager.h | 5 ++++- libs/ardour/ardour/panner_shell.h | 3 +++ libs/ardour/delivery.cc | 1 + libs/ardour/panner_manager.cc | 26 ++++++++++++++++++++++++-- libs/ardour/panner_shell.cc | 20 ++++++++++++++++++++ libs/ardour/route.cc | 5 +++++ 10 files changed, 82 insertions(+), 32 deletions(-) diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 780e8078c6..830d2bf161 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -1041,29 +1041,13 @@ MixerStrip::update_panner_choices () ENSURE_GUI_THREAD (*this, &MixerStrip::update_panner_choices) if (!_route->panner_shell()) { return; } - int in = _route->output()->n_ports().n_audio(); - int out = in; - + uint32_t in = _route->output()->n_ports().n_audio(); + uint32_t out = in; if (_route->panner()) { in = _route->panner()->in().n_audio(); } - if (out < 2 || in == 0) { - panners.set_available_panners(_route, std::map()); - return; - } - - std::map panner_list; - std::list panner_info = PannerManager::instance().panner_info; - /* get available panners for current configuration. */ - for (list::iterator p = panner_info.begin(); p != panner_info.end(); ++p) { - PanPluginDescriptor* d = &(*p)->descriptor; - if (d->in != -1 && d->in != in) continue; - if (d->out != -1 && d->out != out) continue; - if (d->in == -1 && d->out == -1 && out <= 2) continue; - panner_list.insert(std::pair(d->panner_uri,d->name)); - } - panners.set_available_panners(_route, panner_list); + panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out)); } /* @@ -1891,10 +1875,13 @@ MixerStrip::show_send (boost::shared_ptr send) gain_meter().set_controls (_route, send->meter(), send->amp()); gain_meter().setup_meters (); + uint32_t const in = _current_delivery->pans_required(); + uint32_t const out = _current_delivery->pan_outs(); + panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner()); - panner_ui().set_available_panners(boost::shared_ptr(), std::map()); + panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out)); panner_ui().setup_pan (); - panners.show_all (); + panner_ui().show_all (); input_button.set_sensitive (false); group_button.set_sensitive (false); diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc index ab4279a154..2a6568992b 100644 --- a/gtk2_ardour/panner_ui.cc +++ b/gtk2_ardour/panner_ui.cc @@ -388,7 +388,7 @@ PannerUI::build_pan_menu () items.push_back (MenuElem (_("Edit..."), sigc::mem_fun (*this, &PannerUI::pan_edit))); } - if (_route && _panner_list.size() > 1 && !_panshell->bypassed()) { + if (_panner_list.size() > 1 && !_panshell->bypassed()) { RadioMenuItem::Group group; items.push_back (SeparatorElem()); @@ -444,9 +444,7 @@ PannerUI::pan_reset () void PannerUI::pan_set_custom_type (std::string uri) { if (_suspend_menu_callbacks) return; - if (_route) { - _route->set_custom_panner_uri(uri); - } + _panshell->select_panner_by_uri(uri); } void @@ -644,8 +642,7 @@ PannerUI::position_adjusted () } void -PannerUI::set_available_panners(boost::shared_ptr r, std::map p) +PannerUI::set_available_panners(std::map p) { - _route = r; _panner_list = p; } diff --git a/gtk2_ardour/panner_ui.h b/gtk2_ardour/panner_ui.h index dca24451d1..9b349d664f 100644 --- a/gtk2_ardour/panner_ui.h +++ b/gtk2_ardour/panner_ui.h @@ -73,7 +73,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr void set_width (Width); void setup_pan (); - void set_available_panners(boost::shared_ptr, std::map); + void set_available_panners(std::map); void effective_pan_display (); @@ -85,6 +85,7 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr private: friend class MixerStrip; + friend class SendUI; boost::shared_ptr _panshell; boost::shared_ptr _panner; @@ -161,7 +162,6 @@ class PannerUI : public Gtk::HBox, public ARDOUR::SessionHandlePtr void start_touch (boost::weak_ptr); void stop_touch (boost::weak_ptr); - boost::shared_ptr _route; std::map _panner_list; bool _suspend_menu_callbacks; }; diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc index 90ee52b3ad..cdd8673da9 100644 --- a/gtk2_ardour/send_ui.cc +++ b/gtk2_ardour/send_ui.cc @@ -20,6 +20,7 @@ #include #include "ardour/io.h" +#include "ardour/panner_manager.h" #include "ardour/send.h" #include "ardour/rc_configuration.h" @@ -70,7 +71,11 @@ SendUI::SendUI (Gtk::Window* parent, boost::shared_ptr s, Session* session _send->output()->changed.connect (connections, invalidator (*this), boost::bind (&SendUI::outs_changed, this, _1, _2), gui_context()); + uint32_t const in = _send->pans_required(); + uint32_t const out = _send->pan_outs(); + _panners.set_width (Wide); + _panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out)); _panners.setup_pan (); _gpm.setup_meters (); @@ -97,7 +102,14 @@ SendUI::outs_changed (IOChange change, void* /*ignored*/) { ENSURE_GUI_THREAD (*this, &SendUI::outs_changed, change, ignored) if (change.type & IOChange::ConfigurationChanged) { + uint32_t const in = _send->pans_required(); + uint32_t const out = _send->pan_outs(); + if (_panners._panner == 0) { + _panners.set_panner (_send->panner_shell(), _send->panner()); + } + _panners.set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out)); _panners.setup_pan (); + _panners.show_all (); _gpm.setup_meters (); } } diff --git a/libs/ardour/ardour/panner_manager.h b/libs/ardour/ardour/panner_manager.h index 7b52c65c0a..4a29e1c36b 100644 --- a/libs/ardour/ardour/panner_manager.h +++ b/libs/ardour/ardour/panner_manager.h @@ -26,6 +26,8 @@ namespace ARDOUR { +typedef std::map PannerUriMap; + struct PannerInfo { PanPluginDescriptor descriptor; void* module; @@ -50,7 +52,8 @@ public: std::list panner_info; PannerInfo* select_panner (ChanCount in, ChanCount out, std::string const uri = ""); - PannerInfo* get_by_uri (std::string uri); + PannerInfo* get_by_uri (std::string uri) const; + PannerUriMap get_available_panners(uint32_t const a_in, uint32_t const a_out) const; private: PannerManager(); diff --git a/libs/ardour/ardour/panner_shell.h b/libs/ardour/ardour/panner_shell.h index f798e0da97..765acad16a 100644 --- a/libs/ardour/ardour/panner_shell.h +++ b/libs/ardour/ardour/panner_shell.h @@ -75,6 +75,9 @@ public: std::string user_selected_panner_uri() const { return _user_selected_panner_uri; } std::string panner_gui_uri() const { return _panner_gui_uri; } + /* this function takes the process lock */ + bool select_panner_by_uri (std::string const uri); + private: friend class Route; void distribute_no_automation (BufferSet& src, BufferSet& dest, pframes_t nframes, gain_t gain_coeff); diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 11500b4b10..8e636ed861 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -364,6 +364,7 @@ Delivery::set_state (const XMLNode& node, int version) XMLNode* pannnode = node.child (X_("Pannable")); if (_panshell && _panshell->panner() && pannnode) { _panshell->pannable()->set_state (*pannnode, version); + _panshell->pannable()->set_panner(_panshell->panner()); } return 0; diff --git a/libs/ardour/panner_manager.cc b/libs/ardour/panner_manager.cc index 24fa10e225..ec5b675731 100644 --- a/libs/ardour/panner_manager.cc +++ b/libs/ardour/panner_manager.cc @@ -218,13 +218,35 @@ PannerManager::select_panner (ChanCount in, ChanCount out, std::string const uri } PannerInfo* -PannerManager::get_by_uri (std::string uri) +PannerManager::get_by_uri (std::string uri) const { PannerInfo* pi = NULL; - for (list::iterator p = panner_info.begin(); p != panner_info.end(); ++p) { + for (list::const_iterator p = panner_info.begin(); p != panner_info.end(); ++p) { if ((*p)->descriptor.panner_uri != uri) continue; pi = (*p); break; } return pi; } + +PannerUriMap +PannerManager::get_available_panners(uint32_t const a_in, uint32_t const a_out) const +{ + int const in = a_in; + int const out = a_out; + PannerUriMap panner_list; + + if (out < 2 || in == 0) { + return panner_list; + } + + /* get available panners for current configuration. */ + for (list::const_iterator p = panner_info.begin(); p != panner_info.end(); ++p) { + PanPluginDescriptor* d = &(*p)->descriptor; + if (d->in != -1 && d->in != in) continue; + if (d->out != -1 && d->out != out) continue; + if (d->in == -1 && d->out == -1 && out <= 2) continue; + panner_list.insert(std::pair(d->panner_uri,d->name)); + } + return panner_list; +} diff --git a/libs/ardour/panner_shell.cc b/libs/ardour/panner_shell.cc index a25cb49ab5..ccb8aa8e87 100644 --- a/libs/ardour/panner_shell.cc +++ b/libs/ardour/panner_shell.cc @@ -43,8 +43,10 @@ #include "evoral/Curve.hpp" #include "ardour/audio_buffer.h" +#include "ardour/audioengine.h" #include "ardour/buffer_set.h" #include "ardour/debug.h" +#include "ardour/pannable.h" #include "ardour/panner.h" #include "ardour/panner_manager.h" #include "ardour/panner_shell.h" @@ -393,3 +395,21 @@ PannerShell::set_user_selected_panner_uri (std::string const uri) _force_reselect = true; return true; } + +bool +PannerShell::select_panner_by_uri (std::string const uri) +{ + if (uri == _user_selected_panner_uri) return false; + _user_selected_panner_uri = uri; + if (uri == _current_panner_uri) return false; + _force_reselect = true; + if (_panner) { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + ChanCount in = _panner->in(); + ChanCount out = _panner->out(); + configure_io(in, out); + pannable()->set_panner(_panner); + _session.set_dirty (); + } + return true; +} diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index a036c8feb0..ccfe38e9e1 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1568,6 +1568,10 @@ Route::remove_processors (const ProcessorList& to_be_deleted, ProcessorStreams* return 0; } +#if 0 +/* currently unused (again) -- but will come in handy soon (again) + * once there is an option to link route + delivery panner settings + */ void Route::set_custom_panner_uri (std::string const panner_uri) { @@ -1619,6 +1623,7 @@ Route::set_custom_panner_uri (std::string const panner_uri) processors_changed (RouteProcessorChange ()); /* EMIT SIGNAL */ _session.set_dirty (); } +#endif void Route::reset_instrument_info () From 5b0c90299785d46357a70cc7625c9b3ee87486f1 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 19:16:10 +0100 Subject: [PATCH 29/31] center VBAP UI crosshair --- gtk2_ardour/panner2d.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gtk2_ardour/panner2d.cc b/gtk2_ardour/panner2d.cc index 6c37de164b..51c0112397 100644 --- a/gtk2_ardour/panner2d.cc +++ b/gtk2_ardour/panner2d.cc @@ -426,14 +426,14 @@ Panner2d::on_expose_event (GdkEventExpose *event) /* horizontal line of "crosshairs" */ cairo_set_source_rgba (cr, 0.282, 0.517, 0.662, 1.0); - cairo_move_to (cr, 0.0, rint(radius) - .5); - cairo_line_to (cr, diameter, rint(radius) - .5); + cairo_move_to (cr, 0.0, rint(radius) + .5); + cairo_line_to (cr, diameter, rint(radius) + .5); cairo_stroke (cr); /* vertical line of "crosshairs" */ - cairo_move_to (cr, rint(radius) - .5, 0); - cairo_line_to (cr, rint(radius) - .5, diameter); + cairo_move_to (cr, rint(radius) + .5, 0); + cairo_line_to (cr, rint(radius) + .5, diameter); cairo_stroke (cr); /* the circle on which signals live */ From e45151b89c64912077c03fc979f1581780ac9e27 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 23:21:30 +0100 Subject: [PATCH 30/31] first stab at send+route panner link --- gtk2_ardour/ardour.menus.in | 1 + gtk2_ardour/processor_box.cc | 54 +++++++++++++++++++--- gtk2_ardour/processor_box.h | 2 + libs/ardour/ardour/panner_shell.h | 18 ++++++-- libs/ardour/ardour/rc_configuration_vars.h | 2 + libs/ardour/delivery.cc | 8 +++- libs/ardour/panner_shell.cc | 52 ++++++++++++++++++--- libs/ardour/route.cc | 11 ++--- 8 files changed, 121 insertions(+), 27 deletions(-) diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in index a354df603d..b6328963bb 100644 --- a/gtk2_ardour/ardour.menus.in +++ b/gtk2_ardour/ardour.menus.in @@ -551,6 +551,7 @@ + diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index 98bb3f13cd..c2421dcabf 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -45,8 +45,8 @@ #include "ardour/audioengine.h" #include "ardour/internal_return.h" #include "ardour/internal_send.h" +#include "ardour/panner_shell.h" #include "ardour/plugin_insert.h" -#include "ardour/pannable.h" #include "ardour/port_insert.h" #include "ardour/profile.h" #include "ardour/return.h" @@ -456,6 +456,34 @@ ProcessorEntry::toggle_control_visibility (Control* c) _parent->update_gui_object_state (this); } +Menu * +ProcessorEntry::build_send_options_menu () +{ + using namespace Menu_Helpers; + Menu* menu = manage (new Menu); + MenuList& items = menu->items (); + + boost::shared_ptr send = boost::dynamic_pointer_cast (_processor); + if (send) { + + items.push_back (CheckMenuElem (_("Link panner controls"))); + CheckMenuItem* c = dynamic_cast (&items.back ()); + c->set_active (send->panner_shell()->is_linked_to_route()); + c->signal_toggled().connect (sigc::mem_fun (*this, &ProcessorEntry::toggle_panner_link)); + + } + return menu; +} + +void +ProcessorEntry::toggle_panner_link () +{ + boost::shared_ptr send = boost::dynamic_pointer_cast (_processor); + if (send) { + send->panner_shell()->set_linked_to_route(!send->panner_shell()->is_linked_to_route()); + } +} + ProcessorEntry::Control::Control (boost::shared_ptr c, string const & n) : _control (c) , _adjustment (gain_to_slider_position_with_max (1.0, Config->get_max_gain()), 0, 1, 0.01, 0.1) @@ -1092,6 +1120,20 @@ ProcessorBox::show_processor_menu (int arg) } } + Gtk::MenuItem* send_menu_item = dynamic_cast(ActionManager::get_widget("/ProcessorMenu/send_options")); + if (send_menu_item) { + if (single_selection) { + Menu* m = single_selection->build_send_options_menu (); + if (m && !m->items().empty()) { + send_menu_item->set_submenu (*m); + send_menu_item->set_sensitive (true); + } else { + gtk_menu_item_set_submenu (send_menu_item->gobj(), 0); + send_menu_item->set_sensitive (false); + } + } + } + /* Sensitise actions as approprioate */ cut_action->set_sensitive (can_cut()); @@ -1403,8 +1445,7 @@ ProcessorBox::choose_insert () void ProcessorBox::choose_send () { - boost::shared_ptr sendpan(new Pannable (*_session)); - boost::shared_ptr send (new Send (*_session, sendpan, _route->mute_master())); + boost::shared_ptr send (new Send (*_session, _route->pannable(), _route->mute_master())); /* make an educated guess at the initial number of outputs for the send */ ChanCount outs = (_session->master_out()) @@ -2053,9 +2094,8 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr continue; } - boost::shared_ptr sendpan(new Pannable (*_session)); XMLNode n (**niter); - InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(), + InternalSend* s = new InternalSend (*_session, _route->pannable(), _route->mute_master(), boost::shared_ptr(), Delivery::Aux); IOProcessor::prepare_for_reset (n, s->name()); @@ -2069,9 +2109,8 @@ ProcessorBox::paste_processor_state (const XMLNodeList& nlist, boost::shared_ptr } else if (type->value() == "send") { - boost::shared_ptr sendpan(new Pannable (*_session)); XMLNode n (**niter); - Send* s = new Send (*_session, sendpan, _route->mute_master()); + Send* s = new Send (*_session, _route->pannable(), _route->mute_master()); IOProcessor::prepare_for_reset (n, s->name()); @@ -2404,6 +2443,7 @@ ProcessorBox::register_actions () ActionManager::register_action (popup_act_grp, X_("newaux"), _("New Aux Send ...")); ActionManager::register_action (popup_act_grp, X_("controls"), _("Controls")); + ActionManager::register_action (popup_act_grp, X_("send_options"), _("Send Options")); ActionManager::register_action (popup_act_grp, X_("clear"), _("Clear (all)"), sigc::ptr_fun (ProcessorBox::rb_clear)); diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index a66a6c4a56..021e557d36 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -138,6 +138,7 @@ public: void set_control_state (XMLNode const *); std::string state_id () const; Gtk::Menu* build_controls_menu (); + Gtk::Menu* build_send_options_menu (); protected: ArdourButton _button; @@ -205,6 +206,7 @@ private: std::list _controls; void toggle_control_visibility (Control *); + void toggle_panner_link (); class PortIcon : public Gtk::DrawingArea { public: diff --git a/libs/ardour/ardour/panner_shell.h b/libs/ardour/ardour/panner_shell.h index 765acad16a..60db264b4d 100644 --- a/libs/ardour/ardour/panner_shell.h +++ b/libs/ardour/ardour/panner_shell.h @@ -49,7 +49,7 @@ class Pannable; class PannerShell : public SessionObject { public: - PannerShell (std::string name, Session&, boost::shared_ptr); + PannerShell (std::string name, Session&, boost::shared_ptr, bool is_send = false); virtual ~PannerShell (); std::string describe_parameter (Evoral::Parameter param); @@ -63,19 +63,25 @@ public: XMLNode& get_state (); int set_state (const XMLNode&, int version); + PBD::Signal0 PannableChanged; /* Pannable changed -- l*/ PBD::Signal0 Changed; /* panner and/or outputs count and/or bypass state changed */ boost::shared_ptr panner() const { return _panner; } - boost::shared_ptr pannable() const { return _pannable; } + boost::shared_ptr pannable() const { return _panlinked ? _pannable_route : _pannable_internal; } bool bypassed () const; void set_bypassed (bool); + bool is_send () const { return (_is_send); } + bool is_linked_to_route () const { return (_is_send && _panlinked); } + /* this function takes the process lock: */ + void set_linked_to_route (bool); + std::string current_panner_uri() const { return _current_panner_uri; } std::string user_selected_panner_uri() const { return _user_selected_panner_uri; } std::string panner_gui_uri() const { return _panner_gui_uri; } - /* this function takes the process lock */ + /* this function takes the process lock: */ bool select_panner_by_uri (std::string const uri); private: @@ -84,7 +90,11 @@ public: bool set_user_selected_panner_uri (std::string const uri); boost::shared_ptr _panner; - boost::shared_ptr _pannable; + + boost::shared_ptr _pannable_internal; + boost::shared_ptr _pannable_route; + bool _is_send; + bool _panlinked; bool _bypassed; std::string _current_panner_uri; diff --git a/libs/ardour/ardour/rc_configuration_vars.h b/libs/ardour/ardour/rc_configuration_vars.h index e1f8b42914..b92d0e152a 100644 --- a/libs/ardour/ardour/rc_configuration_vars.h +++ b/libs/ardour/ardour/rc_configuration_vars.h @@ -113,6 +113,8 @@ CONFIG_VARIABLE (gain_t, solo_mute_gain, "solo-mute-gain", 0.0) CONFIG_VARIABLE (std::string, monitor_bus_preferred_bundle, "monitor-bus-preferred-bundle", "") CONFIG_VARIABLE (bool, quieten_at_speed, "quieten-at-speed", true) +CONFIG_VARIABLE (bool, link_send_and_route_panner, "link-send-and-route-panner", true) + /* click */ CONFIG_VARIABLE (bool, clicking, "clicking", false) diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 8e636ed861..b4b9831d97 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -58,7 +58,9 @@ Delivery::Delivery (Session& s, boost::shared_ptr io, boost::shared_ptr(new PannerShell (_name, _session, pannable)); + bool is_send = false; + if (r & (Delivery::Send|Delivery::Aux)) is_send = true; + _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable, is_send)); } _display_to_user = false; @@ -80,7 +82,9 @@ Delivery::Delivery (Session& s, boost::shared_ptr pannable, boost::sha , _no_panner_reset (false) { if (pannable) { - _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable)); + bool is_send = false; + if (r & (Delivery::Send|Delivery::Aux)) is_send = true; + _panshell = boost::shared_ptr(new PannerShell (_name, _session, pannable, is_send)); } _display_to_user = false; diff --git a/libs/ardour/panner_shell.cc b/libs/ardour/panner_shell.cc index ccb8aa8e87..b489354e21 100644 --- a/libs/ardour/panner_shell.cc +++ b/libs/ardour/panner_shell.cc @@ -61,21 +61,31 @@ using namespace std; using namespace ARDOUR; using namespace PBD; -PannerShell::PannerShell (string name, Session& s, boost::shared_ptr p) +PannerShell::PannerShell (string name, Session& s, boost::shared_ptr p, bool is_send) : SessionObject (s, name) - , _pannable (p) + , _pannable_route (p) + , _is_send (is_send) + , _panlinked (true) , _bypassed (false) , _current_panner_uri("") , _user_selected_panner_uri("") , _panner_gui_uri("") , _force_reselect (false) { + if (is_send) { + _pannable_internal.reset(new Pannable (s)); + if (Config->get_link_send_and_route_panner()) { + _panlinked = true; + } else { + _panlinked = false; + } + } set_name (name); } PannerShell::~PannerShell () { - DEBUG_TRACE(DEBUG::Destruction, string_compose ("panner shell %3 for %1 destructor, panner is %4, pannable is %2\n", _name, _pannable, this, _panner)); + DEBUG_TRACE(DEBUG::Destruction, string_compose ("panner shell %3 for %1 destructor, panner is %4, pannable is %2\n", _name, _pannable_route, this, _panner)); } void @@ -122,7 +132,8 @@ PannerShell::configure_io (ChanCount in, ChanCount out) speakers.reset (s); } - Panner* p = pi->descriptor.factory (_pannable, speakers); + /* TODO don't allow to link _is_send if internal & route panners are different types */ + Panner* p = pi->descriptor.factory (pannable(), speakers); // boost_debug_shared_ptr_mark_interesting (p, "Panner"); _panner.reset (p); _panner->configure_io (in, out); @@ -139,8 +150,9 @@ PannerShell::get_state () node->add_property (X_("bypassed"), _bypassed ? X_("yes") : X_("no")); node->add_property (X_("user-panner"), _user_selected_panner_uri); + node->add_property (X_("linked-to-route"), _panlinked ? X_("yes") : X_("no")); - if (_panner) { + if (_panner && _is_send) { node->add_child_nocopy (_panner->get_state ()); } @@ -159,6 +171,10 @@ PannerShell::set_state (const XMLNode& node, int version) set_bypassed (string_is_affirmative (prop->value ())); } + if ((prop = node.property (X_("linked-to-route"))) != 0) { + _panlinked = string_is_affirmative (prop->value ()); + } + if ((prop = node.property (X_("user-panner"))) != 0) { _user_selected_panner_uri = prop->value (); } @@ -172,7 +188,8 @@ PannerShell::set_state (const XMLNode& node, int version) if ((prop = (*niter)->property (X_("uri")))) { PannerInfo* p = PannerManager::instance().get_by_uri(prop->value()); if (p) { - _panner.reset (p->descriptor.factory (_pannable, _session.get_speakers ())); + _panner.reset (p->descriptor.factory ( + _is_send ? _pannable_internal : _pannable_route, _session.get_speakers ())); _current_panner_uri = p->descriptor.panner_uri; _panner_gui_uri = p->descriptor.gui_uri; if (_panner->set_state (**niter, version) == 0) { @@ -195,7 +212,8 @@ PannerShell::set_state (const XMLNode& node, int version) assumption, but it's still an assumption. */ - _panner.reset ((*p)->descriptor.factory (_pannable, _session.get_speakers ())); + _panner.reset ((*p)->descriptor.factory ( + _is_send ? _pannable_internal : _pannable_route, _session.get_speakers ())); _current_panner_uri = (*p)->descriptor.panner_uri; _panner_gui_uri = (*p)->descriptor.gui_uri; @@ -413,3 +431,23 @@ PannerShell::select_panner_by_uri (std::string const uri) } return true; } + +void +PannerShell::set_linked_to_route (bool onoff) +{ + if (!_is_send || onoff == _panlinked) { + return; + } + _panlinked = onoff; + + _force_reselect = true; + if (_panner) { + Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ()); + ChanCount in = _panner->in(); + ChanCount out = _panner->out(); + configure_io(in, out); + pannable()->set_panner(_panner); + _session.set_dirty (); + } + PannableChanged(); +} diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index ccfe38e9e1..a2176e8d45 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -1069,8 +1069,7 @@ Route::add_processor_from_xml_2X (const XMLNode& node, int version) } else if (node.name() == "Send") { - boost::shared_ptr sendpan (new Pannable (_session)); - processor.reset (new Send (_session, sendpan, _mute_master)); + processor.reset (new Send (_session, _pannable, _mute_master)); } else { @@ -2553,8 +2552,7 @@ Route::set_processor_state (const XMLNode& node) if (prop->value() == "intsend") { - boost::shared_ptr sendpan (new Pannable (_session)); - processor.reset (new InternalSend (_session, sendpan, _mute_master, boost::shared_ptr(), Delivery::Role (0))); + processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr(), Delivery::Aux)); } else if (prop->value() == "ladspa" || prop->value() == "Ladspa" || prop->value() == "lv2" || @@ -2571,7 +2569,7 @@ Route::set_processor_state (const XMLNode& node) } else if (prop->value() == "send") { boost::shared_ptr sendpan (new Pannable (_session)); - processor.reset (new Send (_session, sendpan, _mute_master)); + processor.reset (new Send (_session, _pannable, _mute_master)); } else { error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg; @@ -2761,8 +2759,7 @@ Route::add_aux_send (boost::shared_ptr route, boost::shared_ptrprocess_lock ()); - boost::shared_ptr sendpan (new Pannable (_session)); - listener.reset (new InternalSend (_session, sendpan, _mute_master, route, Delivery::Aux)); + listener.reset (new InternalSend (_session, _pannable, _mute_master, route, Delivery::Aux)); } add_processor (listener, before); From d9296b71237d9d3058b0796eda25258fadd45ad9 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Mon, 13 Jan 2014 23:22:02 +0100 Subject: [PATCH 31/31] add preference -- link-send-and-route-panner default --- gtk2_ardour/rc_option_editor.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/gtk2_ardour/rc_option_editor.cc b/gtk2_ardour/rc_option_editor.cc index b514a75a80..4085ffa934 100644 --- a/gtk2_ardour/rc_option_editor.cc +++ b/gtk2_ardour/rc_option_editor.cc @@ -1591,6 +1591,8 @@ RCOptionEditor::RCOptionEditor () /* SOLO AND MUTE */ + add_option (_("Solo / mute"), new OptionEditorHeading (_("Solo"))); + add_option (_("Solo / mute"), new FaderOption ( "solo-mute-gain", @@ -1704,6 +1706,16 @@ RCOptionEditor::RCOptionEditor () sigc::mem_fun (*_rc_config, &RCConfiguration::set_mute_affects_main_outs) )); + add_option (_("Solo / mute"), new OptionEditorHeading (_("Send Routing"))); + + add_option (_("Solo / mute"), + new BoolOption ( + "link-send-and-route-panner", + _("Link panners of Aux and External Sends with main panner by default"), + sigc::mem_fun (*_rc_config, &RCConfiguration::get_link_send_and_route_panner), + sigc::mem_fun (*_rc_config, &RCConfiguration::set_link_send_and_route_panner) + )); + add_option (_("MIDI"), new BoolOption ( "send-midi-clock",