aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT37
-rw-r--r--define_loudspeakers.c852
-rw-r--r--define_loudspeakers.dllbin0 -> 45056 bytes
-rwxr-xr-xdefine_loudspeakers.pd_irix6bin0 -> 29312 bytes
-rw-r--r--graph-to-aziele.pd51
-rw-r--r--high.pd27
-rw-r--r--makefile.irix12
-rw-r--r--makefile.nt89
-rw-r--r--playsample~.pd56
-rw-r--r--recent.pd14
-rw-r--r--so_locations6
-rw-r--r--vbap-demo.pd41
-rw-r--r--vbap-help.pd52
-rw-r--r--vbap.c650
-rw-r--r--vbap.dllbin0 -> 40960 bytes
-rw-r--r--vbap.main.pd92
-rwxr-xr-xvbap.pd_irix6bin0 -> 27025 bytes
-rw-r--r--vbapmodule.pd22
-rw-r--r--vbapsnd.pd68
19 files changed, 2069 insertions, 0 deletions
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..d22d3d1
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,37 @@
+Copyright
+
+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
+with these terms and conditions: Permission to use, copy, modify and
+distribute, including the right to grant others rights to distribute
+at any tier, this software and its documentation for any purpose and
+without fee or royalty is hereby granted, provided that you agree to
+comply with the following copyright notice and statements, including
+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-2002 by Ville Pulkki. All rights reserved.
+
+Written by Ville Pulkki
+Port to Pure Data by Juha Vehviläinen
+Helsinki University of Technology
+Laboratory of acoustics and audio signal processing
+
+
+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
+University of Technology or Ville Pulkki make no representations or
+warranties of merchantability or fitness for any particular purpose or
+that the use of the licensed software or documentation will not
+infringe any third party patents, copyrights, trademarks or other
+rights. The name of Ville Pulkki or Helsinki University of Technology
+may not be used in advertising or publicity pertaining to distribution
+of the software.
diff --git a/define_loudspeakers.c b/define_loudspeakers.c
new file mode 100644
index 0000000..1b1c757
--- /dev/null
+++ b/define_loudspeakers.c
@@ -0,0 +1,852 @@
+/* define_loudspeakers.c
+
+written by Ville Pulkki 1999
+Helsinki University of Technology
+and
+Unversity of California at Berkeley
+
+See copyright in file with name COPYRIGHT */
+
+#include <math.h>
+#include "m_pd.h" /* you must include this - it contains the external object's link to pure data */
+
+#define RES_ID 9172 /* resource ID for assistance (we'll add that later) */
+#define MAX_LS_AMOUNT 55 /* maximum amount of loudspeakers, can be raised */
+#define MIN_VOL_P_SIDE_LGTH 0.01
+
+#ifndef NT
+#define NULL 0L
+#endif
+#ifdef NT
+#define fabsf fabs
+#endif
+
+/* A struct for a loudspeaker instance */
+typedef struct { /* distance value is 1.0 == unit vectors */
+ float x; /* cartesian coordinates */
+ float y;
+ float z;
+ float azi; /* polar coordinates */
+ float ele;
+ int channel_nbr; /* which speaker channel number */
+} t_ls;
+
+/* A struct for all loudspeaker sets */
+typedef struct t_ls_set {
+ int ls_nos[3]; /* channel numbers */
+ float inv_mx[9]; /* inverse 3x3 or 2x2 matrix */
+ struct t_ls_set *next; /* next set (triplet or pair) */
+} t_ls_set;
+
+typedef struct /* This defines the object as an entity made up of other things */
+{
+ t_object x_ob; /* gotta say this... it creates a reference to your object */
+ long x_ls_read; /* 1 if loudspeaker directions have been read */
+ long x_triplets_specified; /* 1 if loudspeaker triplets have been chosen */
+ t_ls x_ls[MAX_LS_AMOUNT]; /* loudspeakers */
+ t_ls_set *x_ls_set; /* loudspeaker sets */
+ void *x_outlet0; /* outlet creation - inlets are automatic */
+ long x_ls_amount; /* number of loudspeakers */
+ long x_dimension; /* 2 (horizontal arrays) or 3 (3d setups) */
+} t_def_ls;
+
+static t_class *def_ls_class; /* so max can identify your object */
+void def_ls_bang(t_def_ls *x);
+void def_ls_int(t_def_ls *x, t_float n);
+void def_ls_read_directions(t_def_ls *x, t_symbol *s, int ac, t_atom *av);
+void def_ls_read_triplets(t_def_ls *x, t_symbol *s, int ac, t_atom *av);
+static void *def_ls_new(t_symbol *s, int ac, t_atom *av); /* using A_GIMME - typed message list */
+void def_ls(float g[3], long ls[3], t_def_ls *x);
+void ls_angles_to_cart(t_ls *ls);
+void choose_ls_triplets(t_def_ls *x);
+int any_ls_inside_triplet(int a, int b, int c,t_ls lss[MAX_LS_AMOUNT],int ls_amount);
+void add_ldsp_triplet(int i, int j, int k, t_def_ls *x);
+float vec_angle(t_ls v1, t_ls v2);
+float vec_length(t_ls v1);
+float vec_prod(t_ls v1, t_ls v2);
+float vec_prod(t_ls v1, t_ls v2);
+float vol_p_side_lgth(int i, int j,int k, t_ls lss[MAX_LS_AMOUNT] );
+void unq_cross_prod(t_ls v1,t_ls v2, t_ls *res);
+int lines_intersect(int i,int j,int k,int l,t_ls lss[MAX_LS_AMOUNT]);
+void calculate_3x3_matrixes(t_def_ls *x);
+void choose_ls_tuplets(t_def_ls *x);
+int calc_2D_inv_tmatrix(float azi1,float azi2, float inv_mat[4]);
+void sort_2D_lss(t_ls lss[MAX_LS_AMOUNT], int sorted_lss[MAX_LS_AMOUNT],
+ int ls_amount);
+
+/* above are the prototypes for the methods/procedures/functions you will use */
+
+void define_loudspeakers_setup(void)
+{
+ def_ls_class = class_new(gensym("define_loudspeakers"), (t_newmethod)def_ls_new, 0, (short)sizeof(t_def_ls), 0, A_GIMME, 0);
+ /* def_ls_new = creation function, A_DEFLONG = its (optional) arguement is a long (32-bit) int */
+/*
+/* addbang((method)def_ls_bang); /* the procedure it uses when it gets a bang in the left inlet */
+/* addint((method)def_ls_int); /* the rocedure for an int in the left inlet (inlet 0) */
+/* addmess((method)def_ls_read_directions, "ls-directions", A_GIMME, 0);
+/* addmess((method)def_ls_read_triplets, "ls-triplets", A_GIMME, 0);
+*/
+ class_addbang(def_ls_class, def_ls_bang);
+ class_addfloat(def_ls_class, def_ls_int);
+ class_addmethod(def_ls_class, (t_method)def_ls_read_directions, gensym("ls-directions"), A_GIMME, 0);
+ class_addmethod(def_ls_class, (t_method)def_ls_read_triplets, gensym("ls-triplets"), A_GIMME, 0);
+}
+
+
+void def_ls_bang(t_def_ls *x) /* x = reference to this instance of the object */
+{ /* calculate and print out chosen loudspeaker sets and corresponding matrices */
+ t_atom at[MAX_LS_AMOUNT*2+1];
+ float g[3];
+ long ls[3];
+ long i;
+
+ if(x->x_ls_read == 1){
+ if(x->x_ls_amount < x->x_dimension){
+ post("define-loudspeakers: Too few loudspeakers!",0);
+ return;
+ } else if(x->x_dimension == 3){
+ if(x->x_triplets_specified==0)
+ choose_ls_triplets(x);
+ calculate_3x3_matrixes(x);
+ } else if(x->x_dimension == 2)
+ choose_ls_tuplets(x);
+ else {
+ post("define-loudspeakers: Error in loudspeaker direction data");
+ post("dimension azimuth1 [elevation1] azimuth2 [elevation2]...");
+ post("dimension == 2 for horizontal ls arrays");
+ post("dimension == 3 for 3-D ls arrays (speakers also upward and/or downward ",0);
+ }
+ } else{
+ post("define-loudspeakers: Error in loudspeaker direction data",0);
+ post("dimension azimuth1 [elevation1] azimuth2 [elevation2]...",0);
+ post("dimension == 2 for horizontal ls arrays",0);
+ post("dimension == 3 for 3-D ls arrays (speakers also upward and/or downward ",0);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+
+void def_ls_int(t_def_ls *x, t_float n) /* x = the instance of the object, n = the int received in the right inlet */
+{
+ /* do something if an int comes in the left inlet??? */
+}
+
+void def_ls_read_triplets(t_def_ls *x, t_symbol *s, int ac, t_atom *av)
+/* when loudspeaker triplets come in a message */
+{
+ long l1,l2,l3,i;
+ t_ls_set *trip_ptr, *tmp_ptr, *prev;
+ if(x->x_ls_read == 0){
+ post("define_loudspeakers: Define loudspeaker directions first!",0);
+ return;
+ }
+
+ if(x->x_dimension == 2){
+ post("define_loudspeakers: Can't specify loudspeaker triplets in 2-D setup!",0);
+ return;
+ }
+
+ trip_ptr = x->x_ls_set;
+ prev = NULL;
+ while (trip_ptr != NULL){
+ tmp_ptr = trip_ptr;
+ trip_ptr = trip_ptr->next;
+ freebytes(tmp_ptr, sizeof (struct t_ls_set));
+ }
+ x->x_ls_set = NULL;
+
+ for(i=0;i<ac;i+=3){
+/* if(av[i].a_type == A_LONG)
+ l1 = av[i].a_w.w_long;
+ else */
+ if(av[i].a_type == A_FLOAT)
+ l1 = (long) av[i].a_w.w_float;
+/* if(av[i+1].a_type == A_LONG)
+ l2 = av[i+1].a_w.w_long;
+ else */
+ if(av[i+1].a_type == A_FLOAT)
+ l2 = (long) av[i+1].a_w.w_float;
+/* if(av[i+2].a_type == A_LONG)
+ l3 = av[i+2].a_w.w_long;
+ else */
+ if(av[i+2].a_type == A_FLOAT)
+ l3 = (long) av[i+2].a_w.w_float;
+ add_ldsp_triplet(l1-1,l2-1,l3-1,x);
+ }
+ x->x_triplets_specified=1;
+}
+
+
+
+void def_ls_read_directions(t_def_ls *x, t_symbol *s, int ac, t_atom *av)
+/* when loudspeaker directions come in a message */
+{
+ t_ls_set *trip_ptr, *prev, *tmp_ptr;
+ long i,pointer,newlyread;
+ newlyread = 0;
+/* if(av[0].a_type == A_LONG){
+ x->x_dimension= av[0].a_w.w_long;
+ newlyread = 1;
+ } else */
+ if(av[0].a_type == A_FLOAT) {
+ x->x_dimension= (int) av[0].a_w.w_float;
+ newlyread = 1;
+ } else x->x_dimension= 0;
+
+ if(x->x_dimension <2 || x->x_dimension >3){
+ post("define-loudspeakers: Dimension has to be 2 or 3!",0);
+ return;
+ }
+
+ pointer = 1;
+ x->x_ls_amount= (ac-1) / (x->x_dimension - 1);
+ for(i=0; i < x->x_ls_amount;i++){
+/* if(av[0].a_type == A_LONG)
+ x->x_ls[i].azi = (float) av[pointer].a_w.w_long;
+ else */
+ if(av[0].a_type == A_FLOAT)
+ x->x_ls[i].azi = av[pointer].a_w.w_float;
+ else {
+ post("define-loudspeakers: Error in loudspeaker data!",0);
+ newlyread =0;
+ return;
+ }
+ pointer++;
+ if(x->x_dimension == 3){
+/* if(av[0].a_type == A_LONG)
+ x->x_ls[i].ele = (float) av[pointer].a_w.w_long;
+ else */
+ if(av[0].a_type == A_FLOAT)
+ x->x_ls[i].ele = av[pointer].a_w.w_float;
+ else {
+ post("define-loudspeakers: Error in loudspeaker data!",0);
+ newlyread =0;
+ return;
+ }
+ pointer++;
+ } else
+ x->x_ls[i].ele = 0.0; /* 2-D case */
+ }
+
+ if(newlyread == 1){
+ x->x_ls_read = 1; /* preprocess data */
+ for(i=0;i<x->x_ls_amount;i++)
+ ls_angles_to_cart(&x->x_ls[i]);
+ trip_ptr = x->x_ls_set;
+ prev = NULL;
+ while (trip_ptr != NULL){ /* remove old matrices */
+ tmp_ptr = trip_ptr;
+ trip_ptr = trip_ptr->next;
+ freebytes(tmp_ptr, sizeof (struct t_ls_set));
+ }
+ x->x_ls_set = NULL;
+ }
+ x->x_triplets_specified=0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+void ls_angles_to_cart(t_ls *ls)
+/* convert angular direction to cartesian */
+{
+ float atorad = (2 * 3.1415927 / 360) ;
+ float azi = ls->azi;
+ float ele = ls->ele;
+ ls->x = cos((float) azi * atorad) * cos((float) ele * atorad);
+ ls->y = sin((float) azi * atorad) * cos((float) ele * atorad);
+ ls->z = sin((float) ele * atorad);
+}
+
+static void *def_ls_new(t_symbol *s, int ac, t_atom *av) /* create new instance of object... MUST
+ send it an int even if you do nothing with this int!! */
+{
+ long i,pointer;
+ t_def_ls *x;
+
+ x = (t_def_ls *)pd_new(def_ls_class);
+
+ x->x_ls_read = 0;
+/* if(av[0].a_type == A_LONG){
+ x->x_dimension= av[0].a_w.w_long;
+ x->x_ls_read = 1;
+ } else */
+ if(av[0].a_type == A_FLOAT) {
+ x->x_dimension= (int) av[0].a_w.w_float;
+ x->x_ls_read = 1;
+ } else x->x_dimension= 0;
+
+ if(x->x_dimension <2 || x->x_dimension >3){
+ post("define-loudspeakers: Dimension has to be 2 or 3!",0);
+ return(0);
+ }
+
+
+ pointer = 1;
+ x->x_ls_amount= (ac-1) / (x->x_dimension - 1);
+
+ /* read loudspeaker direction angles */
+ for(i=0; i < x->x_ls_amount;i++){
+/* if(av[0].a_type == A_LONG)
+ x->x_ls[i].azi = (float) av[pointer].a_w.w_long;
+ else */
+ if(av[0].a_type == A_FLOAT)
+ x->x_ls[i].azi = av[pointer].a_w.w_float;
+ else {
+ post("define-loudspeakers: Error in loudspeaker data!",0);
+ x->x_ls_read =0;
+ return;
+ }
+ pointer++;
+ if(x->x_dimension == 3){ /* 3-D */
+/* if(av[0].a_type == A_LONG)
+ x->x_ls[i].ele = (float) av[pointer].a_w.w_long;
+ else */
+ if(av[0].a_type == A_FLOAT)
+ x->x_ls[i].ele = av[pointer].a_w.w_float;
+ else {
+ post("define-loudspeakers: Error in loudspeaker data!",0);
+ x->x_ls_read =0;
+ return;
+ }
+ pointer++;
+ } else
+ x->x_ls[i].ele = 0.0; /* in 2d elevation is zero */
+ }
+
+ if(x->x_ls_read == 1)
+ for(i=0;i<x->x_ls_amount;i++)
+ ls_angles_to_cart(&x->x_ls[i]);
+ x->x_triplets_specified=0;
+ x->x_outlet0 = outlet_new(&x->x_ob, gensym("list")); /* create a (list) outlet */
+ x->x_ls_set = NULL;
+ return(x); /* return a reference to the object instance */
+}
+
+
+
+void choose_ls_triplets(t_def_ls *x)
+ /* 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
+ longer connection is erased. This yields non-intesecting triangles,
+ which can be used in panning.
+ 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
+ Auditory Displays -98.*/
+{
+ int i,j,k,l,m,li, table_size;
+ int *i_ptr;
+ t_ls vb1,vb2,tmp_vec;
+ int connections[MAX_LS_AMOUNT][MAX_LS_AMOUNT];
+ float angles[MAX_LS_AMOUNT];
+ int sorted_angles[MAX_LS_AMOUNT];
+ float distance_table[((MAX_LS_AMOUNT * (MAX_LS_AMOUNT - 1)) / 2)];
+ int distance_table_i[((MAX_LS_AMOUNT * (MAX_LS_AMOUNT - 1)) / 2)];
+ int distance_table_j[((MAX_LS_AMOUNT * (MAX_LS_AMOUNT - 1)) / 2)];
+ float distance;
+ t_ls_set *trip_ptr, *prev, *tmp_ptr;
+ int ls_amount = x->x_ls_amount;
+ t_ls *lss = x->x_ls;
+ if (ls_amount == 0) {
+ post("define-loudspeakers: Number of loudspeakers is zero",0);
+ return;
+ }
+
+ for(i=0;i<ls_amount;i++)
+ for(j=i+1;j<ls_amount;j++)
+ for(k=j+1;k<ls_amount;k++){
+ if(vol_p_side_lgth(i,j,k, x->x_ls) > MIN_VOL_P_SIDE_LGTH){
+ connections[i][j]=1;
+ connections[j][i]=1;
+ connections[i][k]=1;
+ connections[k][i]=1;
+ connections[j][k]=1;
+ connections[k][j]=1;
+ add_ldsp_triplet(i,j,k,x);
+ }
+ }
+
+ /*calculate distancies between all lss and sorting them*/
+ table_size =(((ls_amount - 1) * (ls_amount)) / 2);
+ for(i=0;i<table_size; i++)
+ distance_table[i] = 100000.0;
+ for(i=0;i<ls_amount;i++){
+ for(j=(i+1);j<ls_amount; j++){
+ if(connections[i][j] == 1) {
+ distance = fabs(vec_angle(lss[i],lss[j]));
+ k=0;
+ while(distance_table[k] < distance)
+ k++;
+ for(l=(table_size - 1);l > k ;l--){
+ distance_table[l] = distance_table[l-1];
+ distance_table_i[l] = distance_table_i[l-1];
+ distance_table_j[l] = distance_table_j[l-1];
+ }
+ distance_table[k] = distance;
+ distance_table_i[k] = i;
+ distance_table_j[k] = j;
+ } else
+ table_size--;
+ }
+ }
+
+ /* disconnecting connections which are crossing shorter ones,
+ starting from shortest one and removing all that cross it,
+ and proceeding to next shortest */
+ for(i=0; i<(table_size); i++){
+ int fst_ls = distance_table_i[i];
+ int sec_ls = distance_table_j[i];
+ if(connections[fst_ls][sec_ls] == 1)
+ for(j=0; j<ls_amount ; j++)
+ for(k=j+1; k<ls_amount; k++)
+ if( (j!=fst_ls) && (k != sec_ls) && (k!=fst_ls) && (j != sec_ls)){
+ if(lines_intersect(fst_ls, sec_ls, j,k,x->x_ls) == 1){
+ connections[j][k] = 0;
+ connections[k][j] = 0;
+ }
+ }
+ }
+
+ /* remove triangles which had crossing sides
+ with smaller triangles or include loudspeakers*/
+ trip_ptr = x->x_ls_set;
+ prev = NULL;
+ while (trip_ptr != NULL){
+ 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 ||
+ connections[j][k] == 0 ||
+ any_ls_inside_triplet(i,j,k,x->x_ls,ls_amount) == 1 ){
+ if(prev != NULL) {
+ prev->next = trip_ptr->next;
+ tmp_ptr = trip_ptr;
+ trip_ptr = trip_ptr->next;
+ freebytes(tmp_ptr, sizeof (struct t_ls_set));
+ } else {
+ x->x_ls_set = trip_ptr->next;
+ tmp_ptr = trip_ptr;
+ trip_ptr = trip_ptr->next;
+ freebytes(tmp_ptr, sizeof (struct t_ls_set));
+ }
+ } else {
+ prev = trip_ptr;
+ trip_ptr = trip_ptr->next;
+
+ }
+ }
+ x->x_triplets_specified=1;
+}
+
+
+int any_ls_inside_triplet(int a, int b, int c,t_ls lss[MAX_LS_AMOUNT],int ls_amount)
+ /* returns 1 if there is loudspeaker(s) inside given ls triplet */
+{
+ float invdet;
+ t_ls *lp1, *lp2, *lp3;
+ float invmx[9];
+ int i,j,k;
+ float tmp;
+ int any_ls_inside, this_inside;
+
+ lp1 = &(lss[a]);
+ lp2 = &(lss[b]);
+ lp3 = &(lss[c]);
+
+ /* 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;
+ invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
+ invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
+ invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
+ 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 = 0;
+ for(i=0; i< ls_amount; i++) {
+ if (i != a && i!=b && i != c){
+ this_inside = 1;
+ for(j=0; j< 3; j++){
+ tmp = lss[i].x * invmx[0 + j*3];
+ tmp += lss[i].y * invmx[1 + j*3];
+ tmp += lss[i].z * invmx[2 + j*3];
+ if(tmp < -0.001)
+ this_inside = 0;
+ }
+ if(this_inside == 1)
+ any_ls_inside=1;
+ }
+ }
+ return any_ls_inside;
+}
+
+void add_ldsp_triplet(int i, int j, int k, t_def_ls *x)
+ /* adds i,j,k triplet to structure*/
+{
+ struct t_ls_set *trip_ptr, *prev;
+ trip_ptr = x->x_ls_set;
+ prev = NULL;
+ while (trip_ptr != NULL){
+ prev = trip_ptr;
+ trip_ptr = trip_ptr->next;
+ }
+ trip_ptr = (struct t_ls_set*)
+ getbytes (sizeof (struct t_ls_set));
+ if(prev == NULL)
+ x->x_ls_set = trip_ptr;
+ else
+ prev->next = trip_ptr;
+ trip_ptr->next = NULL;
+ trip_ptr->ls_nos[0] = i;
+ trip_ptr->ls_nos[1] = j;
+ trip_ptr->ls_nos[2] = k;
+}
+
+
+
+
+float vec_angle(t_ls v1, t_ls v2)
+/* angle between two loudspeakers */
+{
+ float 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;
+ if (inner < -1.0)
+ inner = -1.0;
+ return fabs( acos( inner));
+}
+
+float vec_length(t_ls v1)
+/* length of a vector */
+{
+ return (sqrt(v1.x*v1.x + v1.y*v1.y + v1.z*v1.z));
+}
+
+float vec_prod(t_ls v1, t_ls v2)
+/* vector dot product */
+{
+ return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
+}
+
+
+float vol_p_side_lgth(int i, int j,int k, t_ls lss[MAX_LS_AMOUNT] ){
+ /* calculate volume of the parallelepiped defined by the loudspeaker
+ direction vectors and divide it with total length of the triangle sides.
+ This is used when removing too narrow triangles. */
+
+ float volper, lgth;
+ t_ls xprod;
+ unq_cross_prod(lss[i], lss[j], &xprod);
+ volper = fabsf(vec_prod(xprod, lss[k]));
+ lgth = (fabsf(vec_angle(lss[i],lss[j]))
+ + fabsf(vec_angle(lss[i],lss[k]))
+ + fabsf(vec_angle(lss[j],lss[k])));
+ if(lgth>0.00001)
+ return volper / lgth;
+ else
+ return 0.0;
+}
+
+void unq_cross_prod(t_ls v1,t_ls v2,
+ t_ls *res)
+/* vector cross product */
+{
+ float 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);
+ length= vec_length(*res);
+ res->x /= length;
+ res->y /= length;
+ res->z /= length;
+}
+
+
+int lines_intersect(int i,int j,int k,int l,t_ls lss[MAX_LS_AMOUNT])
+ /* checks if two lines intersect on 3D sphere
+ */
+{
+ t_ls v1;
+ t_ls v2;
+ t_ls v3, neg_v3;
+ float angle;
+ float dist_ij,dist_kl,dist_iv3,dist_jv3,dist_inv3,dist_jnv3;
+ float dist_kv3,dist_lv3,dist_knv3,dist_lnv3;
+
+ unq_cross_prod(lss[i],lss[j],&v1);
+ unq_cross_prod(lss[k],lss[l],&v2);
+ unq_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;
+
+ dist_ij = (vec_angle(lss[i],lss[j]));
+ dist_kl = (vec_angle(lss[k],lss[l]));
+ dist_iv3 = (vec_angle(lss[i],v3));
+ dist_jv3 = (vec_angle(v3,lss[j]));
+ dist_inv3 = (vec_angle(lss[i],neg_v3));
+ dist_jnv3 = (vec_angle(neg_v3,lss[j]));
+ dist_kv3 = (vec_angle(lss[k],v3));
+ dist_lv3 = (vec_angle(v3,lss[l]));
+ dist_knv3 = (vec_angle(lss[k],neg_v3));
+ dist_lnv3 = (vec_angle(neg_v3,lss[l]));
+
+ /* if one of loudspeakers is close to crossing point, don't do anything*/
+ 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_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) &&
+ (fabsf(dist_kl - (dist_knv3 + dist_lnv3)) <= 0.01 ))) {
+ return (1);
+ } else {
+ return (0);
+ }
+}
+
+void calculate_3x3_matrixes(t_def_ls *x)
+ /* Calculates the inverse matrices for 3D */
+{
+ float invdet;
+ t_ls *lp1, *lp2, *lp3;
+ float *invmx;
+ float *ptr;
+ struct t_ls_set *tr_ptr = x->x_ls_set;
+ int triplet_amount = 0, ftable_size,i,j,k, pointer,list_length=0;
+ t_atom *at;
+ t_ls *lss = x->x_ls;
+
+ if (tr_ptr == NULL){
+ post("define-loudspeakers: Not valid 3-D configuration\n",1);
+ return;
+ }
+
+ /* counting triplet amount */
+ while(tr_ptr != NULL){
+ triplet_amount++;
+ tr_ptr = tr_ptr->next;
+ }
+ tr_ptr = x->x_ls_set;
+ list_length= triplet_amount * 21 + 2; /* was: + 3, pure data doesn't like errors on list_length */
+ at= (t_atom *) getbytes(list_length*sizeof(t_atom));
+
+ SETFLOAT(&at[0], x->x_dimension);
+ SETFLOAT(&at[1], x->x_ls_amount);
+ pointer=2;
+
+ while(tr_ptr != NULL){
+ lp1 = &(lss[tr_ptr->ls_nos[0]]);
+ lp2 = &(lss[tr_ptr->ls_nos[1]]);
+ lp3 = &(lss[tr_ptr->ls_nos[2]]);
+
+ /* 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;
+ invmx[1] = ((lp2->x * lp3->z) - (lp2->z * lp3->x)) * -invdet;
+ invmx[4] = ((lp1->x * lp3->z) - (lp1->z * lp3->x)) * invdet;
+ invmx[7] = ((lp1->x * lp2->z) - (lp1->z * lp2->x)) * -invdet;
+ 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;
+ for(i=0;i<3;i++){
+ SETFLOAT(&at[pointer], tr_ptr->ls_nos[i]+1);
+ pointer++;
+ }
+ for(i=0;i<9;i++){
+ SETFLOAT(&at[pointer], invmx[i]);
+ pointer++;
+ }
+ SETFLOAT(&at[pointer], lp1->x); pointer++;
+ SETFLOAT(&at[pointer], lp2->x); pointer++;
+ SETFLOAT(&at[pointer], lp3->x); pointer++;
+ SETFLOAT(&at[pointer], lp1->y); pointer++;
+ SETFLOAT(&at[pointer], lp2->y); pointer++;
+ SETFLOAT(&at[pointer], lp3->y); pointer++;
+ SETFLOAT(&at[pointer], lp1->z); pointer++;
+ SETFLOAT(&at[pointer], lp2->z); pointer++;
+ SETFLOAT(&at[pointer], lp3->z); pointer++;
+
+ tr_ptr = tr_ptr->next;
+ }
+ outlet_anything(x->x_outlet0, gensym("loudspeaker-matrices"), list_length, at);
+ freebytes(at, list_length*sizeof(t_atom));
+}
+
+
+
+void choose_ls_tuplets(t_def_ls *x)
+ /* selects the loudspeaker pairs, calculates the inversion
+ matrices and stores the data to a global array*/
+{
+ float atorad = (2 * 3.1415927 / 360) ;
+ int i,j,k;
+ float w1,w2;
+ float p1,p2;
+ int sorted_lss[MAX_LS_AMOUNT];
+ int exist[MAX_LS_AMOUNT];
+ int amount=0;
+ float inv_mat[MAX_LS_AMOUNT][4]; /* In 2-D ls amount == max amount of LS pairs */
+ float *ptr;
+ float *ls_table;
+ t_ls *lss = x->x_ls;
+ long ls_amount=x->x_ls_amount;
+ long list_length;
+ t_atom *at;
+ long pointer;
+
+ for(i=0;i<MAX_LS_AMOUNT;i++){
+ exist[i]=0;
+ }
+
+ /* sort loudspeakers according their aximuth angle */
+ sort_2D_lss(x->x_ls,sorted_lss,ls_amount);
+
+ /* adjacent loudspeakers are the loudspeaker pairs to be used.*/
+ for(i=0;i<(ls_amount-1);i++){
+ if((lss[sorted_lss[i+1]].azi -
+ lss[sorted_lss[i]].azi) <= (180 - 10)){
+ if (calc_2D_inv_tmatrix( lss[sorted_lss[i]].azi,
+ lss[sorted_lss[i+1]].azi,
+ inv_mat[i]) != 0){
+ exist[i]=1;
+ amount++;
+ }
+ }
+ }
+
+ if(((6.283 - lss[sorted_lss[ls_amount-1]].azi)
+ +lss[sorted_lss[0]].azi) <= (180 - 10)) {
+ if(calc_2D_inv_tmatrix(lss[sorted_lss[ls_amount-1]].azi,
+ lss[sorted_lss[0]].azi,
+ inv_mat[ls_amount-1]) != 0) {
+ exist[ls_amount-1]=1;
+ amount++;
+ }
+ }
+
+
+ /* Output */
+ list_length= amount * 6 + 2;
+ at= (t_atom *) getbytes(list_length*sizeof(t_atom));
+
+ SETFLOAT(&at[0], x->x_dimension);
+ SETFLOAT(&at[1], x->x_ls_amount);
+ pointer=2;
+
+ for (i=0;i<ls_amount - 1;i++){
+ if(exist[i] == 1) {
+ SETFLOAT(&at[pointer], sorted_lss[i]+1);
+ pointer++;
+ SETFLOAT(&at[pointer], sorted_lss[i+1]+1);
+ pointer++;
+ for(j=0;j<4;j++) {
+ SETFLOAT(&at[pointer], inv_mat[i][j]);
+ pointer++;
+ }
+ }
+ }
+ if(exist[ls_amount-1] == 1) {
+ SETFLOAT(&at[pointer], sorted_lss[ls_amount-1]+1);
+ pointer++;
+ SETFLOAT(&at[pointer], sorted_lss[0]+1);
+ pointer++;
+ for(j=0;j<4;j++) {
+ SETFLOAT(&at[pointer], inv_mat[ls_amount-1][j]);
+ pointer++;
+ }
+ }
+ outlet_anything(x->x_outlet0, gensym("loudspeaker-matrices"), list_length, at);
+ freebytes(at, list_length*sizeof(t_atom));
+}
+
+void sort_2D_lss(t_ls lss[MAX_LS_AMOUNT], int sorted_lss[MAX_LS_AMOUNT],
+ int ls_amount)
+/* sort loudspeakers according to azimuth angle */
+{
+ int i,j,index;
+ float tmp, tmp_azi;
+ float rad2ang = 360.0 / ( 2 * 3.141592 );
+
+ float x,y;
+ /* Transforming angles between -180 and 180 */
+ for (i=0;i<ls_amount;i++) {
+ ls_angles_to_cart(&lss[i]);
+ lss[i].azi = acos( lss[i].x) * rad2ang;
+ if (fabs(lss[i].y) <= 0.001)
+ tmp = 1.0;
+ else
+ tmp = lss[i].y / fabs(lss[i].y);
+ lss[i].azi *= tmp;
+ }
+ for (i=0;i<ls_amount;i++){
+ tmp = 2000;
+ for (j=0 ; j<ls_amount;j++){
+ if (lss[j].azi <= tmp){
+ tmp=lss[j].azi;
+ index = j;
+ }
+ }
+ sorted_lss[i]=index;
+ tmp_azi = (lss[index].azi);
+ lss[index].azi = (tmp_azi + (float) 4000.0);
+ }
+ for (i=0;i<ls_amount;i++) {
+ tmp_azi = (lss[i].azi);
+ lss[i].azi = (tmp_azi - (float) 4000.0);
+ }
+}
+
+
+int calc_2D_inv_tmatrix(float azi1,float azi2, float inv_mat[4])
+/* calculate inverse 2x2 matrix */
+{
+ float x1,x2,x3,x4; /* x1 x3 */
+ float y1,y2,y3,y4; /* x2 x4 */
+ float det;
+ float rad2ang = 360.0 / ( 2 * 3.141592 );
+
+ x1 = cos(azi1 / rad2ang);
+ x2 = sin(azi1 / rad2ang);
+ x3 = cos(azi2 / rad2ang);
+ x4 = sin(azi2 / rad2ang);
+ det = (x1 * x4) - ( x3 * x2 );
+ if(fabsf(det) <= 0.001) {
+
+ inv_mat[0] = 0.0;
+ inv_mat[1] = 0.0;
+ inv_mat[2] = 0.0;
+ inv_mat[3] = 0.0;
+ return 0;
+ } else {
+ inv_mat[0] = (x4 / det);
+ inv_mat[1] = (-x3 / det);
+ inv_mat[2] = (-x2 / det);
+ inv_mat[3] = (x1 / det);
+ return 1;
+ }
+}
+
+
diff --git a/define_loudspeakers.dll b/define_loudspeakers.dll
new file mode 100644
index 0000000..bf1380e
--- /dev/null
+++ b/define_loudspeakers.dll
Binary files differ
diff --git a/define_loudspeakers.pd_irix6 b/define_loudspeakers.pd_irix6
new file mode 100755
index 0000000..ead3d7b
--- /dev/null
+++ b/define_loudspeakers.pd_irix6
Binary files differ
diff --git a/graph-to-aziele.pd b/graph-to-aziele.pd
new file mode 100644
index 0000000..0597f8a
--- /dev/null
+++ b/graph-to-aziele.pd
@@ -0,0 +1,51 @@
+#N canvas 549 17 647 652 12;
+#X obj 42 505 expr acos($f1/$f3) * 180 / 3.1415 * $f2;
+#X obj 323 366 expr sqrt($f1);
+#X obj 323 338 + 0;
+#X obj 78 313 * 0;
+#X obj 343 276 * 0;
+#X msg 460 505 0;
+#X msg 233 265 -1;
+#X msg 268 265 1;
+#X obj 42 249 expr ($f1-0.5)/0.5;
+#X obj 233 145 expr ($f1-0.5)/0.5;
+#X obj 42 16 inlet;
+#X obj 42 570 outlet;
+#X obj 488 569 outlet;
+#X obj 407 431 expr 90 - ($f1*90.0);
+#X obj 460 479 moses 0;
+#X obj 233 238 moses 0;
+#X obj 42 279 t f f f;
+#X obj 233 179 t f f f;
+#X obj 42 99 unpack f f;
+#X msg 42 67 \$2 \$1;
+#X text 101 66 reverse order;
+#X text 101 16 float x \, float y;
+#X obj 323 393 t f f;
+#X connect 0 0 11 0;
+#X connect 1 0 22 0;
+#X connect 2 0 1 0;
+#X connect 3 0 2 0;
+#X connect 4 0 2 1;
+#X connect 5 0 12 0;
+#X connect 6 0 0 1;
+#X connect 7 0 0 1;
+#X connect 8 0 16 0;
+#X connect 9 0 17 0;
+#X connect 10 0 19 0;
+#X connect 13 0 14 0;
+#X connect 14 0 5 0;
+#X connect 14 1 12 0;
+#X connect 15 0 6 0;
+#X connect 15 1 7 0;
+#X connect 16 0 0 0;
+#X connect 16 1 3 0;
+#X connect 16 2 3 1;
+#X connect 17 0 15 0;
+#X connect 17 1 4 0;
+#X connect 17 2 4 1;
+#X connect 18 0 8 0;
+#X connect 18 1 9 0;
+#X connect 19 0 18 0;
+#X connect 22 0 0 2;
+#X connect 22 1 13 0;
diff --git a/high.pd b/high.pd
new file mode 100644
index 0000000..f6bbfba
--- /dev/null
+++ b/high.pd
@@ -0,0 +1,27 @@
+#N canvas 0 0 576 425 10;
+#X obj 102 43 inlet;
+#X obj 124 188 f 0;
+#X obj 102 239 >;
+#X obj 102 281 sel 1;
+#X obj 201 158 f;
+#X obj 203 354 outlet;
+#X obj 203 267 f;
+#X obj 292 42 inlet;
+#X text 339 42 reset to float;
+#X text 148 44 test against high;
+#X obj 102 91 t b f b f;
+#X obj 292 89 t b f;
+#X connect 0 0 10 0;
+#X connect 1 0 2 1;
+#X connect 1 0 6 1;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 4 0 1 0;
+#X connect 6 0 5 0;
+#X connect 7 0 11 0;
+#X connect 10 0 6 0;
+#X connect 10 1 2 0;
+#X connect 10 2 1 0;
+#X connect 10 3 4 1;
+#X connect 11 0 6 0;
+#X connect 11 1 1 0;
diff --git a/makefile.irix b/makefile.irix
new file mode 100644
index 0000000..6960439
--- /dev/null
+++ b/makefile.irix
@@ -0,0 +1,12 @@
+FLAGS = -DPD -DUNIX -DIRIX -DN32 -O2 -w
+INCLUDE = -I../pd/src -I /usr/local/include
+
+all: vbap_irix6 definels_irix6
+
+vbap_irix6:
+ cc $(FLAGS) $(INCLUDE) -o vbap.o -c vbap.c
+ ld -elf -shared -rdata_shared -o vbap.pd_irix6 vbap.o
+
+definels_irix6:
+ cc $(FLAGS) $(INCLUDE) -o define_loudspeakers.o -c define_loudspeakers.c
+ ld -elf -shared -rdata_shared -o define_loudspeakers.pd_irix6 define_loudspeakers.o
diff --git a/makefile.nt b/makefile.nt
new file mode 100644
index 0000000..bd0b6f3
--- /dev/null
+++ b/makefile.nt
@@ -0,0 +1,89 @@
+NAME=vbap
+CSYM=vbap
+NAMEB=define_loudspeakers
+CSYMB=define_loudspeakers
+
+current: pd_nt
+
+# ----------------------- NT -----------------------
+
+pd_nt: $(NAME).dll
+
+.SUFFIXES: .dll
+
+PDNTCFLAGS = /W3 /WX /O2 /G6 /DNT /DPD /nologo
+VC="C:\Programme\Microsoft Visual Studio\VC98"
+
+PDNTINCLUDE = /I. /Ic:\pd\tcl\include /Ic:\pd\src /I$(VC)\include /Iinclude
+
+PDNTLDIR = $(VC)\Lib
+PDNTLIB = $(PDNTLDIR)\libc.lib \
+ $(PDNTLDIR)\oldnames.lib \
+ $(PDNTLDIR)\kernel32.lib \
+ $(PDNTLDIR)\user32.lib \
+ $(PDNTLDIR)\uuid.lib \
+ $(PDNTLDIR)\ws2_32.lib \
+ c:\pd\bin\pd.lib \
+
+.c.dll:
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $(NAME).c
+ cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $(NAMEB).c
+ link /dll /export:$(CSYM)_setup $(NAME).obj $(PDNTLIB)
+ link /dll /export:$(CSYMB)_setup $(NAMEB).obj $(PDNTLIB)
+
+# ----------------------- IRIX 5.x -----------------------
+
+pd_irix5: $(NAME).pd_irix5
+
+.SUFFIXES: .pd_irix5
+
+SGICFLAGS5 = -o32 -DPD -DUNIX -DIRIX -O2
+
+SGIINCLUDE = -I../../src
+
+.c.pd_irix5:
+ cc $(SGICFLAGS5) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -elf -shared -rdata_shared -o $*.pd_irix5 $*.o
+ rm $*.o
+
+# ----------------------- IRIX 6.x -----------------------
+
+pd_irix6: $(NAME).pd_irix6
+
+.SUFFIXES: .pd_irix6
+
+SGICFLAGS6 = -n32 -DPD -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \
+ -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \
+ -Ofast=ip32
+
+.c.pd_irix6:
+ cc $(SGICFLAGS6) $(SGIINCLUDE) -o $*.o -c $*.c
+ ld -n32 -IPA -shared -rdata_shared -o $*.pd_irix6 $*.o
+ rm $*.o
+
+# ----------------------- LINUX i386 -----------------------
+
+pd_linux: $(NAME).pd_linux
+
+.SUFFIXES: .pd_linux
+
+LINUXCFLAGS = -DPD -DUNIX -O2 -funroll-loops -fomit-frame-pointer \
+ -Wall -W -Wshadow -Wstrict-prototypes -Werror \
+ -Wno-unused -Wno-parentheses -Wno-switch
+
+LINUXINCLUDE = -I../../src
+
+.c.pd_linux:
+ cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c
+ ld -export_dynamic -shared -o $*.pd_linux $*.o -lc -lm -L/usr/local/lib
+ strip --strip-unneeded $*.pd_linux
+ rm -f $*.o ../$*.pd_linux
+ ln -s $*/$*.pd_linux ..
+
+# ----------------------------------------------------------
+
+install:
+ cp help-*.pd ../../doc/5.reference
+
+clean:
+ rm -f *.o *.pd_* so_locations
diff --git a/playsample~.pd b/playsample~.pd
new file mode 100644
index 0000000..bf2b06e
--- /dev/null
+++ b/playsample~.pd
@@ -0,0 +1,56 @@
+#N canvas 27 300 849 403 10;
+#X obj 38 354 table \$0music;
+#X obj 148 160 soundfiler;
+#X floatatom 148 183 5 0 0;
+#X obj 243 207 phasor~ 1;
+#X floatatom 266 181 8 0 0;
+#X obj 243 237 *~ 0;
+#X msg 148 137 read -resize \$1 \$2;
+#X obj 199 83 symbol \$0music;
+#X obj 148 110 pack s s;
+#X obj 199 48 loadbang;
+#X obj 12 13 inlet;
+#X text 59 12 .aiff file name;
+#X obj 311 15 inlet;
+#X text 356 15 phasor speed;
+#X obj 243 334 outlet~;
+#X obj 243 267 tabread4~ \$0music;
+#X obj 302 83 samplerate~;
+#X floatatom 310 107 5 0 0;
+#X obj 460 14 inlet;
+#X text 505 14 gain;
+#X obj 243 306 *~ 1;
+#X obj 148 210 t b f;
+#X obj 148 232 f;
+#X obj 148 254 /;
+#X obj 419 334 outlet~;
+#X text 303 333 sample signal;
+#X text 483 334 phasor signal;
+#X msg 557 41 0;
+#X obj 557 16 inlet;
+#X text 601 16 restart;
+#X connect 1 0 2 0;
+#X connect 2 0 5 1;
+#X connect 2 0 21 0;
+#X connect 3 0 5 0;
+#X connect 3 0 24 0;
+#X connect 4 0 3 0;
+#X connect 5 0 15 0;
+#X connect 6 0 1 0;
+#X connect 7 0 8 1;
+#X connect 8 0 6 0;
+#X connect 9 0 7 0;
+#X connect 9 0 16 0;
+#X connect 10 0 8 0;
+#X connect 12 0 3 0;
+#X connect 15 0 20 0;
+#X connect 16 0 17 0;
+#X connect 16 0 22 1;
+#X connect 18 0 20 1;
+#X connect 20 0 14 0;
+#X connect 21 0 22 0;
+#X connect 21 1 23 1;
+#X connect 22 0 23 0;
+#X connect 23 0 4 0;
+#X connect 27 0 3 1;
+#X connect 28 0 27 0;
diff --git a/recent.pd b/recent.pd
new file mode 100644
index 0000000..add6c7d
--- /dev/null
+++ b/recent.pd
@@ -0,0 +1,14 @@
+#N canvas 399 148 452 302 12;
+#X obj 56 55 inlet;
+#X msg 91 123 clear;
+#X obj 59 252 outlet;
+#X obj 166 54 inlet;
+#X text 8 5 output only most "recent" messages;
+#X obj 56 89 t a b;
+#X obj 59 202 pipe \$1;
+#X connect 0 0 5 0;
+#X connect 1 0 6 0;
+#X connect 3 0 6 1;
+#X connect 5 0 6 0;
+#X connect 5 1 1 0;
+#X connect 6 0 2 0;
diff --git a/so_locations b/so_locations
new file mode 100644
index 0000000..0edc062
--- /dev/null
+++ b/so_locations
@@ -0,0 +1,6 @@
+vbap.pd_irix6 \
+ :st = .text 0x5ffe0000, 0x00010000:\
+ :st = .data 0x5fff0000, 0x00010000:
+define_loudspeakers.pd_irix6 \
+ :st = .text 0x5ffc0000, 0x00010000:\
+ :st = .data 0x5ffd0000, 0x00010000:
diff --git a/vbap-demo.pd b/vbap-demo.pd
new file mode 100644
index 0000000..027840a
--- /dev/null
+++ b/vbap-demo.pd
@@ -0,0 +1,41 @@
+#N canvas 11 10 871 352 10;
+#X obj 49 298 vbap.main;
+#X obj 50 80 playsample~;
+#X obj 50 55 openpanel;
+#X msg 50 31 bang;
+#X text 96 30 click to load and play a sample;
+#X floatatom 133 80 5 0 0;
+#X floatatom 133 101 5 0 0;
+#X text 182 80 pitch;
+#X text 182 102 volume;
+#X floatatom 76 142 5 0 0;
+#X floatatom 122 142 5 0 0;
+#X floatatom 168 142 5 0 0;
+#X text 218 143 set azimuth \, elevation \, spread;
+#N canvas 152 420 615 353 using 0;
+#X obj 59 258 graph-to-aziele;
+#X obj 59 233 pack f f;
+#X floatatom 59 281 5 0 0;
+#X obj 59 78 grid grid1 144 0 1 144 0 1 0 0.001 0.001 10 10 103 91
+;
+#X floatatom 159 281 5 0 0;
+#X text 21 17 To use Yves Degoyon's GRID (http://ydegoyon.free.fr)
+use graph-to-aziele.pd to count the azimuth and elevation.;
+#X text 25 49 (note: GRID must output values between 0 - 1);
+#X connect 0 0 2 0;
+#X connect 0 1 4 0;
+#X connect 1 0 0 0;
+#X connect 3 0 1 0;
+#X connect 3 1 1 1;
+#X restore 506 112 pd using GRID with vbap;
+#X text 130 292 define loudspeakers \, receive signals and data from
+vbap \, output audio;
+#X obj 50 174 vbapmodule 1;
+#X connect 1 0 15 0;
+#X connect 2 0 1 0;
+#X connect 3 0 2 0;
+#X connect 5 0 1 1;
+#X connect 6 0 1 2;
+#X connect 9 0 15 1;
+#X connect 10 0 15 2;
+#X connect 11 0 15 3;
diff --git a/vbap-help.pd b/vbap-help.pd
new file mode 100644
index 0000000..7a1f979
--- /dev/null
+++ b/vbap-help.pd
@@ -0,0 +1,52 @@
+#N canvas 91 360 949 493 10;
+#X obj 112 257 vbap 0 0;
+#X obj 134 319 print;
+#X obj 62 113 define_loudspeakers 3 -45 0 45 0 0 45 180 45;
+#X msg 62 87 bang;
+#X text 442 79 1 Use define_loudspeakers to list the speaker positions.
+The example here defines loudspeakers in three dimensions (the first
+parameter). For each speaker \, define its azimuth and elevation. Here
+we have speakers front left and right with no elevation (-45 0 45 0)
+and front and back with 45 degrees of elevation (0 45 180 45). Send
+the data to vbap.;
+#X floatatom 129 223 5 0 0;
+#X floatatom 173 223 5 0 0;
+#X floatatom 217 223 5 0 0;
+#X msg 112 179 bang;
+#X obj 112 356 route 0 1 2 3;
+#X floatatom 112 415 10 0 0;
+#X floatatom 191 415 10 0 0;
+#X floatatom 270 415 10 0 0;
+#X floatatom 349 415 10 0 0;
+#X text 442 169 In two dimensions \, only specify the azimuth. (for
+example "define_loudspeakers 2 -45 45 0 180";
+#X text 151 201 azimuth \, elevation and spread;
+#X text 444 352 For an example of how to use vbap with matrix~ from
+zexy-library \, see vbap-demo.pd.;
+#X text 63 21 VBAP and define_loudspeakers;
+#X text 444 300 The spread-parameter can be used to prevent a situation
+where sound is coming from one speaker only \, which would make speaker
+positions "visible". The range is 0 to 100;
+#X floatatom 152 286 5 0 0;
+#X floatatom 196 286 5 0 0;
+#X floatatom 240 286 5 0 0;
+#X text 178 306 actual location;
+#X text 444 222 2 For vbap \, give azimuth and elevation for the desired
+location. Bang the first inlet and vbap will output gain-factors for
+each speaker and the actual location produced. This can be different
+from the desired one depending where your speakers are.;
+#X connect 0 0 1 0;
+#X connect 0 0 9 0;
+#X connect 0 1 19 0;
+#X connect 0 2 20 0;
+#X connect 0 3 21 0;
+#X connect 2 0 0 0;
+#X connect 3 0 2 0;
+#X connect 5 0 0 1;
+#X connect 6 0 0 2;
+#X connect 7 0 0 3;
+#X connect 8 0 0 0;
+#X connect 9 0 10 0;
+#X connect 9 1 11 0;
+#X connect 9 2 12 0;
+#X connect 9 3 13 0;
diff --git a/vbap.c b/vbap.c
new file mode 100644
index 0000000..507ef39
--- /dev/null
+++ b/vbap.c
@@ -0,0 +1,650 @@
+/* vbap.c vers 0.99 for max4.0
+
+written by Ville Pulkki 1999-2001
+Helsinki University of Technology
+and
+Unversity of California at Berkeley
+
+See copyright in file with name COPYRIGHT */
+
+#ifdef NT
+#define sqrtf sqrt
+#endif
+
+#include <math.h>
+#include "m_pd.h" /* you must include this - it contains the external object's link to pure data */
+
+#define RES_ID 9171 /* resource ID for assistance (we'll add that later) */
+#define MAX_LS_SETS 100 /* maximum number of loudspeaker sets (triplets or pairs) allowed */
+#define MAX_LS_AMOUNT 55 /* maximum amount of loudspeakers, can be increased */
+
+typedef struct vbap /* This defines the object as an entity made up of other things */
+{
+ t_object x_ob;
+ t_float x_azi; /* panning direction azimuth */
+ t_float x_ele; /* panning direction elevation */
+ void *x_outlet0; /* outlet creation - inlets are automatic */
+ void *x_outlet1;
+ void *x_outlet2;
+ void *x_outlet3;
+ float x_set_inv_matx[MAX_LS_SETS][9]; /* inverse matrice for each loudspeaker set */
+ float x_set_matx[MAX_LS_SETS][9]; /* matrice for each loudspeaker set */
+ long x_lsset[MAX_LS_SETS][3]; /* channel numbers of loudspeakers in each LS set */
+ long x_lsset_available; /* have loudspeaker sets been defined with define_loudspeakers */
+ long x_lsset_amount; /* amount of loudspeaker sets */
+ long x_ls_amount; /* amount of loudspeakers */
+ long x_dimension; /* 2 or 3 */
+ t_float x_spread; /* speading amount of virtual source (0-100) */
+ float x_spread_base[3]; /* used to create uniform spreading */
+} t_vbap;
+
+/* Globals */
+
+void new_spread_dir(t_vbap *x, float spreaddir[3], float vscartdir[3], float spread_base[3]);
+void new_spread_base(t_vbap *x, float spreaddir[3], float vscartdir[3]);
+static t_class *vbap_class;
+void cross_prod(float v1[3], float v2[3],
+ float v3[3]);
+void additive_vbap(float *final_gs, float cartdir[3], t_vbap *x);
+void vbap_bang(t_vbap *x);
+void vbap_int(t_vbap *x, t_float n);
+void vbap_matrix(t_vbap *x, t_symbol *s, int ac, t_atom *av);
+void vbap_in1(t_vbap *x, long n);
+void vbap_in2(t_vbap *x, long n);
+void vbap_in3(t_vbap *x, long n);
+void spread_it(t_vbap *x, float *final_gs);
+static void *vbap_new(t_symbol *s, int ac, t_atom *av); /* using A_GIMME - typed message list */
+void vbap(float g[3], long ls[3], t_vbap *x);
+void angle_to_cart(long azi, long ele, float res[3]);
+void cart_to_angle(float cvec[3], float avec[3]);
+
+/* above are the prototypes for the methods/procedures/functions you will use */
+
+void vbap_setup(void)
+{
+ vbap_class = class_new(gensym("vbap"), (t_newmethod)vbap_new, 0, (short)sizeof(t_vbap), 0, A_GIMME, 0);
+ /* vbap_new = creation function, A_DEFLONG = its (optional) arguement is a long (32-bit) int */
+
+/* max methods ... */
+/*
+/* addbang((method)vbap_bang); /* the procedure it uses when it gets a bang in the left inlet */
+/* addint((method)vbap_int); /* the rocedure for an int in the left inlet (inlet 0) */
+/* addinx((method)vbap_in1, 1); /* the rocedure for an int in the right inlet (inlet 1) */
+/* addinx((method)vbap_in2, 2); /* the rocedure for an int in the right inlet (inlet 2) */
+/* addinx((method)vbap_in3, 3);
+/* addmess((method)vbap_matrix, "loudspeaker-matrices", A_GIMME, 0); */
+
+/* pure data: */
+
+ class_addbang(vbap_class, vbap_bang);
+ class_addfloat(vbap_class, vbap_int);
+ class_addmethod(vbap_class, (t_method)vbap_matrix, gensym("loudspeaker-matrices"), A_GIMME, 0);
+}
+
+
+void angle_to_cart(long azi, long ele, float res[3])
+/* converts angular coordinates to cartesian */
+{
+ float atorad = (2 * 3.1415927 / 360) ;
+ res[0] = cos((float) azi * atorad) * cos((float) ele * atorad);
+ res[1] = sin((float) azi * atorad) * cos((float) ele * atorad);
+ res[2] = sin((float) ele * atorad);
+}
+
+void cart_to_angle(float cvec[3], float avec[3])
+/* converts cartesian coordinates to angular */
+{
+ float tmp, tmp2, tmp3, tmp4;
+ float atorad = (2 * 3.1415927 / 360) ;
+ float pi = 3.1415927;
+ float power;
+ float dist, atan_y_per_x, atan_x_pl_y_per_z;
+ float azi, ele;
+
+ if(cvec[0]==0.0)
+ atan_y_per_x = pi / 2;
+ else
+ atan_y_per_x = atan(cvec[1] / cvec[0]);
+ azi = atan_y_per_x / atorad;
+ if(cvec[0]<0.0)
+ azi +=180;
+ dist = sqrt(cvec[0]*cvec[0] + cvec[1]*cvec[1]);
+ if(cvec[2]==0.0)
+ atan_x_pl_y_per_z = 0.0;
+ else
+ atan_x_pl_y_per_z = atan(cvec[2] / dist);
+ if(dist == 0.0)
+ if(cvec[2]<0.0)
+ atan_x_pl_y_per_z = -pi/2.0;
+ else
+ atan_x_pl_y_per_z = pi/2.0;
+ ele = atan_x_pl_y_per_z / atorad;
+ dist = sqrtf(cvec[0] * cvec[0] +cvec[1] * cvec[1] +cvec[2]*cvec[2]);
+ avec[0]=azi;
+ avec[1]=ele;
+ avec[2]=dist;
+}
+
+
+void vbap(float g[3], long ls[3], t_vbap *x)
+{
+ /* calculates gain factors using loudspeaker setup and given direction */
+ float power;
+ int i,j,k, gains_modified;
+ float small_g;
+ float big_sm_g, gtmp[3];
+ long winner_set;
+ float cartdir[3];
+ float new_cartdir[3];
+ float new_angle_dir[3];
+ long dim = x->x_dimension;
+ long neg_g_am, best_neg_g_am;
+
+ /* transfering the azimuth angle to a decent value */
+ while(x->x_azi > 180)
+ x->x_azi -= 360;
+ while(x->x_azi < -179)
+ x->x_azi += 360;
+
+ /* transferring the elevation to a decent value */
+ if(dim == 3){
+ while(x->x_ele > 180)
+ x->x_ele -= 360;
+ while(x->x_ele < -179)
+ x->x_ele += 360;
+ } else
+ x->x_ele = 0;
+
+
+ /* go through all defined loudspeaker sets and find the set which
+ // has all positive values. If such is not found, set with largest
+ // minimum value is chosen. If at least one of gain factors of one LS set is negative
+ // it means that the virtual source does not lie in that LS set. */
+
+ angle_to_cart(x->x_azi,x->x_ele,cartdir);
+ big_sm_g = -100000.0; /* initial value for largest minimum gain value */
+ best_neg_g_am=3; /* how many negative values in this set */
+
+
+ for(i=0;i<x->x_lsset_amount;i++){
+ small_g = 10000000.0;
+ neg_g_am = 3;
+ for(j=0;j<dim;j++){
+ gtmp[j]=0.0;
+ for(k=0;k<dim;k++)
+ gtmp[j]+=cartdir[k]* x->x_set_inv_matx[i][k+j*dim];
+ if(gtmp[j] < small_g)
+ small_g = gtmp[j];
+ if(gtmp[j]>= -0.01)
+ neg_g_am--;
+ }
+ if(small_g > big_sm_g && neg_g_am <= best_neg_g_am){
+ big_sm_g = small_g;
+ best_neg_g_am = neg_g_am;
+ winner_set=i;
+ g[0]=gtmp[0]; g[1]=gtmp[1];
+ ls[0]= x->x_lsset[i][0]; ls[1]= x->x_lsset[i][1];
+ if(dim==3){
+ g[2]=gtmp[2];
+ ls[2]= x->x_lsset[i][2];
+ } else {
+ g[2]=0.0;
+ ls[2]=0;
+ }
+ }
+ }
+
+ /* If chosen set produced a negative value, make it zero and
+ // calculate direction that corresponds to these new
+ // gain values. This happens when the virtual source is outside of
+ // all loudspeaker sets. */
+
+ if(dim==3){
+ gains_modified=0;
+ for(i=0;i<dim;i++)
+ if(g[i]<-0.01){
+ g[i]=0.0001;
+ gains_modified=1;
+ }
+ if(gains_modified==1){
+ new_cartdir[0] = x->x_set_matx[winner_set][0] * g[0]
+ + x->x_set_matx[winner_set][1] * g[1]
+ + x->x_set_matx[winner_set][2] * g[2];
+ new_cartdir[1] = x->x_set_matx[winner_set][3] * g[0]
+ + x->x_set_matx[winner_set][4] * g[1]
+ + x->x_set_matx[winner_set][5] * g[2];
+ new_cartdir[2] = x->x_set_matx[winner_set][6] * g[0]
+ + x->x_set_matx[winner_set][7] * g[1]
+ + x->x_set_matx[winner_set][8] * g[2];
+ cart_to_angle(new_cartdir,new_angle_dir);
+ x->x_azi = (long) (new_angle_dir[0] + 0.5);
+ x->x_ele = (long) (new_angle_dir[1] + 0.5);
+ }
+ }
+
+ power=sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]);
+ g[0] /= power;
+ g[1] /= power;
+ g[2] /= power;
+}
+
+
+void cross_prod(float v1[3], float v2[3],
+ float v3[3])
+/* vector cross product */
+{
+ float length;
+ v3[0] = (v1[1] * v2[2] ) - (v1[2] * v2[1]);
+ v3[1] = (v1[2] * v2[0] ) - (v1[0] * v2[2]);
+ v3[2] = (v1[0] * v2[1] ) - (v1[1] * v2[0]);
+
+ length= sqrt(v3[0]*v3[0] + v3[1]*v3[1] + v3[2]*v3[2]);
+ v3[0] /= length;
+ v3[1] /= length;
+ v3[2] /= length;
+}
+
+void additive_vbap(float *final_gs, float cartdir[3], t_vbap *x)
+/* calculates gains to be added to previous gains, used in
+// multiple direction panning (source spreading) */
+{
+ float power;
+ int i,j,k, gains_modified;
+ float small_g;
+ float big_sm_g, gtmp[3];
+ long winner_set;
+ float new_cartdir[3];
+ float new_angle_dir[3];
+ long dim = x->x_dimension;
+ long neg_g_am, best_neg_g_am;
+ float g[3];
+ long ls[3];
+
+ big_sm_g = -100000.0;
+ best_neg_g_am=3;
+
+ for(i=0;i<x->x_lsset_amount;i++){
+ small_g = 10000000.0;
+ neg_g_am = 3;
+ for(j=0;j<dim;j++){
+ gtmp[j]=0.0;
+ for(k=0;k<dim;k++)
+ gtmp[j]+=cartdir[k]* x->x_set_inv_matx[i][k+j*dim];
+ if(gtmp[j] < small_g)
+ small_g = gtmp[j];
+ if(gtmp[j]>= -0.01)
+ neg_g_am--;
+ }
+ if(small_g > big_sm_g && neg_g_am <= best_neg_g_am){
+ big_sm_g = small_g;
+ best_neg_g_am = neg_g_am;
+ winner_set=i;
+ g[0]=gtmp[0]; g[1]=gtmp[1];
+ ls[0]= x->x_lsset[i][0]; ls[1]= x->x_lsset[i][1];
+ if(dim==3){
+ g[2]=gtmp[2];
+ ls[2]= x->x_lsset[i][2];
+ } else {
+ g[2]=0.0;
+ ls[2]=0;
+ }
+ }
+ }
+
+ gains_modified=0;
+ for(i=0;i<dim;i++)
+ if(g[i]<-0.01){
+ gains_modified=1;
+ }
+
+ if(gains_modified != 1){
+ power=sqrt(g[0]*g[0] + g[1]*g[1] + g[2]*g[2]);
+ g[0] /= power;
+ g[1] /= power;
+ g[2] /= power;
+
+ final_gs[ls[0]-1] += g[0];
+ final_gs[ls[1]-1] += g[1];
+ final_gs[ls[2]-1] += g[2];
+ }
+}
+
+
+void new_spread_dir(t_vbap *x, float spreaddir[3], float vscartdir[3], float spread_base[3])
+/* subroutine for spreading */
+{
+ float beta,gamma;
+ float a,b;
+ float pi = 3.1415927;
+ float power;
+
+ gamma = acos(vscartdir[0] * spread_base[0] +
+ vscartdir[1] * spread_base[1] +
+ vscartdir[2] * spread_base[2])/pi*180;
+ if(fabs(gamma) < 1){
+ angle_to_cart(x->x_azi+90, 0, spread_base);
+ gamma = acos(vscartdir[0] * spread_base[0] +
+ vscartdir[1] * spread_base[1] +
+ vscartdir[2] * spread_base[2])/pi*180;
+ }
+ beta = 180 - gamma;
+ b=sin(x->x_spread * pi / 180) / sin(beta * pi / 180);
+ a=sin((180- x->x_spread - beta) * pi / 180) / sin (beta * pi / 180);
+ spreaddir[0] = a * vscartdir[0] + b * spread_base[0];
+ spreaddir[1] = a * vscartdir[1] + b * spread_base[1];
+ spreaddir[2] = a * vscartdir[2] + b * spread_base[2];
+
+ power=sqrt(spreaddir[0]*spreaddir[0] + spreaddir[1]*spreaddir[1]
+ + spreaddir[2]*spreaddir[2]);
+ spreaddir[0] /= power;
+ spreaddir[1] /= power;
+ spreaddir[2] /= power;
+}
+
+void new_spread_base(t_vbap *x, float spreaddir[3], float vscartdir[3])
+/* subroutine for spreading */
+{
+ float d;
+ float pi = 3.1415927;
+ float power;
+
+ d = cos(x->x_spread/180*pi);
+ x->x_spread_base[0] = spreaddir[0] - d * vscartdir[0];
+ x->x_spread_base[1] = spreaddir[1] - d * vscartdir[1];
+ x->x_spread_base[2] = spreaddir[2] - d * vscartdir[2];
+ power=sqrt(x->x_spread_base[0]*x->x_spread_base[0] + x->x_spread_base[1]*x->x_spread_base[1]
+ + x->x_spread_base[2]*x->x_spread_base[2]);
+ x->x_spread_base[0] /= power;
+ x->x_spread_base[1] /= power;
+ x->x_spread_base[2] /= power;
+}
+
+void spread_it(t_vbap *x, float *final_gs)
+/*
+// apply the sound signal to multiple panning directions
+// that causes some spreading.
+// See theory in paper V. Pulkki "Uniform spreading of amplitude panned
+// virtual sources" in WASPAA 99
+*/
+{
+ float vscartdir[3];
+ float spreaddir[16][3];
+ float spreadbase[16][3];
+ long i, spreaddirnum;
+ float power;
+ if(x->x_dimension == 3){
+ spreaddirnum=16;
+ angle_to_cart(x->x_azi,x->x_ele,vscartdir);
+ new_spread_dir(x, spreaddir[0], vscartdir, x->x_spread_base);
+ new_spread_base(x, spreaddir[0], vscartdir);
+ cross_prod(x->x_spread_base, vscartdir, spreadbase[1]); /* four orthogonal dirs */
+ cross_prod(spreadbase[1], vscartdir, spreadbase[2]);
+ cross_prod(spreadbase[2], vscartdir, spreadbase[3]);
+
+ /* four between them */
+ for(i=0;i<3;i++) spreadbase[4][i] = (x->x_spread_base[i] + spreadbase[1][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[5][i] = (spreadbase[1][i] + spreadbase[2][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[6][i] = (spreadbase[2][i] + spreadbase[3][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[7][i] = (spreadbase[3][i] + x->x_spread_base[i]) / 2.0;
+
+ /* four at half spreadangle */
+ for(i=0;i<3;i++) spreadbase[8][i] = (vscartdir[i] + x->x_spread_base[i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[9][i] = (vscartdir[i] + spreadbase[1][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[10][i] = (vscartdir[i] + spreadbase[2][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[11][i] = (vscartdir[i] + spreadbase[3][i]) / 2.0;
+
+ /* four at quarter spreadangle */
+ for(i=0;i<3;i++) spreadbase[12][i] = (vscartdir[i] + spreadbase[8][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[13][i] = (vscartdir[i] + spreadbase[9][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[14][i] = (vscartdir[i] + spreadbase[10][i]) / 2.0;
+ for(i=0;i<3;i++) spreadbase[15][i] = (vscartdir[i] + spreadbase[11][i]) / 2.0;
+
+ additive_vbap(final_gs,spreaddir[0],x);
+ for(i=1;i<spreaddirnum;i++){
+ new_spread_dir(x, spreaddir[i], vscartdir, spreadbase[i]);
+ additive_vbap(final_gs,spreaddir[i],x);
+ }
+ } else if (x->x_dimension == 2) {
+ spreaddirnum=6;
+
+ angle_to_cart(x->x_azi - x->x_spread, 0, spreaddir[0]);
+ angle_to_cart(x->x_azi - x->x_spread/2, 0, spreaddir[1]);
+ angle_to_cart(x->x_azi - x->x_spread/4, 0, spreaddir[2]);
+ angle_to_cart(x->x_azi + x->x_spread/4, 0, spreaddir[3]);
+ angle_to_cart(x->x_azi + x->x_spread/2, 0, spreaddir[4]);
+ angle_to_cart(x->x_azi + x->x_spread, 0, spreaddir[5]);
+
+ for(i=0;i<spreaddirnum;i++)
+ additive_vbap(final_gs,spreaddir[i],x);
+ } else
+ return;
+
+ if(x->x_spread > 70)
+ for(i=0;i<x->x_ls_amount;i++){
+ final_gs[i] += (x->x_spread - 70) / 30.0 * (x->x_spread - 70) / 30.0 * 10.0;
+ }
+
+ for(i=0,power=0.0;i<x->x_ls_amount;i++){
+ power += final_gs[i] * final_gs[i];
+ }
+
+ power = sqrt(power);
+ for(i=0;i<x->x_ls_amount;i++){
+ final_gs[i] /= power;
+ }
+}
+
+
+void vbap_bang(t_vbap *x)
+/* top level, vbap gains are calculated and outputted */
+{
+ t_atom at[MAX_LS_AMOUNT];
+ float g[3];
+ long ls[3];
+ long i;
+ float *final_gs;
+
+ final_gs = (float *) getbytes(x->x_ls_amount * sizeof(float));
+ if(x->x_lsset_available ==1){
+ vbap(g,ls, x);
+ for(i=0;i<x->x_ls_amount;i++)
+ final_gs[i]=0.0;
+ for(i=0;i<x->x_dimension;i++){
+ final_gs[ls[i]-1]=g[i];
+ }
+ if(x->x_spread != 0){
+ spread_it(x,final_gs);
+ }
+ for(i=0;i<x->x_ls_amount;i++) {
+ SETFLOAT(&at[0], (t_float)i);
+ SETFLOAT(&at[1], (t_float)final_gs[i]);
+ outlet_list(x->x_outlet0, gensym("list") /* was: 0L */, 2, at);
+ }
+ outlet_float(x->x_outlet1, x->x_azi);
+ outlet_float(x->x_outlet2, x->x_ele);
+ outlet_float(x->x_outlet3, x->x_spread);
+ }
+ else
+ post("vbap: Configure loudspeakers first!",0);
+/* freebytes(final_gs, x->x_ls_amount * sizeof(float)); /* bug fix added 9/00 */
+}
+
+/*--------------------------------------------------------------------------*/
+
+void vbap_int(t_vbap *x, t_float n) /* x = the instance of the object, n = the int received in the right inlet */
+{
+ /* do something if an int comes in the left inlet??? */
+}
+
+void vbap_matrix(t_vbap *x, t_symbol *s, int ac, t_atom *av)
+/* read in loudspeaker matrices */
+{
+ long counter;
+ long datapointer=0;
+ long setpointer=0;
+ long i;
+ long deb=0;
+
+ if(ac>0)
+/* if(av[datapointer].a_type == A_LONG){
+ x->x_dimension = av[datapointer++].a_w.w_long;
+ x->x_lsset_available=1;
+ } else */
+ if(av[datapointer].a_type == A_FLOAT){
+ x->x_dimension = (long) av[datapointer++].a_w.w_float;
+ x->x_lsset_available=1;
+ } else {
+ post("Error in loudspeaker data!",0);
+ x->x_lsset_available=0;
+ return;
+ }
+/* post("%d",deb++); */
+ if(ac>1)
+/* if(av[datapointer].a_type == A_LONG)
+ x->x_ls_amount = av[datapointer++].a_w.w_long;
+ else */
+ if(av[datapointer].a_type == A_FLOAT)
+ x->x_ls_amount = (long) av[datapointer++].a_w.w_float;
+ else {
+ post("vbap: Error in loudspeaker data!",0);
+ x->x_lsset_available=0;
+ return;
+ }
+ else
+ x->x_lsset_available=0;
+
+/* post("%d",deb++); */
+ if(x->x_dimension == 3)
+ counter = (ac - 2) / ((x->x_dimension * x->x_dimension*2) + x->x_dimension);
+ if(x->x_dimension == 2)
+ counter = (ac - 2) / ((x->x_dimension * x->x_dimension) + x->x_dimension);
+ x->x_lsset_amount=counter;
+
+ if(counter<=0){
+ post("vbap: Error in loudspeaker data!",0);
+ x->x_lsset_available=0;
+ return;
+ }
+
+
+ while(counter-- > 0){
+ for(i=0; i < x->x_dimension; i++){
+ if(av[datapointer].a_type == A_FLOAT){
+ x->x_lsset[setpointer][i]=(long)av[datapointer++].a_w.w_float;
+/* post("%d",deb++); */
+ }
+ else{
+ post("vbap: Error in loudspeaker data!",0);
+ x->x_lsset_available=0;
+ return;
+ }
+ }
+
+ for(i=0; i < x->x_dimension*x->x_dimension; i++){
+ if(av[datapointer].a_type == A_FLOAT){
+ x->x_set_inv_matx[setpointer][i]=av[datapointer++].a_w.w_float;
+/* post("%d",deb++); */
+ }
+ else {
+ post("vbap: Error in loudspeaker data!",0);
+ x->x_lsset_available=0;
+ return;
+ }
+ }
+ if(x->x_dimension == 3){
+ for(i=0; i < x->x_dimension*x->x_dimension; i++){
+ if(av[datapointer].a_type == A_FLOAT){
+ x->x_set_matx[setpointer][i]=av[datapointer++].a_w.w_float;
+/* post("%d",deb++); */
+ }
+ else {
+ post("vbap: Error in loudspeaker data!",0);
+ x->x_lsset_available=0;
+ return;
+ }
+ }
+ }
+
+ setpointer++;
+ }
+ post("vbap: Loudspeaker setup configured!",0);
+}
+
+void vbap_in1(t_vbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */
+/* panning angle azimuth */
+{
+ x->x_azi = n; /* store n in a global variable */
+
+}
+
+void vbap_in2(t_vbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */
+/* panning angle elevation */
+{
+ x->x_ele = n; /* store n in a global variable */
+
+}
+/*--------------------------------------------------------------------------*/
+
+void vbap_in3(t_vbap *x, long n) /* x = the instance of the object, n = the int received in the right inlet */
+/* spread amount */
+{
+ if (n<0) n = 0;
+ if (n>100) n = 100;
+ x->x_spread = n; /* store n in a global variable */
+
+}
+
+
+static void *vbap_new(t_symbol *s, int ac, t_atom *av)
+/* create new instance of object... MUST send it an int even if you do nothing with this int!! */
+{
+ t_vbap *x;
+ x = (t_vbap *)pd_new(vbap_class);
+
+/* MAX:
+/* intin(x,3);
+/* intin(x,2); /* create a second (int) inlet... remember right-to-left ordering in Max */
+/* intin(x,1); /* create a second (int) inlet... remember right-to-left ordering in Max */
+/* x->x_outlet3 = intout(x);
+/* x->x_outlet2 = intout(x); /* create an (int) outlet - rightmost outlet first... */
+/* x->x_outlet1 = intout(x); /* create an (int) outlet */
+/* x->x_outlet0 = listout(x); /* create a (list) outlet */
+/*
+/* pure data: */
+
+ floatinlet_new(&x->x_ob, &x->x_azi);
+ floatinlet_new(&x->x_ob, &x->x_ele);
+ floatinlet_new(&x->x_ob, &x->x_spread);
+
+ x->x_outlet0 = outlet_new(&x->x_ob, gensym("list"));
+ x->x_outlet1 = outlet_new(&x->x_ob, gensym("float"));
+ x->x_outlet2 = outlet_new(&x->x_ob, gensym("float"));
+ x->x_outlet3 = outlet_new(&x->x_ob, gensym("float"));
+
+/* - */
+
+
+ x->x_azi = 0;
+ x->x_ele = 0;
+ x->x_spread_base[0] = 0.0;
+ x->x_spread_base[1] = 1.0;
+ x->x_spread_base[2] = 0.0;
+ x->x_spread = 0;
+ x->x_lsset_available =0;
+ if (ac>0) {
+/* if (av[0].a_type == A_LONG)
+ x->x_azi = av[0].a_w.w_long;
+ else */
+ if (av[0].a_type == A_FLOAT)
+ x->x_azi = (long)av[0].a_w.w_float;
+ }
+ if (ac>1) {
+/* if (av[1].a_type == A_LONG)
+ x->x_ele = av[1].a_w.w_long;
+ else */
+ if (av[1].a_type == A_FLOAT)
+ x->x_ele = (long)av[1].a_w.w_float;
+ }
+ return(x); /* return a reference to the object instance */
+}
+
diff --git a/vbap.dll b/vbap.dll
new file mode 100644
index 0000000..1ee6214
--- /dev/null
+++ b/vbap.dll
Binary files differ
diff --git a/vbap.main.pd b/vbap.main.pd
new file mode 100644
index 0000000..e4c11b4
--- /dev/null
+++ b/vbap.main.pd
@@ -0,0 +1,92 @@
+#N canvas 153 122 664 665 10;
+#X obj 22 149 bng 15 250 50 0 empty empty empty 20 8 32 8 -262144 -1
+-1;
+#X floatatom 353 470 5 0 0;
+#X obj 166 579 dac~ 1 2 3 4 5 6 7 8;
+#X obj 109 535 *~ 1;
+#X obj 142 535 *~ 1;
+#X obj 175 535 *~ 1;
+#X obj 207 535 *~ 1;
+#X obj 239 535 *~ 1;
+#X obj 271 535 *~ 1;
+#X obj 304 535 *~ 1;
+#X obj 336 535 *~ 1;
+#X floatatom 382 504 5 0 0;
+#X text 401 470 crossfade;
+#X obj 22 208 send speaker_setup;
+#X obj 22 169 define_loudspeakers 3 -30 0 30 0 -90 0 90 0 180 0 180
+45 -45 45 45 45;
+#X obj 326 443 matrix 4 8;
+#X obj 269 470 matrix~ 4 8;
+#X obj 39 146 loadbang;
+#X msg 471 169 \; pd dsp 1;
+#X text 540 175 turn on audio;
+#X text 430 504 gain;
+#X obj 467 496 vsl 15 128 0 1 0 0 empty empty empty 20 8 32 8 -262144
+-1 -1 0 1;
+#X msg 422 452 50;
+#X obj 438 413 loadbang;
+#X obj 484 494 r master;
+#X obj 21 89 inlet;
+#X text 66 89 no meaning;
+#X obj 326 340 receive matrix;
+#X text 30 22 define loudspeakers \, receive signals and data from
+vbap \, output audio;
+#X obj 99 146 receive define_ls;
+#X obj 52 318 catch~ 1chan;
+#X obj 162 318 catch~ 2chan;
+#X obj 272 318 catch~ 3chan;
+#X obj 382 318 catch~ 4chan;
+#N canvas 229 245 540 236 parameters.readme 0;
+#X text 37 35 define_loudspeakers <dimensions> <azimuth (elevation)>
+<..>;
+#X text 37 70 dimensions is 2 or 3 \, followed by list of azimuths
+(in 2d) or <azimuth elevation> pairs (in 3d) \, defining the number
+and positions of loudspeakers.;
+#X text 38 122 azimuth is -180 to 180 \, where -90 is left \, 0 front
+\, 90 right and 180 back.;
+#X text 38 161 elevation is -90 to 90 \, where -90 is down \, 0 is
+not elevated and 90 is up.;
+#X restore 258 206 pd parameters.readme;
+#X connect 0 0 14 0;
+#X connect 1 0 16 5;
+#X connect 3 0 2 0;
+#X connect 4 0 2 1;
+#X connect 5 0 2 2;
+#X connect 6 0 2 3;
+#X connect 7 0 2 4;
+#X connect 8 0 2 5;
+#X connect 9 0 2 6;
+#X connect 10 0 2 7;
+#X connect 11 0 10 1;
+#X connect 11 0 9 1;
+#X connect 11 0 8 1;
+#X connect 11 0 7 1;
+#X connect 11 0 6 1;
+#X connect 11 0 5 1;
+#X connect 11 0 4 1;
+#X connect 11 0 3 1;
+#X connect 11 0 9 1;
+#X connect 11 0 10 1;
+#X connect 14 0 13 0;
+#X connect 15 0 16 4;
+#X connect 16 0 3 0;
+#X connect 16 1 4 0;
+#X connect 16 2 5 0;
+#X connect 16 3 6 0;
+#X connect 16 4 7 0;
+#X connect 16 5 8 0;
+#X connect 16 6 9 0;
+#X connect 16 7 10 0;
+#X connect 17 0 0 0;
+#X connect 21 0 11 0;
+#X connect 22 0 1 0;
+#X connect 23 0 22 0;
+#X connect 23 0 18 0;
+#X connect 24 0 21 0;
+#X connect 27 0 15 0;
+#X connect 29 0 0 0;
+#X connect 30 0 16 0;
+#X connect 31 0 16 1;
+#X connect 32 0 16 2;
+#X connect 33 0 16 3;
diff --git a/vbap.pd_irix6 b/vbap.pd_irix6
new file mode 100755
index 0000000..17866f9
--- /dev/null
+++ b/vbap.pd_irix6
Binary files differ
diff --git a/vbapmodule.pd b/vbapmodule.pd
new file mode 100644
index 0000000..01484a8
--- /dev/null
+++ b/vbapmodule.pd
@@ -0,0 +1,22 @@
+#N canvas 94 380 450 300 10;
+#X obj 30 44 inlet~;
+#X obj 62 158 send matrix;
+#X obj 46 183 throw~ \$1chan;
+#X floatatom 180 186 5 0 0;
+#X floatatom 225 186 5 0 0;
+#X floatatom 270 186 5 0 0;
+#X text 137 217 actual azi / ele / spread;
+#X obj 82 44 inlet;
+#X obj 122 44 inlet;
+#X obj 162 44 inlet;
+#X text 210 44 azi / ele / spread;
+#X obj 46 112 vbapsnd \$1;
+#X connect 0 0 11 0;
+#X connect 7 0 11 1;
+#X connect 8 0 11 2;
+#X connect 9 0 11 3;
+#X connect 11 0 2 0;
+#X connect 11 1 1 0;
+#X connect 11 2 3 0;
+#X connect 11 3 4 0;
+#X connect 11 4 5 0;
diff --git a/vbapsnd.pd b/vbapsnd.pd
new file mode 100644
index 0000000..e73f027
--- /dev/null
+++ b/vbapsnd.pd
@@ -0,0 +1,68 @@
+#N canvas 33 53 661 597 10;
+#X obj 133 20 inlet;
+#X obj 189 171 vbap 0 0;
+#X obj 240 20 inlet;
+#X obj 359 20 inlet;
+#X text 176 19 azimuth;
+#X text 285 19 elevation;
+#X text 405 19 spread;
+#X obj 233 460 pack f f f;
+#X obj 298 437 float \$1;
+#X obj 298 414 loadbang;
+#X msg 233 495 element \$3 \$1 \$2;
+#X text 287 536 to matrix object;
+#X obj 441 535 outlet;
+#X obj 488 535 outlet;
+#X obj 535 535 outlet;
+#X obj 359 78 t b f;
+#X obj 240 80 t b f;
+#X obj 133 80 t b f;
+#X obj 189 106 receive speaker_setup;
+#X obj 189 256 unpack f f;
+#X obj 189 279 + 1;
+#X obj 167 429 ==;
+#X obj 167 453 sel 1;
+#X obj 189 303 t f f f;
+#X obj 195 429 high;
+#X obj 233 536 outlet;
+#X obj 31 20 inlet~;
+#X obj 31 83 outlet~;
+#X text 28 113 for convenience;
+#X floatatom 489 62 5 0 0;
+#X obj 359 49 recent 10;
+#X obj 240 51 recent 10;
+#X obj 133 51 recent 10;
+#X connect 0 0 32 0;
+#X connect 1 0 19 0;
+#X connect 1 1 12 0;
+#X connect 1 2 13 0;
+#X connect 1 3 14 0;
+#X connect 2 0 31 0;
+#X connect 3 0 30 0;
+#X connect 7 0 10 0;
+#X connect 8 0 7 2;
+#X connect 9 0 8 0;
+#X connect 10 0 25 0;
+#X connect 15 0 1 0;
+#X connect 15 1 1 3;
+#X connect 16 0 1 0;
+#X connect 16 1 1 2;
+#X connect 17 0 1 0;
+#X connect 17 1 1 1;
+#X connect 18 0 1 0;
+#X connect 19 0 20 0;
+#X connect 19 1 7 1;
+#X connect 20 0 23 0;
+#X connect 21 0 22 0;
+#X connect 22 0 25 0;
+#X connect 23 0 21 0;
+#X connect 23 1 24 0;
+#X connect 23 2 7 0;
+#X connect 24 0 21 1;
+#X connect 26 0 27 0;
+#X connect 29 0 30 1;
+#X connect 29 0 31 1;
+#X connect 29 0 32 1;
+#X connect 30 0 15 0;
+#X connect 31 0 16 0;
+#X connect 32 0 17 0;