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 ();
118 int connections[n_speakers][n_speakers];
119 float distance_table[((n_speakers * (n_speakers - 1)) / 2)];
120 int distance_table_i[((n_speakers * (n_speakers - 1)) / 2)];
121 int distance_table_j[((n_speakers * (n_speakers - 1)) / 2)];
123 struct ls_triplet_chain *trip_ptr, *prev, *tmp_ptr;
125 if (n_speakers == 0) {
129 for (i = 0; i < n_speakers; i++) {
130 for (j = i+1; j < n_speakers; j++) {
131 for(k = j+1; k < n_speakers; k++) {
132 if (vol_p_side_lgth(i, j, k, _speakers) > MIN_VOL_P_SIDE_LGTH) {
139 add_ldsp_triplet(i,j,k,ls_triplets);
145 /*calculate distancies between all speakers and sorting them*/
146 table_size =(((n_speakers - 1) * (n_speakers)) / 2);
147 for (i = 0; i < table_size; i++) {
148 distance_table[i] = 100000.0;
151 for (i = 0;i < n_speakers; i++) {
152 for (j = i+1; j < n_speakers; j++) {
153 if (connections[i][j] == 1) {
154 distance = fabs(vec_angle(_speakers[i].coords(),_speakers[j].coords()));
156 while(distance_table[k] < distance) {
159 for (l = table_size - 1; l > k ; l--) {
160 distance_table[l] = distance_table[l-1];
161 distance_table_i[l] = distance_table_i[l-1];
162 distance_table_j[l] = distance_table_j[l-1];
164 distance_table[k] = distance;
165 distance_table_i[k] = i;
166 distance_table_j[k] = j;
172 /* disconnecting connections which are crossing shorter ones,
173 starting from shortest one and removing all that cross it,
174 and proceeding to next shortest */
175 for (i = 0; i < table_size; i++) {
176 int fst_ls = distance_table_i[i];
177 int sec_ls = distance_table_j[i];
178 if (connections[fst_ls][sec_ls] == 1) {
179 for (j = 0; j < n_speakers; j++) {
180 for (k = j+1; k < n_speakers; k++) {
181 if ((j != fst_ls) && (k != sec_ls) && (k != fst_ls) && (j != sec_ls)) {
182 if (lines_intersect(fst_ls, sec_ls, j, k) == 1){
183 connections[j][k] = 0;
184 connections[k][j] = 0;
192 /* remove triangles which had crossing sides
193 with smaller triangles or include loudspeakers*/
194 trip_ptr = *ls_triplets;
196 while (trip_ptr != 0){
197 i = trip_ptr->ls_nos[0];
198 j = trip_ptr->ls_nos[1];
199 k = trip_ptr->ls_nos[2];
200 if (connections[i][j] == 0 ||
201 connections[i][k] == 0 ||
202 connections[j][k] == 0 ||
203 any_ls_inside_triplet(i,j,k) == 1 ){
205 prev->next = trip_ptr->next;
207 trip_ptr = trip_ptr->next;
210 *ls_triplets = trip_ptr->next;
212 trip_ptr = trip_ptr->next;
217 trip_ptr = trip_ptr->next;
224 VBAPSpeakers::any_ls_inside_triplet(int a, int b, int c)
226 /* returns 1 if there is loudspeaker(s) inside given ls triplet */
228 const CartesianVector* lp1;
229 const CartesianVector* lp2;
230 const CartesianVector* lp3;
236 int n_speakers = _speakers.size();
238 lp1 = &(_speakers[a].coords());
239 lp2 = &(_speakers[b].coords());
240 lp3 = &(_speakers[c].coords());
242 /* matrix inversion */
243 invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
244 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
245 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
247 invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
248 invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
249 invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
250 invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
251 invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
252 invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
253 invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
254 invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
255 invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
257 any_ls_inside = false;
258 for (i = 0; i < n_speakers; i++) {
259 if (i != a && i!=b && i != c) {
261 for (j = 0; j < 3; j++) {
262 tmp = _speakers[i].coords().x * invmx[0 + j*3];
263 tmp += _speakers[i].coords().y * invmx[1 + j*3];
264 tmp += _speakers[i].coords().z * invmx[2 + j*3];
270 any_ls_inside = true;
275 return any_ls_inside;
280 VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls_triplets)
282 /* adds i,j,k triplet to triplet chain*/
284 struct ls_triplet_chain *trip_ptr, *prev;
285 trip_ptr = *ls_triplets;
288 while (trip_ptr != 0){
290 trip_ptr = trip_ptr->next;
293 trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain));
296 *ls_triplets = trip_ptr;
298 prev->next = trip_ptr;
302 trip_ptr->ls_nos[0] = i;
303 trip_ptr->ls_nos[1] = j;
304 trip_ptr->ls_nos[2] = k;
308 VBAPSpeakers::vec_angle(CartesianVector v1, CartesianVector v2)
310 double inner= ((v1.x*v2.x + v1.y*v2.y + v1.z*v2.z)/
311 (vec_length(v1) * vec_length(v2)));
321 return fabs(acos(inner));
325 VBAPSpeakers::vec_length(CartesianVector v1)
327 double rv = sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z);
328 if (rv > 1e-14) return rv;
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;
347 cross_prod (speakers[i].coords(), speakers[j].coords(), &xprod);
348 volper = fabs (vec_prod(xprod, speakers[k].coords()));
349 lgth = ( fabs (vec_angle(speakers[i].coords(), speakers[j].coords()))
350 + fabs (vec_angle(speakers[i].coords(), speakers[k].coords()))
351 + fabs (vec_angle(speakers[j].coords(), speakers[k].coords())));
352 if (lgth > 0.00001) {
353 return volper / lgth;
360 VBAPSpeakers::cross_prod(CartesianVector v1,CartesianVector v2, CartesianVector *res)
364 res->x = (v1.y * v2.z) - (v1.z * v2.y);
365 res->y = (v1.z * v2.x) - (v1.x * v2.z);
366 res->z = (v1.x * v2.y) - (v1.y * v2.x);
368 length = vec_length(*res);
381 VBAPSpeakers::lines_intersect (int i, int j, int k, int l)
383 /* checks if two lines intersect on 3D sphere
384 see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
385 with Multiple Loudspeakers Using VBAP: A Case Study with
386 DIVA Project" in International Conference on
387 Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
388 if you want to have that paper.
393 CartesianVector v3, neg_v3;
394 float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
395 float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
397 cross_prod(_speakers[i].coords(),_speakers[j].coords(),&v1);
398 cross_prod(_speakers[k].coords(),_speakers[l].coords(),&v2);
399 cross_prod(v1,v2,&v3);
401 neg_v3.x= 0.0 - v3.x;
402 neg_v3.y= 0.0 - v3.y;
403 neg_v3.z= 0.0 - v3.z;
405 dist_ij = (vec_angle(_speakers[i].coords(),_speakers[j].coords()));
406 dist_kl = (vec_angle(_speakers[k].coords(),_speakers[l].coords()));
407 dist_iv3 = (vec_angle(_speakers[i].coords(),v3));
408 dist_jv3 = (vec_angle(v3,_speakers[j].coords()));
409 dist_inv3 = (vec_angle(_speakers[i].coords(),neg_v3));
410 dist_jnv3 = (vec_angle(neg_v3,_speakers[j].coords()));
411 dist_kv3 = (vec_angle(_speakers[k].coords(),v3));
412 dist_lv3 = (vec_angle(v3,_speakers[l].coords()));
413 dist_knv3 = (vec_angle(_speakers[k].coords(),neg_v3));
414 dist_lnv3 = (vec_angle(neg_v3,_speakers[l].coords()));
416 /* if one of loudspeakers is close to crossing point, don't do anything*/
417 if(fabsf(dist_iv3) <= 0.01 || fabsf(dist_jv3) <= 0.01 ||
418 fabsf(dist_kv3) <= 0.01 || fabsf(dist_lv3) <= 0.01 ||
419 fabsf(dist_inv3) <= 0.01 || fabsf(dist_jnv3) <= 0.01 ||
420 fabsf(dist_knv3) <= 0.01 || fabsf(dist_lnv3) <= 0.01 ) {
424 /* if crossing point is on line between both loudspeakers return 1 */
425 if (((fabsf(dist_ij - (dist_iv3 + dist_jv3)) <= 0.01 ) &&
426 (fabsf(dist_kl - (dist_kv3 + dist_lv3)) <= 0.01)) ||
427 ((fabsf(dist_ij - (dist_inv3 + dist_jnv3)) <= 0.01) &&
428 (fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) {
436 VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets)
438 /* Calculates the inverse matrices for 3D */
440 const CartesianVector* lp1;
441 const CartesianVector* lp2;
442 const CartesianVector* lp3;
444 struct ls_triplet_chain *tr_ptr = ls_triplets;
445 int triplet_count = 0;
450 /* counting triplet amount */
452 while (tr_ptr != 0) {
454 tr_ptr = tr_ptr->next;
458 cerr << "@@@ VBAP triplets generate " << triplet_count << " of speaker tuples\n";
464 _speaker_tuples.clear ();
466 for (int n = 0; n < triplet_count; ++n) {
467 _matrices.push_back (threeDmatrix());
468 _speaker_tuples.push_back (tmatrix());
471 tr_ptr = ls_triplets;
472 while (tr_ptr != 0) {
473 lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords());
474 lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords());
475 lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords());
477 /* matrix inversion */
478 invmx = tr_ptr->inv_mx;
479 invdet = 1.0 / ( lp1->x * ((lp2->y * lp3->z) - (lp2->z * lp3->y))
480 - lp1->y * ((lp2->x * lp3->z) - (lp2->z * lp3->x))
481 + lp1->z * ((lp2->x * lp3->y) - (lp2->y * lp3->x)));
483 invmx[0] = ((lp2->y * lp3->z) - (lp2->z * lp3->y)) * invdet;
484 invmx[3] = ((lp1->y * lp3->z) - (lp1->z * lp3->y)) * -invdet;
485 invmx[6] = ((lp1->y * lp2->z) - (lp1->z * lp2->y)) * invdet;
486 invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
487 invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
488 invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
489 invmx[2] = ((lp2->x * lp3->y) - (lp2->y * lp3->x)) * invdet;
490 invmx[5] = ((lp1->x * lp3->y) - (lp1->y * lp3->x)) * -invdet;
491 invmx[8] = ((lp1->x * lp2->y) - (lp1->y * lp2->x)) * invdet;
493 /* copy the matrix */
495 _matrices[triplet][0] = invmx[0];
496 _matrices[triplet][1] = invmx[1];
497 _matrices[triplet][2] = invmx[2];
498 _matrices[triplet][3] = invmx[3];
499 _matrices[triplet][4] = invmx[4];
500 _matrices[triplet][5] = invmx[5];
501 _matrices[triplet][6] = invmx[6];
502 _matrices[triplet][7] = invmx[7];
503 _matrices[triplet][8] = invmx[8];
505 _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0];
506 _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1];
507 _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2];
510 cerr << "Triplet[" << triplet << "] = "
511 << tr_ptr->ls_nos[0] << " + "
512 << tr_ptr->ls_nos[1] << " + "
513 << tr_ptr->ls_nos[2] << endl;
518 tr_ptr = tr_ptr->next;
523 VBAPSpeakers::choose_speaker_pairs (){
525 /* selects the loudspeaker pairs, calculates the inversion
526 matrices and stores the data to a global array
528 const int n_speakers = _speakers.size();
529 const double AZIMUTH_DELTA_THRESHOLD_DEGREES = (180.0/M_PI) * (M_PI - 0.175);
530 int sorted_speakers[n_speakers];
531 bool exists[n_speakers];
532 double inverse_matrix[n_speakers][4];
533 int expected_pairs = 0;
538 if (n_speakers == 0) {
542 for (speaker = 0; speaker < n_speakers; ++speaker) {
543 exists[speaker] = false;
546 /* sort loudspeakers according their aximuth angle */
547 sort_2D_lss (sorted_speakers);
549 /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
550 for (speaker = 0; speaker < n_speakers-1; speaker++) {
552 if ((_speakers[sorted_speakers[speaker+1]].angles().azi -
553 _speakers[sorted_speakers[speaker]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
554 if (calc_2D_inv_tmatrix( _speakers[sorted_speakers[speaker]].angles().azi,
555 _speakers[sorted_speakers[speaker+1]].angles().azi,
556 inverse_matrix[speaker]) != 0){
557 exists[speaker] = true;
563 if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles().azi)
564 +_speakers[sorted_speakers[0]].angles().azi) <= AZIMUTH_DELTA_THRESHOLD_DEGREES) {
565 if (calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles().azi,
566 _speakers[sorted_speakers[0]].angles().azi,
567 inverse_matrix[n_speakers-1]) != 0) {
568 exists[n_speakers-1] = true;
576 _speaker_tuples.clear ();
578 for (int n = 0; n < expected_pairs; ++n) {
579 _matrices.push_back (twoDmatrix());
580 _speaker_tuples.push_back (tmatrix());
583 for (speaker = 0; speaker < n_speakers - 1; speaker++) {
584 if (exists[speaker]) {
585 _matrices[pair][0] = inverse_matrix[speaker][0];
586 _matrices[pair][1] = inverse_matrix[speaker][1];
587 _matrices[pair][2] = inverse_matrix[speaker][2];
588 _matrices[pair][3] = inverse_matrix[speaker][3];
590 _speaker_tuples[pair][0] = sorted_speakers[speaker];
591 _speaker_tuples[pair][1] = sorted_speakers[speaker+1];
597 if (exists[n_speakers-1]) {
598 _matrices[pair][0] = inverse_matrix[speaker][0];
599 _matrices[pair][1] = inverse_matrix[speaker][1];
600 _matrices[pair][2] = inverse_matrix[speaker][2];
601 _matrices[pair][3] = inverse_matrix[speaker][3];
603 _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1];
604 _speaker_tuples[pair][1] = sorted_speakers[0];
609 VBAPSpeakers::sort_2D_lss (int* sorted_speakers)
611 vector<Speaker> tmp = _speakers;
612 vector<Speaker>::iterator s;
613 azimuth_sorter sorter;
616 sort (tmp.begin(), tmp.end(), sorter);
618 for (n = 0, s = tmp.begin(); s != tmp.end(); ++s, ++n) {
619 sorted_speakers[n] = (*s).id;
624 VBAPSpeakers::calc_2D_inv_tmatrix (double azi1, double azi2, double* inverse_matrix)
629 x1 = cos (azi1 * (M_PI/180.0));
630 x2 = sin (azi1 * (M_PI/180.0));
631 x3 = cos (azi2 * (M_PI/180.0));
632 x4 = sin (azi2 * (M_PI/180.0));
633 det = (x1 * x4) - ( x3 * x2 );
635 if (fabs(det) <= 0.001) {
637 inverse_matrix[0] = 0.0;
638 inverse_matrix[1] = 0.0;
639 inverse_matrix[2] = 0.0;
640 inverse_matrix[3] = 0.0;
646 inverse_matrix[0] = x4 / det;
647 inverse_matrix[1] = -x3 / det;
648 inverse_matrix[2] = -x2 / det;
649 inverse_matrix[3] = x1 / det;