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 VBAPSpeakers::VBAPSpeakers (boost::shared_ptr
<Speakers
> s
)
50 _parent
->Changed
.connect_same_thread (speaker_connection
, boost::bind (&VBAPSpeakers::update
, this));
54 VBAPSpeakers::~VBAPSpeakers ()
59 VBAPSpeakers::update ()
63 _speakers
= _parent
->speakers();
65 for (vector
<Speaker
>::const_iterator i
= _speakers
.begin(); i
!= _speakers
.end(); ++i
) {
66 if ((*i
).angles().ele
!= 0.0) {
74 if (_speakers
.size() < 2) {
75 /* nothing to be done with less than two speakers */
79 if (_dimension
== 3) {
80 ls_triplet_chain
*ls_triplets
= 0;
81 choose_speaker_triplets (&ls_triplets
);
83 calculate_3x3_matrixes (ls_triplets
);
87 choose_speaker_pairs ();
92 VBAPSpeakers::choose_speaker_triplets(struct ls_triplet_chain
**ls_triplets
)
94 /* Selects the loudspeaker triplets, and
95 calculates the inversion matrices for each selected triplet.
96 A line (connection) is drawn between each loudspeaker. The lines
97 denote the sides of the triangles. The triangles should not be
98 intersecting. All crossing connections are searched and the
99 longer connection is erased. This yields non-intesecting triangles,
100 which can be used in panning.
103 int i
,j
,k
,l
,table_size
;
104 int n_speakers
= _speakers
.size ();
105 int connections
[n_speakers
][n_speakers
];
106 float distance_table
[((n_speakers
* (n_speakers
- 1)) / 2)];
107 int distance_table_i
[((n_speakers
* (n_speakers
- 1)) / 2)];
108 int distance_table_j
[((n_speakers
* (n_speakers
- 1)) / 2)];
110 struct ls_triplet_chain
*trip_ptr
, *prev
, *tmp_ptr
;
112 if (n_speakers
== 0) {
116 for (i
= 0; i
< n_speakers
; i
++) {
117 for (j
= i
+1; j
< n_speakers
; j
++) {
118 for(k
=j
+1;k
<n_speakers
;k
++) {
119 if (vol_p_side_lgth(i
,j
, k
, _speakers
) > MIN_VOL_P_SIDE_LGTH
){
126 add_ldsp_triplet(i
,j
,k
,ls_triplets
);
132 /*calculate distancies between all speakers and sorting them*/
133 table_size
=(((n_speakers
- 1) * (n_speakers
)) / 2);
134 for (i
= 0; i
< table_size
; i
++) {
135 distance_table
[i
] = 100000.0;
138 for (i
= 0;i
< n_speakers
; i
++) {
139 for (j
= i
+1; j
< n_speakers
; j
++) {
140 if (connections
[i
][j
] == 1) {
141 distance
= fabs(vec_angle(_speakers
[i
].coords(),_speakers
[j
].coords()));
143 while(distance_table
[k
] < distance
) {
146 for (l
= table_size
- 1; l
> k
; l
--) {
147 distance_table
[l
] = distance_table
[l
-1];
148 distance_table_i
[l
] = distance_table_i
[l
-1];
149 distance_table_j
[l
] = distance_table_j
[l
-1];
151 distance_table
[k
] = distance
;
152 distance_table_i
[k
] = i
;
153 distance_table_j
[k
] = j
;
159 /* disconnecting connections which are crossing shorter ones,
160 starting from shortest one and removing all that cross it,
161 and proceeding to next shortest */
162 for (i
= 0; i
< table_size
; i
++) {
163 int fst_ls
= distance_table_i
[i
];
164 int sec_ls
= distance_table_j
[i
];
165 if (connections
[fst_ls
][sec_ls
] == 1) {
166 for (j
= 0; j
< n_speakers
; j
++) {
167 for (k
= j
+1; k
< n_speakers
; k
++) {
168 if ((j
!=fst_ls
) && (k
!= sec_ls
) && (k
!=fst_ls
) && (j
!= sec_ls
)){
169 if (lines_intersect(fst_ls
, sec_ls
, j
,k
) == 1){
170 connections
[j
][k
] = 0;
171 connections
[k
][j
] = 0;
179 /* remove triangles which had crossing sides
180 with smaller triangles or include loudspeakers*/
181 trip_ptr
= *ls_triplets
;
183 while (trip_ptr
!= 0){
184 i
= trip_ptr
->ls_nos
[0];
185 j
= trip_ptr
->ls_nos
[1];
186 k
= trip_ptr
->ls_nos
[2];
187 if (connections
[i
][j
] == 0 ||
188 connections
[i
][k
] == 0 ||
189 connections
[j
][k
] == 0 ||
190 any_ls_inside_triplet(i
,j
,k
) == 1 ){
192 prev
->next
= trip_ptr
->next
;
194 trip_ptr
= trip_ptr
->next
;
197 *ls_triplets
= trip_ptr
->next
;
199 trip_ptr
= trip_ptr
->next
;
204 trip_ptr
= trip_ptr
->next
;
211 VBAPSpeakers::any_ls_inside_triplet(int a
, int b
, int c
)
213 /* returns 1 if there is loudspeaker(s) inside given ls triplet */
215 const CartesianVector
* lp1
;
216 const CartesianVector
* lp2
;
217 const CartesianVector
* lp3
;
223 int n_speakers
= _speakers
.size();
225 lp1
= &(_speakers
[a
].coords());
226 lp2
= &(_speakers
[b
].coords());
227 lp3
= &(_speakers
[c
].coords());
229 /* matrix inversion */
230 invdet
= 1.0 / ( lp1
->x
* ((lp2
->y
* lp3
->z
) - (lp2
->z
* lp3
->y
))
231 - lp1
->y
* ((lp2
->x
* lp3
->z
) - (lp2
->z
* lp3
->x
))
232 + lp1
->z
* ((lp2
->x
* lp3
->y
) - (lp2
->y
* lp3
->x
)));
234 invmx
[0] = ((lp2
->y
* lp3
->z
) - (lp2
->z
* lp3
->y
)) * invdet
;
235 invmx
[3] = ((lp1
->y
* lp3
->z
) - (lp1
->z
* lp3
->y
)) * -invdet
;
236 invmx
[6] = ((lp1
->y
* lp2
->z
) - (lp1
->z
* lp2
->y
)) * invdet
;
237 invmx
[1] = ((lp2
->x
* lp3
->z
) - (lp2
->z
* lp3
->x
)) * -invdet
;
238 invmx
[4] = ((lp1
->x
* lp3
->z
) - (lp1
->z
* lp3
->x
)) * invdet
;
239 invmx
[7] = ((lp1
->x
* lp2
->z
) - (lp1
->z
* lp2
->x
)) * -invdet
;
240 invmx
[2] = ((lp2
->x
* lp3
->y
) - (lp2
->y
* lp3
->x
)) * invdet
;
241 invmx
[5] = ((lp1
->x
* lp3
->y
) - (lp1
->y
* lp3
->x
)) * -invdet
;
242 invmx
[8] = ((lp1
->x
* lp2
->y
) - (lp1
->y
* lp2
->x
)) * invdet
;
244 any_ls_inside
= false;
245 for (i
= 0; i
< n_speakers
; i
++) {
246 if (i
!= a
&& i
!=b
&& i
!= c
) {
248 for (j
= 0; j
< 3; j
++) {
249 tmp
= _speakers
[i
].coords().x
* invmx
[0 + j
*3];
250 tmp
+= _speakers
[i
].coords().y
* invmx
[1 + j
*3];
251 tmp
+= _speakers
[i
].coords().z
* invmx
[2 + j
*3];
257 any_ls_inside
= true;
262 return any_ls_inside
;
267 VBAPSpeakers::add_ldsp_triplet(int i
, int j
, int k
, struct ls_triplet_chain
**ls_triplets
)
269 /* adds i,j,k triplet to triplet chain*/
271 struct ls_triplet_chain
*trip_ptr
, *prev
;
272 trip_ptr
= *ls_triplets
;
275 while (trip_ptr
!= 0){
277 trip_ptr
= trip_ptr
->next
;
280 trip_ptr
= (struct ls_triplet_chain
*) malloc (sizeof (struct ls_triplet_chain
));
283 *ls_triplets
= trip_ptr
;
285 prev
->next
= trip_ptr
;
289 trip_ptr
->ls_nos
[0] = i
;
290 trip_ptr
->ls_nos
[1] = j
;
291 trip_ptr
->ls_nos
[2] = k
;
295 VBAPSpeakers::vec_angle(CartesianVector v1
, CartesianVector v2
)
297 float inner
= ((v1
.x
*v2
.x
+ v1
.y
*v2
.y
+ v1
.z
*v2
.z
)/
298 (vec_length(v1
) * vec_length(v2
)));
308 return fabsf((float) acos((double) inner
));
312 VBAPSpeakers::vec_length(CartesianVector v1
)
314 return (sqrt(v1
.x
*v1
.x
+ v1
.y
*v1
.y
+ v1
.z
*v1
.z
));
318 VBAPSpeakers::vec_prod(CartesianVector v1
, CartesianVector v2
)
320 return (v1
.x
*v2
.x
+ v1
.y
*v2
.y
+ v1
.z
*v2
.z
);
324 VBAPSpeakers::vol_p_side_lgth(int i
, int j
,int k
, const vector
<Speaker
>& speakers
)
326 /* calculate volume of the parallelepiped defined by the loudspeaker
327 direction vectors and divide it with total length of the triangle sides.
328 This is used when removing too narrow triangles. */
331 CartesianVector xprod
;
333 cross_prod (speakers
[i
].coords(), speakers
[j
].coords(), &xprod
);
334 volper
= fabsf (vec_prod(xprod
, speakers
[k
].coords()));
335 lgth
= (fabsf (vec_angle(speakers
[i
].coords(), speakers
[j
].coords()))
336 + fabsf (vec_angle(speakers
[i
].coords(), speakers
[k
].coords()))
337 + fabsf (vec_angle(speakers
[j
].coords(), speakers
[k
].coords())));
339 if (lgth
> 0.00001) {
340 return volper
/ lgth
;
347 VBAPSpeakers::cross_prod(CartesianVector v1
,CartesianVector v2
, CartesianVector
*res
)
351 res
->x
= (v1
.y
* v2
.z
) - (v1
.z
* v2
.y
);
352 res
->y
= (v1
.z
* v2
.x
) - (v1
.x
* v2
.z
);
353 res
->z
= (v1
.x
* v2
.y
) - (v1
.y
* v2
.x
);
355 length
= vec_length(*res
);
362 VBAPSpeakers::lines_intersect (int i
, int j
, int k
, int l
)
364 /* checks if two lines intersect on 3D sphere
365 see theory in paper Pulkki, V. Lokki, T. "Creating Auditory Displays
366 with Multiple Loudspeakers Using VBAP: A Case Study with
367 DIVA Project" in International Conference on
368 Auditory Displays -98. E-mail Ville.Pulkki@hut.fi
369 if you want to have that paper.
374 CartesianVector v3
, neg_v3
;
375 float dist_ij
,dist_kl
,dist_iv3
,dist_jv3
,dist_inv3
,dist_jnv3
;
376 float dist_kv3
,dist_lv3
,dist_knv3
,dist_lnv3
;
378 cross_prod(_speakers
[i
].coords(),_speakers
[j
].coords(),&v1
);
379 cross_prod(_speakers
[k
].coords(),_speakers
[l
].coords(),&v2
);
380 cross_prod(v1
,v2
,&v3
);
382 neg_v3
.x
= 0.0 - v3
.x
;
383 neg_v3
.y
= 0.0 - v3
.y
;
384 neg_v3
.z
= 0.0 - v3
.z
;
386 dist_ij
= (vec_angle(_speakers
[i
].coords(),_speakers
[j
].coords()));
387 dist_kl
= (vec_angle(_speakers
[k
].coords(),_speakers
[l
].coords()));
388 dist_iv3
= (vec_angle(_speakers
[i
].coords(),v3
));
389 dist_jv3
= (vec_angle(v3
,_speakers
[j
].coords()));
390 dist_inv3
= (vec_angle(_speakers
[i
].coords(),neg_v3
));
391 dist_jnv3
= (vec_angle(neg_v3
,_speakers
[j
].coords()));
392 dist_kv3
= (vec_angle(_speakers
[k
].coords(),v3
));
393 dist_lv3
= (vec_angle(v3
,_speakers
[l
].coords()));
394 dist_knv3
= (vec_angle(_speakers
[k
].coords(),neg_v3
));
395 dist_lnv3
= (vec_angle(neg_v3
,_speakers
[l
].coords()));
397 /* if one of loudspeakers is close to crossing point, don't do anything*/
400 if(fabsf(dist_iv3
) <= 0.01 || fabsf(dist_jv3
) <= 0.01 ||
401 fabsf(dist_kv3
) <= 0.01 || fabsf(dist_lv3
) <= 0.01 ||
402 fabsf(dist_inv3
) <= 0.01 || fabsf(dist_jnv3
) <= 0.01 ||
403 fabsf(dist_knv3
) <= 0.01 || fabsf(dist_lnv3
) <= 0.01 ) {
407 if (((fabsf(dist_ij
- (dist_iv3
+ dist_jv3
)) <= 0.01 ) &&
408 (fabsf(dist_kl
- (dist_kv3
+ dist_lv3
)) <= 0.01)) ||
409 ((fabsf(dist_ij
- (dist_inv3
+ dist_jnv3
)) <= 0.01) &&
410 (fabsf(dist_kl
- (dist_knv3
+ dist_lnv3
)) <= 0.01 ))) {
418 VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain
*ls_triplets
)
420 /* Calculates the inverse matrices for 3D */
422 const CartesianVector
* lp1
;
423 const CartesianVector
* lp2
;
424 const CartesianVector
* lp3
;
426 struct ls_triplet_chain
*tr_ptr
= ls_triplets
;
427 int triplet_count
= 0;
432 /* counting triplet amount */
434 while (tr_ptr
!= 0) {
436 tr_ptr
= tr_ptr
->next
;
439 cerr
<< "@@@ triplets generate " << triplet_count
<< " of speaker tuples\n";
444 _speaker_tuples
.clear ();
446 for (int n
= 0; n
< triplet_count
; ++n
) {
447 _matrices
.push_back (threeDmatrix());
448 _speaker_tuples
.push_back (tmatrix());
451 while (tr_ptr
!= 0) {
452 lp1
= &(_speakers
[tr_ptr
->ls_nos
[0]].coords());
453 lp2
= &(_speakers
[tr_ptr
->ls_nos
[1]].coords());
454 lp3
= &(_speakers
[tr_ptr
->ls_nos
[2]].coords());
456 /* matrix inversion */
457 invmx
= tr_ptr
->inv_mx
;
458 invdet
= 1.0 / ( lp1
->x
* ((lp2
->y
* lp3
->z
) - (lp2
->z
* lp3
->y
))
459 - lp1
->y
* ((lp2
->x
* lp3
->z
) - (lp2
->z
* lp3
->x
))
460 + lp1
->z
* ((lp2
->x
* lp3
->y
) - (lp2
->y
* lp3
->x
)));
462 invmx
[0] = ((lp2
->y
* lp3
->z
) - (lp2
->z
* lp3
->y
)) * invdet
;
463 invmx
[3] = ((lp1
->y
* lp3
->z
) - (lp1
->z
* lp3
->y
)) * -invdet
;
464 invmx
[6] = ((lp1
->y
* lp2
->z
) - (lp1
->z
* lp2
->y
)) * invdet
;
465 invmx
[1] = ((lp2
->x
* lp3
->z
) - (lp2
->z
* lp3
->x
)) * -invdet
;
466 invmx
[4] = ((lp1
->x
* lp3
->z
) - (lp1
->z
* lp3
->x
)) * invdet
;
467 invmx
[7] = ((lp1
->x
* lp2
->z
) - (lp1
->z
* lp2
->x
)) * -invdet
;
468 invmx
[2] = ((lp2
->x
* lp3
->y
) - (lp2
->y
* lp3
->x
)) * invdet
;
469 invmx
[5] = ((lp1
->x
* lp3
->y
) - (lp1
->y
* lp3
->x
)) * -invdet
;
470 invmx
[8] = ((lp1
->x
* lp2
->y
) - (lp1
->y
* lp2
->x
)) * invdet
;
472 /* copy the matrix */
474 _matrices
[triplet
][0] = invmx
[0];
475 _matrices
[triplet
][1] = invmx
[1];
476 _matrices
[triplet
][2] = invmx
[2];
477 _matrices
[triplet
][3] = invmx
[3];
478 _matrices
[triplet
][4] = invmx
[4];
479 _matrices
[triplet
][5] = invmx
[5];
480 _matrices
[triplet
][6] = invmx
[6];
481 _matrices
[triplet
][7] = invmx
[7];
482 _matrices
[triplet
][8] = invmx
[8];
484 _speaker_tuples
[triplet
][0] = tr_ptr
->ls_nos
[0];
485 _speaker_tuples
[triplet
][1] = tr_ptr
->ls_nos
[1];
486 _speaker_tuples
[triplet
][2] = tr_ptr
->ls_nos
[2];
488 cerr
<< "Triplet[" << triplet
<< "] = "
489 << tr_ptr
->ls_nos
[0] << " + "
490 << tr_ptr
->ls_nos
[1] << " + "
491 << tr_ptr
->ls_nos
[2] << endl
;
495 tr_ptr
= tr_ptr
->next
;
500 VBAPSpeakers::choose_speaker_pairs (){
502 /* selects the loudspeaker pairs, calculates the inversion
503 matrices and stores the data to a global array
505 const int n_speakers
= _speakers
.size();
506 const double AZIMUTH_DELTA_THRESHOLD_DEGREES
= (180.0/M_PI
) * (M_PI
- 0.175);
507 int sorted_speakers
[n_speakers
];
508 bool exists
[n_speakers
];
509 double inverse_matrix
[n_speakers
][4];
510 int expected_pairs
= 0;
515 if (n_speakers
== 0) {
519 for (speaker
= 0; speaker
< n_speakers
; ++speaker
) {
520 exists
[speaker
] = false;
523 /* sort loudspeakers according their aximuth angle */
524 sort_2D_lss (sorted_speakers
);
526 /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
527 for (speaker
= 0; speaker
< n_speakers
-1; speaker
++) {
529 if ((_speakers
[sorted_speakers
[speaker
+1]].angles().azi
-
530 _speakers
[sorted_speakers
[speaker
]].angles().azi
) <= AZIMUTH_DELTA_THRESHOLD_DEGREES
) {
531 if (calc_2D_inv_tmatrix( _speakers
[sorted_speakers
[speaker
]].angles().azi
,
532 _speakers
[sorted_speakers
[speaker
+1]].angles().azi
,
533 inverse_matrix
[speaker
]) != 0){
534 exists
[speaker
] = true;
540 if (((6.283 - _speakers
[sorted_speakers
[n_speakers
-1]].angles().azi
)
541 +_speakers
[sorted_speakers
[0]].angles().azi
) <= AZIMUTH_DELTA_THRESHOLD_DEGREES
) {
542 if (calc_2D_inv_tmatrix(_speakers
[sorted_speakers
[n_speakers
-1]].angles().azi
,
543 _speakers
[sorted_speakers
[0]].angles().azi
,
544 inverse_matrix
[n_speakers
-1]) != 0) {
545 exists
[n_speakers
-1] = true;
553 _speaker_tuples
.clear ();
555 for (int n
= 0; n
< expected_pairs
; ++n
) {
556 _matrices
.push_back (twoDmatrix());
557 _speaker_tuples
.push_back (tmatrix());
560 for (speaker
= 0; speaker
< n_speakers
- 1; speaker
++) {
561 if (exists
[speaker
]) {
562 _matrices
[pair
][0] = inverse_matrix
[speaker
][0];
563 _matrices
[pair
][1] = inverse_matrix
[speaker
][1];
564 _matrices
[pair
][2] = inverse_matrix
[speaker
][2];
565 _matrices
[pair
][3] = inverse_matrix
[speaker
][3];
567 _speaker_tuples
[pair
][0] = sorted_speakers
[speaker
];
568 _speaker_tuples
[pair
][1] = sorted_speakers
[speaker
+1];
574 if (exists
[n_speakers
-1]) {
575 _matrices
[pair
][0] = inverse_matrix
[speaker
][0];
576 _matrices
[pair
][1] = inverse_matrix
[speaker
][1];
577 _matrices
[pair
][2] = inverse_matrix
[speaker
][2];
578 _matrices
[pair
][3] = inverse_matrix
[speaker
][3];
580 _speaker_tuples
[pair
][0] = sorted_speakers
[n_speakers
-1];
581 _speaker_tuples
[pair
][1] = sorted_speakers
[0];
586 VBAPSpeakers::sort_2D_lss (int* sorted_speakers
)
588 vector
<Speaker
> tmp
= _speakers
;
589 vector
<Speaker
>::iterator s
;
590 azimuth_sorter sorter
;
593 sort (tmp
.begin(), tmp
.end(), sorter
);
595 for (n
= 0, s
= tmp
.begin(); s
!= tmp
.end(); ++s
, ++n
) {
596 sorted_speakers
[n
] = (*s
).id
;
601 VBAPSpeakers::calc_2D_inv_tmatrix (double azi1
, double azi2
, double* inverse_matrix
)
606 x1
= cos (azi1
* (M_PI
/180.0));
607 x2
= sin (azi1
* (M_PI
/180.0));
608 x3
= cos (azi2
* (M_PI
/180.0));
609 x4
= sin (azi2
* (M_PI
/180.0));
610 det
= (x1
* x4
) - ( x3
* x2
);
612 if (fabs(det
) <= 0.001) {
614 inverse_matrix
[0] = 0.0;
615 inverse_matrix
[1] = 0.0;
616 inverse_matrix
[2] = 0.0;
617 inverse_matrix
[3] = 0.0;
623 inverse_matrix
[0] = x4
/ det
;
624 inverse_matrix
[1] = -x3
/ det
;
625 inverse_matrix
[2] = -x2
/ det
;
626 inverse_matrix
[3] = x1
/ det
;