2 This software is being provided to you, the licensee, by Ville Pulkki,
3 under the following license. By obtaining, using and/or copying this
4 software, you agree that you have read, understood, and will comply
5 with these terms and conditions: Permission to use, copy, modify and
6 distribute, including the right to grant others rights to distribute
7 at any tier, this software and its documentation for any purpose and
8 without fee or royalty is hereby granted, provided that you agree to
9 comply with the following copyright notice and statements, including
10 the disclaimer, and that the same appear on ALL copies of the software
11 and documentation, including modifications that you make for internal
12 use or for distribution:
14 Copyright 1998 by Ville Pulkki, Helsinki University of Technology. All
17 The software may be used, distributed, and included to commercial
18 products without any charges. When included to a commercial product,
19 the method "Vector Base Amplitude Panning" and its developer Ville
20 Pulkki must be referred to in documentation.
22 This software is provided "as is", and Ville Pulkki or Helsinki
23 University of Technology make no representations or warranties,
24 expressed or implied. By way of example, but not limitation, Helsinki
25 University of Technology or Ville Pulkki make no representations or
26 warranties of merchantability or fitness for any particular purpose or
27 that the use of the licensed software or documentation will not
28 infringe any third party patents, copyrights, trademarks or other
29 rights. The name of Ville Pulkki or Helsinki University of Technology
30 may not be used in advertising or publicity pertaining to distribution
35 #pragma warning ( disable : 4244 )
43 #include "pbd/cartesian.h"
45 #include "vbap_speakers.h"
47 using namespace ARDOUR;
51 const double VBAPSpeakers::MIN_VOL_P_SIDE_LGTH = 0.01;
53 typedef std::vector<double> DoubleVector;
54 typedef std::vector<float> FloatVector;
55 typedef std::vector<bool> BoolVector;
56 typedef std::vector<int> IntVector;
57 typedef std::vector<IntVector> IntVector2D;
58 typedef std::vector<DoubleVector> DoubleVector2D;
60 VBAPSpeakers::VBAPSpeakers (boost::shared_ptr<Speakers> s)
64 _parent->Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
68 VBAPSpeakers::~VBAPSpeakers ()
73 VBAPSpeakers::update ()
77 _speakers = _parent->speakers();
79 for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
80 if ((*i).angles().ele != 0.0) {
88 if (_speakers.size() < 2) {
89 /* nothing to be done with less than two speakers */
93 if (_dimension == 3) {
94 ls_triplet_chain *ls_triplets = 0;
95 choose_speaker_triplets (&ls_triplets);
97 calculate_3x3_matrixes (ls_triplets);
101 choose_speaker_pairs ();
106 VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
108 /* Selects the loudspeaker triplets, and
109 calculates the inversion matrices for each selected triplet.
110 A line (connection) is drawn between each loudspeaker. The lines
111 denote the sides of the triangles. The triangles should not be
112 intersecting. All crossing connections are searched and the
113 longer connection is erased. This yields non-intesecting triangles,
114 which can be used in panning.
117 int i,j,k,l,table_size;
118 int n_speakers = _speakers.size ();
120 if (n_speakers < 1) {
124 FloatVector distance_table(((n_speakers * (n_speakers - 1)) / 2));
125 IntVector distance_table_i(((n_speakers * (n_speakers - 1)) / 2));
126 IntVector distance_table_j(((n_speakers * (n_speakers - 1)) / 2));
127 IntVector2D connections(n_speakers, IntVector(n_speakers));
129 struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
131 for (i = 0; i < n_speakers; i++) {
132 for (j = i+1; j < n_speakers; j++) {
133 for(k=j+1;k<n_speakers;k++) {
134 if (vol_p_side_lgth(i,j, k, _speakers) > MIN_VOL_P_SIDE_LGTH){
141 add_ldsp_triplet(i,j,k,ls_triplets);
147 /*calculate distancies between all speakers and sorting them*/
148 table_size =(((n_speakers - 1) * (n_speakers)) / 2);
149 for (i = 0; i < table_size; i++) {
150 distance_table[i] = 100000.0;
153 for (i = 0;i < n_speakers; i++) {
154 for (j = i+1; j < n_speakers; j++) {
155 if (connections[i][j] == 1) {
156 distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
158 while(distance_table[k] < distance) {
161 for (l = table_size - 1; l > k ; l--) {
162 distance_table[l] = distance_table[l-1];
163 distance_table_i[l] = distance_table_i[l-1];
164 distance_table_j[l] = distance_table_j[l-1];
166 distance_table[k] = distance;
167 distance_table_i[k] = i;
168 distance_table_j[k] = j;
174 /* disconnecting connections which are crossing shorter ones,
175 starting from shortest one and removing all that cross it,
176 and proceeding to next shortest */
177 for (i = 0; i < table_size; i++) {
178 int fst_ls = distance_table_i[i];
179 int sec_ls = distance_table_j[i];
180 if (connections[fst_ls][sec_ls] == 1) {
181 for (j = 0; j < n_speakers; j++) {
182 for (k = j+1; k < n_speakers; k++) {
183 if ((j!=fst_ls) && (k != sec_ls) && (k!=fst_ls) && (j != sec_ls)){
184 if (lines_intersect(fst_ls, sec_ls, j,k) == 1){
185 connections[j][k] = 0;
186 connections[k][j] = 0;
194 /* remove triangles which had crossing sides
195 with smaller triangles or include loudspeakers*/
196 trip_ptr = *ls_triplets;
198 while (trip_ptr != 0){
199 i = trip_ptr->ls_nos[0];
200 j = trip_ptr->ls_nos[1];
201 k = trip_ptr->ls_nos[2];
202 if (connections[i][j] == 0 ||
203 connections[i][k] == 0 ||
204 connections[j][k] == 0 ||
205 any_ls_inside_triplet(i,j,k) == 1 ){
207 prev->next = trip_ptr->next;
209 trip_ptr = trip_ptr->next;
212 *ls_triplets = trip_ptr->next;
214 trip_ptr = trip_ptr->next;
219 trip_ptr = trip_ptr->next;
226 VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
228 /* returns 1 if there is loudspeaker(s) inside given ls triplet */
230 const CartesianVector* lp1;
231 const CartesianVector* lp2;
232 const CartesianVector* lp3;
238 int n_speakers = _speakers.size();
240 lp1 = &(_speakers[a].coords());
241 lp2 = &(_speakers[b].coords());
242 lp3 = &(_speakers[c].coords());
244 /* matrix inversion */
245 invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
246 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
247 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
249 invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
250 invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
251 invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
252 invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
253 invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
254 invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
255 invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
256 invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
257 invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
259 any_ls_inside = false;
260 for (i = 0; i < n_speakers; i++) {
261 if (i != a && i!=b && i != c) {
263 for (j = 0; j < 3; j++) {
264 tmp = _speakers[i].coords().x * invmx[0 + j*3];
265 tmp += _speakers[i].coords().y * invmx[1 + j*3];
266 tmp += _speakers[i].coords().z * invmx[2 + j*3];
272 any_ls_inside = true;
277 return any_ls_inside;
282 VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets)
284 /* adds i,j,k triplet to triplet chain*/
286 struct ls_triplet_chain *trip_ptr, *prev;
287 trip_ptr = *ls_triplets;
290 while (trip_ptr != 0){
292 trip_ptr = trip_ptr->next;
295 trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain));
298 *ls_triplets = trip_ptr;
300 prev->next = trip_ptr;
304 trip_ptr->ls_nos[0] = i;
305 trip_ptr->ls_nos[1] = j;
306 trip_ptr->ls_nos[2] = k;
310 VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
312 float inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/
313 (vec_length(v1) * vec_length(v2)));
323 return fabsf((float) acos((double) inner));
327 VBAPSpeakers::vec_length(CartesianVector v1)
329 return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z));
333 VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2)
335 return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
339 VBAPSpeakers::vol_p_side_lgth(int i, int j,int k, const vector<Speaker>& speakers)
341 /* calculate volume of the parallelepiped defined by the loudspeaker
342 direction vectors and divide it with total length of the triangle sides.
343 This is used when removing too narrow triangles. */
346 CartesianVector xprod;
348 cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod);
349 volper = fabsf (vec_prod(xprod, speakers[k].coords()));
350 lgth = (fabsf (vec_angle(speakers[i].coords(), speakers[j].coords()))
351 + fabsf (vec_angle(speakers[i].coords(), speakers[k].coords()))
352 + fabsf (vec_angle(speakers[j].coords(), speakers[k].coords())));
354 if (lgth > 0.00001) {
355 return volper / lgth;
362 VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res)
366 res->x = (v1.y * v2.z ) - (v1.z * v2.y);
367 res->y = (v1.z * v2.x ) - (v1.x * v2.z);
368 res->z = (v1.x * v2.y ) - (v1.y * v2.x);
370 length = vec_length(*res);
377 VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
379 /* checks if two lines intersect on 3D sphere
380 see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
381 with Multiple Loudspeakers Using VBAP: A Case Study with
382 DIVA Project" in International Conference on
383 Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
384 if you want to have that paper.
389 CartesianVector v3, neg_v3;
390 float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
391 float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
393 cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1);
394 cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2);
395 cross_prod(v1,v2,&v3);
397 neg_v3.x= 0.0 - v3.x;
398 neg_v3.y= 0.0 - v3.y;
399 neg_v3.z= 0.0 - v3.z;
401 dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords()));
402 dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords()));
403 dist_iv3 = (vec_angle(_speakers[i].coords(),v3));
404 dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
405 dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3));
406 dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords()));
407 dist_kv3 = (vec_angle(_speakers[k].coords(),v3));
408 dist_lv3 = (vec_angle(v3,_speakers[l].coords()));
409 dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3));
410 dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords()));
412 /* if one of loudspeakers is close to crossing point, don't do anything*/
415 if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 ||
416 fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 ||
417 fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 ||
418 fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) {
422 if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) &&
423 (fabsf(dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) ||
424 ((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) &&
425 (fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) {
433 VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
435 /* Calculates the inverse matrices for 3D */
437 const CartesianVector* lp1;
438 const CartesianVector* lp2;
439 const CartesianVector* lp3;
441 struct ls_triplet_chain *tr_ptr = ls_triplets;
442 int triplet_count = 0;
447 /* counting triplet amount */
449 while (tr_ptr != 0) {
451 tr_ptr = tr_ptr->next;
454 cerr << "@@@ triplets generate " << triplet_count << " of speaker tuples\n";
459 _speaker_tuples.clear ();
461 for (int n = 0; n < triplet_count; ++n) {
462 _matrices.push_back (threeDmatrix());
463 _speaker_tuples.push_back (tmatrix());
466 while (tr_ptr != 0) {
467 lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords());
468 lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords());
469 lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords());
471 /* matrix inversion */
472 invmx = tr_ptr->inv_mx;
473 invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
474 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
475 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
477 invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
478 invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
479 invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
480 invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
481 invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
482 invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
483 invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
484 invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
485 invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
487 /* copy the matrix */
489 _matrices[triplet][0] = invmx[0];
490 _matrices[triplet][1] = invmx[1];
491 _matrices[triplet][2] = invmx[2];
492 _matrices[triplet][3] = invmx[3];
493 _matrices[triplet][4] = invmx[4];
494 _matrices[triplet][5] = invmx[5];
495 _matrices[triplet][6] = invmx[6];
496 _matrices[triplet][7] = invmx[7];
497 _matrices[triplet][8] = invmx[8];
499 _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0];
500 _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1];
501 _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2];
503 cerr << "Triplet[" << triplet << "] = "
504 << tr_ptr->ls_nos[0] << " + "
505 << tr_ptr->ls_nos[1] << " + "
506 << tr_ptr->ls_nos[2] << endl;
510 tr_ptr = tr_ptr->next;
515 VBAPSpeakers::choose_speaker_pairs (){
517 /* selects the loudspeaker pairs, calculates the inversion
518 matrices and stores the data to a global array
520 const int n_speakers = _speakers.size();
522 if (n_speakers < 1) {
526 IntVector sorted_speakers(n_speakers);
527 BoolVector exists(n_speakers);
528 DoubleVector2D inverse_matrix(n_speakers, DoubleVector(4));
529 const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175);
530 int expected_pairs = 0;
534 for (speaker = 0; speaker < n_speakers; ++speaker) {
535 exists[speaker] = false;
538 /* sort loudspeakers according their aximuth angle */
539 sort_2D_lss (&sorted_speakers[0]);
541 /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
542 for (speaker = 0; speaker < n_speakers-1; speaker++) {
544 if ((_speakers[sorted_speakers[speaker+1]].angles().azi -
545 _speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
546 if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi,
547 _speakers[sorted_speakers[speaker+1]].angles().azi,
548 &inverse_matrix[speaker][0]) != 0){
549 exists[speaker] = true;
555 if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi)
556 +_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
557 if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi,
558 _speakers[sorted_speakers[0]].angles().azi,
559 &inverse_matrix[n_speakers-1][0]) != 0) {
560 exists[n_speakers-1] = true;
568 _speaker_tuples.clear ();
570 for (int n = 0; n < expected_pairs; ++n) {
571 _matrices.push_back (twoDmatrix());
572 _speaker_tuples.push_back (tmatrix());
575 for (speaker = 0; speaker < n_speakers - 1; speaker++) {
576 if (exists[speaker]) {
577 _matrices[pair][0] = inverse_matrix[speaker][0];
578 _matrices[pair][1] = inverse_matrix[speaker][1];
579 _matrices[pair][2] = inverse_matrix[speaker][2];
580 _matrices[pair][3] = inverse_matrix[speaker][3];
582 _speaker_tuples[pair][0] = sorted_speakers[speaker];
583 _speaker_tuples[pair][1] = sorted_speakers[speaker+1];
589 if (exists[n_speakers-1]) {
590 _matrices[pair][0] = inverse_matrix[speaker][0];
591 _matrices[pair][1] = inverse_matrix[speaker][1];
592 _matrices[pair][2] = inverse_matrix[speaker][2];
593 _matrices[pair][3] = inverse_matrix[speaker][3];
595 _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1];
596 _speaker_tuples[pair][1] = sorted_speakers[0];
601 VBAPSpeakers::sort_2D_lss (int* sorted_speakers)
603 vector<Speaker> tmp = _speakers;
604 vector<Speaker>::iterator s;
605 azimuth_sorter sorter;
608 sort (tmp.begin(), tmp.end(), sorter);
610 for (n = 0, s = tmp.begin(); s != tmp.end(); ++s, ++n) {
611 sorted_speakers[n] = (*s).id;
616 VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix)
621 x1 = cos (azi1 * (M_PI/180.0));
622 x2 = sin (azi1 * (M_PI/180.0));
623 x3 = cos (azi2 * (M_PI/180.0));
624 x4 = sin (azi2 * (M_PI/180.0));
625 det = (x1 * x4) - ( x3 * x2 );
627 if (fabs(det) <= 0.001) {
629 inverse_matrix[0] = 0.0;
630 inverse_matrix[1] = 0.0;
631 inverse_matrix[2] = 0.0;
632 inverse_matrix[3] = 0.0;
638 inverse_matrix[0] = x4 / det;
639 inverse_matrix[1] = -x3 / det;
640 inverse_matrix[2] = -x2 / det;
641 inverse_matrix[3] = x1 / det;