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
38 #include "pbd/cartesian.h"
40 #include "vbap_speakers.h"
42 using namespace ARDOUR;
46 const double VBAPSpeakers::MIN_VOL_P_SIDE_LGTH = 0.01;
48 VBAPSpeakers::VBAPSpeakers (boost::shared_ptr<Speakers> s)
52 _parent->Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPSpeakers::update, this));
56 VBAPSpeakers::~VBAPSpeakers ()
61 VBAPSpeakers::update ()
65 _speakers = _parent->speakers();
67 for (vector<Speaker>::const_iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
68 if ((*i).angles().ele != 0.0) {
76 if (_speakers.size() < 2) {
77 /* nothing to be done with less than two speakers */
81 if (_dimension == 3) {
82 ls_triplet_chain *ls_triplets = 0;
83 choose_speaker_triplets (&ls_triplets);
85 calculate_3x3_matrixes (ls_triplets);
89 choose_speaker_pairs ();
94 VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain **ls_triplets)
96 /* Selects the loudspeaker triplets, and
97 calculates the inversion matrices for each selected triplet.
98 A line (connection) is drawn between each loudspeaker. The lines
99 denote the sides of the triangles. The triangles should not be
100 intersecting. All crossing connections are searched and the
101 longer connection is erased. This yields non-intesecting triangles,
102 which can be used in panning.
106 for (vector<Speaker>::iterator i = _speakers.begin(); i != _speakers.end(); ++i) {
107 cout << "Speaker " << (*i).id << " @ "
108 << (*i).coords().x << ", " << (*i).coords().y << ", " << (*i).coords().z
109 << " azimuth " << (*i).angles().azi
110 << " elevation " << (*i).angles().ele
111 << " distance " << (*i).angles().length
116 int i,j,k,l,table_size;
117 int n_speakers = _speakers.size ();
119 if (n_speakers < 3) {
120 fprintf(stderr, "VBAP: at least 3 speakers need to be defined.");
124 /* variable length arrays arrived in C99, became optional in C11, and
125 are only planned for C++14. Use alloca which is functionally
126 identical (but uglier to read).
128 int* connections = (int*) alloca (sizeof (int) * n_speakers * n_speakers);
129 float* distance_table = (float *) alloca (sizeof (float) * ((n_speakers * (n_speakers - 1)) / 2));
130 int* distance_table_i = (int *) alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2));
131 int* distance_table_j = (int *) alloca (sizeof (int) * ((n_speakers * (n_speakers - 1)) / 2));
133 struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
135 for (i = 0; i < n_speakers * n_speakers; i++) {
139 for (i = 0; i < n_speakers; i++) {
140 for (j = i+1; j < n_speakers; j++) {
141 for(k = j+1; k < n_speakers; k++) {
142 if (vol_p_side_lgth(i, j, k, _speakers) > MIN_VOL_P_SIDE_LGTH) {
143 connections[(i*n_speakers)+j]=1;
144 connections[(j*n_speakers)+i]=1;
145 connections[(i*n_speakers)+k]=1;
146 connections[(k*n_speakers)+i]=1;
147 connections[(j*n_speakers)+k]=1;
148 connections[(k*n_speakers)+j]=1;
149 add_ldsp_triplet(i,j,k,ls_triplets);
155 /*calculate distancies between all speakers and sorting them*/
156 table_size =(((n_speakers - 1) * (n_speakers)) / 2);
157 for (i = 0; i < table_size; i++) {
158 distance_table[i] = 100000.0;
161 for (i = 0;i < n_speakers; i++) {
162 for (j = i+1; j < n_speakers; j++) {
163 if (connections[(i*n_speakers)+j] == 1) {
164 distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
166 while(distance_table[k] < distance) {
169 for (l = table_size - 1; l > k ; l--) {
170 distance_table[l] = distance_table[l-1];
171 distance_table_i[l] = distance_table_i[l-1];
172 distance_table_j[l] = distance_table_j[l-1];
174 distance_table[k] = distance;
175 distance_table_i[k] = i;
176 distance_table_j[k] = j;
182 /* disconnecting connections which are crossing shorter ones,
183 starting from shortest one and removing all that cross it,
184 and proceeding to next shortest */
185 for (i = 0; i < table_size; i++) {
186 int fst_ls = distance_table_i[i];
187 int sec_ls = distance_table_j[i];
188 if (connections[(fst_ls*n_speakers)+sec_ls] == 1) {
189 for (j = 0; j < n_speakers; j++) {
190 for (k = j+1; k < n_speakers; k++) {
191 if ((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) {
192 if (lines_intersect(fst_ls, sec_ls, j, k) == 1){
193 connections[(j*n_speakers)+k] = 0;
194 connections[(k*n_speakers)+j] = 0;
202 /* remove triangles which had crossing sides
203 with smaller triangles or include loudspeakers*/
204 trip_ptr = *ls_triplets;
206 while (trip_ptr != 0){
207 i = trip_ptr->ls_nos[0];
208 j = trip_ptr->ls_nos[1];
209 k = trip_ptr->ls_nos[2];
210 if (connections[(i*n_speakers)+j] == 0 ||
211 connections[(i*n_speakers)+k] == 0 ||
212 connections[(j*n_speakers)+k] == 0 ||
213 any_ls_inside_triplet(i,j,k) == 1 ){
215 prev->next = trip_ptr->next;
217 trip_ptr = trip_ptr->next;
220 *ls_triplets = trip_ptr->next;
222 trip_ptr = trip_ptr->next;
227 trip_ptr = trip_ptr->next;
234 VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
236 /* returns 1 if there is loudspeaker(s) inside given ls triplet */
238 const CartesianVector* lp1;
239 const CartesianVector* lp2;
240 const CartesianVector* lp3;
246 int n_speakers = _speakers.size();
248 lp1 = &(_speakers[a].coords());
249 lp2 = &(_speakers[b].coords());
250 lp3 = &(_speakers[c].coords());
252 /* matrix inversion */
253 invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
254 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
255 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
257 invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
258 invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
259 invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
260 invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
261 invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
262 invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
263 invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
264 invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
265 invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
267 any_ls_inside = false;
268 for (i = 0; i < n_speakers; i++) {
269 if (i != a && i!=b && i != c) {
271 for (j = 0; j < 3; j++) {
272 tmp = _speakers[i].coords().x * invmx[0 + j*3];
273 tmp += _speakers[i].coords().y * invmx[1 + j*3];
274 tmp += _speakers[i].coords().z * invmx[2 + j*3];
280 any_ls_inside = true;
285 return any_ls_inside;
290 VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets)
292 /* adds i,j,k triplet to triplet chain*/
294 struct ls_triplet_chain *trip_ptr, *prev;
295 trip_ptr = *ls_triplets;
298 while (trip_ptr != 0){
300 trip_ptr = trip_ptr->next;
303 trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain));
306 *ls_triplets = trip_ptr;
308 prev->next = trip_ptr;
312 trip_ptr->ls_nos[0] = i;
313 trip_ptr->ls_nos[1] = j;
314 trip_ptr->ls_nos[2] = k;
318 VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
320 double inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/
321 (vec_length(v1) * vec_length(v2)));
331 return fabs(acos(inner));
335 VBAPSpeakers::vec_length(CartesianVector v1)
337 double rv = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z);
338 if (rv > 1e-14) return rv;
343 VBAPSpeakers::vec_prod(CartesianVector v1, CartesianVector v2)
345 return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
349 VBAPSpeakers::vol_p_side_lgth(int i, int j, int k, const vector<Speaker>& speakers)
351 /* calculate volume of the parallelepiped defined by the loudspeaker
352 direction vectors and divide it with total length of the triangle sides.
353 This is used when removing too narrow triangles. */
356 CartesianVector xprod;
357 cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod);
358 volper = fabs (vec_prod(xprod, speakers[k].coords()));
359 lgth = ( fabs (vec_angle(speakers[i].coords(), speakers[j].coords()))
360 + fabs (vec_angle(speakers[i].coords(), speakers[k].coords()))
361 + fabs (vec_angle(speakers[j].coords(), speakers[k].coords())));
362 if (lgth > 0.00001) {
363 return volper / lgth;
370 VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res)
374 res->x = (v1.y * v2.z) - (v1.z * v2.y);
375 res->y = (v1.z * v2.x) - (v1.x * v2.z);
376 res->z = (v1.x * v2.y) - (v1.y * v2.x);
378 length = vec_length(*res);
391 VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
393 /* checks if two lines intersect on 3D sphere
394 see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
395 with Multiple Loudspeakers Using VBAP: A Case Study with
396 DIVA Project" in International Conference on
397 Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
398 if you want to have that paper.
403 CartesianVector v3, neg_v3;
404 float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
405 float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
407 cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1);
408 cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2);
409 cross_prod(v1,v2,&v3);
411 neg_v3.x= 0.0 - v3.x;
412 neg_v3.y= 0.0 - v3.y;
413 neg_v3.z= 0.0 - v3.z;
415 dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords()));
416 dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords()));
417 dist_iv3 = (vec_angle(_speakers[i].coords(),v3));
418 dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
419 dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3));
420 dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords()));
421 dist_kv3 = (vec_angle(_speakers[k].coords(),v3));
422 dist_lv3 = (vec_angle(v3,_speakers[l].coords()));
423 dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3));
424 dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords()));
426 /* if one of loudspeakers is close to crossing point, don't do anything*/
427 if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 ||
428 fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 ||
429 fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 ||
430 fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) {
434 /* if crossing point is on line between both loudspeakers return 1 */
435 if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) &&
436 (fabsf(dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) ||
437 ((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) &&
438 (fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) {
446 VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
448 /* Calculates the inverse matrices for 3D */
450 const CartesianVector* lp1;
451 const CartesianVector* lp2;
452 const CartesianVector* lp3;
454 struct ls_triplet_chain *tr_ptr = ls_triplets;
455 int triplet_count = 0;
460 /* counting triplet amount */
462 while (tr_ptr != 0) {
464 tr_ptr = tr_ptr->next;
468 cerr << "@@@ VBAP triplets generate " << triplet_count << " of speaker tuples\n";
474 _speaker_tuples.clear ();
476 for (int n = 0; n < triplet_count; ++n) {
477 _matrices.push_back (threeDmatrix());
478 _speaker_tuples.push_back (tmatrix());
481 tr_ptr = ls_triplets;
482 while (tr_ptr != 0) {
483 lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords());
484 lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords());
485 lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords());
487 /* matrix inversion */
488 invmx = tr_ptr->inv_mx;
489 invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
490 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
491 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
493 invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
494 invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
495 invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
496 invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
497 invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
498 invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
499 invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
500 invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
501 invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
503 /* copy the matrix */
505 _matrices[triplet][0] = invmx[0];
506 _matrices[triplet][1] = invmx[1];
507 _matrices[triplet][2] = invmx[2];
508 _matrices[triplet][3] = invmx[3];
509 _matrices[triplet][4] = invmx[4];
510 _matrices[triplet][5] = invmx[5];
511 _matrices[triplet][6] = invmx[6];
512 _matrices[triplet][7] = invmx[7];
513 _matrices[triplet][8] = invmx[8];
515 _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0];
516 _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1];
517 _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2];
520 cerr << "Triplet[" << triplet << "] = "
521 << tr_ptr->ls_nos[0] << " + "
522 << tr_ptr->ls_nos[1] << " + "
523 << tr_ptr->ls_nos[2] << endl;
528 tr_ptr = tr_ptr->next;
533 VBAPSpeakers::choose_speaker_pairs (){
535 /* selects the loudspeaker pairs, calculates the inversion
536 matrices and stores the data to a global array
538 const int n_speakers = _speakers.size();
540 if (n_speakers < 2) {
541 fprintf(stderr, "VBAP: at least 2 speakers need to be defined.");
545 const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175);
546 /* variable length arrays arrived in C99, became optional in C11, and
547 are only planned for C++14. Use alloca which is functionally
548 identical (but uglier to read).
550 int* sorted_speakers = (int*) alloca (sizeof (int) * n_speakers);
551 bool* exists = (bool*) alloca (sizeof(bool) * n_speakers);
552 double* inverse_matrix = (double*) alloca (sizeof (double) * n_speakers * 4);
553 int expected_pairs = 0;
557 for (speaker = 0; speaker < n_speakers; ++speaker) {
558 exists[speaker] = false;
561 /* sort loudspeakers according their aximuth angle */
562 #ifdef __clang_analyzer__
563 // sort_2D_lss() assigns values to all of sorted_speakers
564 // "uninitialized value"
565 memset(sorted_speakers, 0, sizeof(*sorted_speakers));
567 sort_2D_lss (sorted_speakers);
569 /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
570 for (speaker = 0; speaker < n_speakers-1; speaker++) {
572 if ((_speakers[sorted_speakers[speaker+1]].angles().azi -
573 _speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
574 if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi,
575 _speakers[sorted_speakers[speaker+1]].angles().azi,
576 &inverse_matrix[4 * speaker]) != 0){
577 exists[speaker] = true;
583 if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi)
584 +_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
585 if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi,
586 _speakers[sorted_speakers[0]].angles().azi,
587 &inverse_matrix[4*(n_speakers-1)]) != 0) {
588 exists[n_speakers-1] = true;
596 _speaker_tuples.clear ();
598 for (int n = 0; n < expected_pairs; ++n) {
599 _matrices.push_back (twoDmatrix());
600 _speaker_tuples.push_back (tmatrix());
603 for (speaker = 0; speaker < n_speakers - 1; speaker++) {
604 if (exists[speaker]) {
605 _matrices[pair][0] = inverse_matrix[(speaker*4)+0];
606 _matrices[pair][1] = inverse_matrix[(speaker*4)+1];
607 _matrices[pair][2] = inverse_matrix[(speaker*4)+2];
608 _matrices[pair][3] = inverse_matrix[(speaker*4)+3];
610 _speaker_tuples[pair][0] = sorted_speakers[speaker];
611 _speaker_tuples[pair][1] = sorted_speakers[speaker+1];
617 if (exists[n_speakers-1]) {
618 _matrices[pair][0] = inverse_matrix[(speaker*4)+0];
619 _matrices[pair][1] = inverse_matrix[(speaker*4)+1];
620 _matrices[pair][2] = inverse_matrix[(speaker*4)+2];
621 _matrices[pair][3] = inverse_matrix[(speaker*4)+3];
623 _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1];
624 _speaker_tuples[pair][1] = sorted_speakers[0];
629 VBAPSpeakers::sort_2D_lss (int* sorted_speakers)
631 vector<Speaker> tmp = _speakers;
632 vector<Speaker>::iterator s;
633 azimuth_sorter sorter;
636 sort (tmp.begin(), tmp.end(), sorter);
638 for (n = 0, s = tmp.begin(); s != tmp.end(); ++s, ++n) {
639 sorted_speakers[n] = (*s).id;
641 assert(n == _speakers.size ());
645 VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix)
650 x1 = cos (azi1 * (M_PI/180.0));
651 x2 = sin (azi1 * (M_PI/180.0));
652 x3 = cos (azi2 * (M_PI/180.0));
653 x4 = sin (azi2 * (M_PI/180.0));
654 det = (x1 * x4) - ( x3 * x2 );
656 if (fabs(det) <= 0.001) {
658 inverse_matrix[0] = 0.0;
659 inverse_matrix[1] = 0.0;
660 inverse_matrix[2] = 0.0;
661 inverse_matrix[3] = 0.0;
667 inverse_matrix[0] = x4 / det;
668 inverse_matrix[1] = -x3 / det;
669 inverse_matrix[2] = -x2 / det;
670 inverse_matrix[3] = x1 / det;