diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2010-11-18 18:01:30 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2010-11-18 18:01:30 +0000 |
commit | e50bd9e6530d6c708a4b259de6ef19e0fc6b97c0 (patch) | |
tree | db055fa66fdc4e59bf92a4b97a7717d65bdfc78a /libs/ardour | |
parent | 6ce5dc2dc9e38adff8072d351001c92c265d83d8 (diff) |
steps toward a working VBAP panner
git-svn-id: svn://localhost/ardour2/branches/3.0@8055 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r-- | libs/ardour/ardour/vbap.h | 144 | ||||
-rw-r--r-- | libs/ardour/ardour/vbap_speakers.h | 109 | ||||
-rw-r--r-- | libs/ardour/vbap.cc | 106 | ||||
-rw-r--r-- | libs/ardour/vbap_speakers.cc | 87 |
4 files changed, 277 insertions, 169 deletions
diff --git a/libs/ardour/ardour/vbap.h b/libs/ardour/ardour/vbap.h index 45e5a84653..922a2a4631 100644 --- a/libs/ardour/ardour/vbap.h +++ b/libs/ardour/ardour/vbap.h @@ -1,34 +1,19 @@ -/* - 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 by Ville Pulkki, Helsinki University of Technology. All - rights reserved. - - 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. +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __libardour_vbap_h__ @@ -37,83 +22,13 @@ #include <string> #include <map> +#include <pbd/signals.h> + #include "ardour/panner.h" namespace ARDOUR { -class VBAPSpeakers { - public: - struct cart_vec { - float x; - float y; - float z; - }; - - struct ang_vec { - float azi; - float ele; - float length; - }; - - static const int MAX_TRIPLET_AMOUNT = 60; - - VBAPSpeakers (); - ~VBAPSpeakers (); - - int add_speaker (double direction, double elevation = 0.0); - void remove_speaker (int id); - void move_speaker (int id, double direction, double elevation = 0.0); - - const double* matrix (int tuple) const { return _matrices[tuple]; } - int speaker_for_tuple (int tuple, int which) const { return _speaker_tuples[tuple][which]; } - - int n_tuples () const { return _matrices.size(); } - int dimension() const { return _dimension; } - - static void angle_to_cart(ang_vec *from, cart_vec *to); - - private: - static const double MIN_VOL_P_SIDE_LGTH = 0.01; - int _dimension; - - /* A struct for a loudspeaker instance */ - struct Speaker { - int id; - cart_vec coords; - ang_vec angles; - - Speaker (int, double azimuth, double elevation); - - void move (double azimuth, double elevation); - }; - - std::vector<Speaker> _speakers; - std::vector<double[9]> _matrices; /* holds matrices for a given speaker combinations */ - std::vector<int[3]> _speaker_tuples; /* holds speakers IDs for a given combination */ - - /* A struct for all loudspeakers */ - struct ls_triplet_chain { - int ls_nos[3]; - float inv_mx[9]; - struct ls_triplet_chain *next; - }; - - static float vec_angle(cart_vec v1, cart_vec v2); - static float vec_length(cart_vec v1); - static float vec_prod(cart_vec v1, cart_vec v2); - static float vol_p_side_lgth(int i, int j,int k, const std::vector<Speaker>&); - static void cross_prod(cart_vec v1,cart_vec v2, cart_vec *res); - - void update (); - int any_ls_inside_triplet (int a, int b, int c); - void add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain **ls_triplets); - int lines_intersect (int i,int j,int k,int l); - void calculate_3x3_matrixes (struct ls_triplet_chain *ls_triplets); - void choose_ls_triplets (struct ls_triplet_chain **ls_triplets); - void choose_ls_pairs (); - void sort_2D_lss (int* sorted_lss); - int calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat); -}; +class VBAPSpeakers; class VBAPanner : public StreamPanner { public: @@ -124,20 +39,23 @@ class VBAPanner : public StreamPanner { void set_azimuth_elevation (double azimuth, double elevation); - /* a utility function to convert azimuth+elevation into cartesian coordinates - as used by the StreamPanner API - */ - - void azi_ele_to_cart (int azi, int ele, double* c); - private: - double _azimuth; /* direction for the signal source */ - double _elevation; /* elevation of the signal source */ - VBAPSpeakers& _speakers; + double _azimuth; /* direction for the signal source */ + double _elevation; /* elevation of the signal source */ + bool _dirty; + double gains[3]; + double desired_gains[3]; + int outputs[3]; + int desired_outputs[3]; + + PBD::ScopedConnection speaker_connection; + VBAPSpeakers& _speakers; + void compute_gains (double g[3], int ls[3], int azi, int ele); void update (); + void mark_dirty (); }; } /* namespace */ diff --git a/libs/ardour/ardour/vbap_speakers.h b/libs/ardour/ardour/vbap_speakers.h new file mode 100644 index 0000000000..cf1bbab691 --- /dev/null +++ b/libs/ardour/ardour/vbap_speakers.h @@ -0,0 +1,109 @@ +/* + Copyright (C) 2010 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __libardour_vbap_speakers_h__ +#define __libardour_vbap_speakers_h__ + +#include <string> +#include <map> + +#include <pbd/signals.h> + +#include "ardour/panner.h" + +namespace ARDOUR { + +class VBAPSpeakers { + public: + struct cart_vec { + float x; + float y; + float z; + }; + + struct ang_vec { + float azi; + float ele; + float length; + }; + + static const int MAX_TRIPLET_AMOUNT = 60; + + VBAPSpeakers (); + ~VBAPSpeakers (); + + int add_speaker (double direction, double elevation = 0.0); + void remove_speaker (int id); + void move_speaker (int id, double direction, double elevation = 0.0); + + const double* matrix (int tuple) const { return _matrices[tuple]; } + int speaker_for_tuple (int tuple, int which) const { return _speaker_tuples[tuple][which]; } + + int n_tuples () const { return _matrices.size(); } + int dimension() const { return _dimension; } + + static void angle_to_cart(ang_vec *from, cart_vec *to); + + PBD::Signal0<void> Changed; + + private: + static const double MIN_VOL_P_SIDE_LGTH = 0.01; + int _dimension; + + /* A struct for a loudspeaker instance */ + struct Speaker { + int id; + cart_vec coords; + ang_vec angles; + + Speaker (int, double azimuth, double elevation); + + void move (double azimuth, double elevation); + }; + + std::vector<Speaker> _speakers; + std::vector<double[9]> _matrices; /* holds matrices for a given speaker combinations */ + std::vector<int[3]> _speaker_tuples; /* holds speakers IDs for a given combination */ + + /* A struct for all loudspeakers */ + struct ls_triplet_chain { + int ls_nos[3]; + float inv_mx[9]; + struct ls_triplet_chain *next; + }; + + static float vec_angle(cart_vec v1, cart_vec v2); + static float vec_length(cart_vec v1); + static float vec_prod(cart_vec v1, cart_vec v2); + static float vol_p_side_lgth(int i, int j,int k, const std::vector<Speaker>&); + static void cross_prod(cart_vec v1,cart_vec v2, cart_vec *res); + + void update (); + int any_ls_inside_triplet (int a, int b, int c); + void add_ldsp_triplet (int i, int j, int k, struct ls_triplet_chain **ls_triplets); + int lines_intersect (int i,int j,int k,int l); + void calculate_3x3_matrixes (struct ls_triplet_chain *ls_triplets); + void choose_ls_triplets (struct ls_triplet_chain **ls_triplets); + void choose_ls_pairs (); + void sort_2D_lss (int* sorted_lss); + int calc_2D_inv_tmatrix (double azi1,double azi2, double* inv_mat); +}; + +} /* namespace */ + +#endif /* __libardour_vbap_speakers_h__ */ diff --git a/libs/ardour/vbap.cc b/libs/ardour/vbap.cc index d0114986ce..247df7995a 100644 --- a/libs/ardour/vbap.cc +++ b/libs/ardour/vbap.cc @@ -38,14 +38,24 @@ #include <string> +#include "pbd/cartesian.h" + #include "ardour/vbap.h" +#include "ardour/vbap_speakers.h" +#include "ardour/audio_buffer.h" +#include "ardour/buffer_set.h" +using namespace PBD; using namespace ARDOUR; +using namespace std; VBAPanner::VBAPanner (Panner& parent, Evoral::Parameter param, VBAPSpeakers& s) : StreamPanner (parent, param) + , _dirty (false) , _speakers (s) + { + _speakers.Changed.connect_same_thread (speaker_connection, boost::bind (&VBAPanner::mark_dirty, this)); } VBAPanner::~VBAPanner () @@ -53,21 +63,16 @@ VBAPanner::~VBAPanner () } void -VBAPanner::azi_ele_to_cart (int azi, int ele, double* c) +VBAPanner::mark_dirty () { - static const double atorad = (2.0 * M_PI / 360.0) ; - c[0] = cos (azi * atorad) * cos (ele * atorad); - c[1] = sin (azi * atorad) * cos (ele * atorad); - c[2] = sin (ele * atorad); + _dirty = true; } void VBAPanner::update () { - double g[3]; - int ls[3]; - - compute_gains (g, ls, _azimuth, _elevation); + cart_to_azi_ele (_x, _y, _z, _azimuth, _elevation); + _dirty = true; } void @@ -80,23 +85,33 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) double small_g; double big_sm_g, gtmp[3]; - azi_ele_to_cart (azi,ele, cartdir); + azi_ele_to_cart (azi,ele, cartdir[0], cartdir[1], cartdir[2]); big_sm_g = -100000.0; - for (i = 0; i < _speakers.n_tuples(); i++){ + for (i = 0; i < _speakers.n_tuples(); i++) { + small_g = 10000000.0; + for (j = 0; j < _speakers.dimension(); j++) { + gtmp[j]=0.0; - for (k = 0; k < _speakers.dimension(); k++) - gtmp[j]+=cartdir[k]*_speakers.matrix(i)[j*_speakers.dimension()+k]; - if (gtmp[j] < small_g) + + for (k = 0; k < _speakers.dimension(); k++) { + gtmp[j] += cartdir[k] * _speakers.matrix(i)[j*_speakers.dimension()+k]; + } + + if (gtmp[j] < small_g) { small_g = gtmp[j]; + } } if (small_g > big_sm_g) { + big_sm_g = small_g; - gains[0]=gtmp[0]; - gains[1]=gtmp[1]; + + gains[0] = gtmp[0]; + gains[1] = gtmp[1]; + speaker_ids[0]= _speakers.speaker_for_tuple (i, 0); speaker_ids[1]= _speakers.speaker_for_tuple (i, 1); @@ -105,7 +120,7 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) speaker_ids[2] = _speakers.speaker_for_tuple (i, 2); } else { gains[2] = 0.0; - speaker_ids[2] = 0; + speaker_ids[2] = -1; } } } @@ -115,9 +130,64 @@ VBAPanner::compute_gains (double gains[3], int speaker_ids[3], int azi, int ele) gains[0] /= power; gains[1] /= power; gains[2] /= power; + + _dirty = false; } void -VBAPanner::do_distribute (AudioBuffer& bufs, BufferSet& obufs, gain_t gain_coefficient, nframes_t nframes) +VBAPanner::do_distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coefficient, nframes_t nframes) { + if (_muted) { + return; + } + + Sample* const src = srcbuf.data(); + Sample* dst; + pan_t pan; + uint32_t n_audio = obufs.count().n_audio(); + bool was_dirty; + + if ((was_dirty = _dirty)) { + compute_gains (desired_gains, desired_outputs, _azimuth, _elevation); + } + + bool todo[n_audio]; + + for (uint32_t o = 0; o < n_audio; ++o) { + todo[o] = true; + } + + /* VBAP may distribute the signal across up to 3 speakers depending on + the configuration of the speakers. + */ + + for (int o = 0; o < 3; ++o) { + if (outputs[o] != -1) { + + nframes_t n = 0; + + /* XXX TODO: interpolate across changes in gain and/or outputs + */ + + dst = obufs.get_audio(outputs[o]).data(); + + pan = gain_coefficient * desired_gains[o]; + mix_buffers_with_gain (dst+n,src+n,nframes-n,pan); + + todo[o] = false; + } + } + + for (uint32_t o = 0; o < n_audio; ++o) { + if (todo[o]) { + /* VBAP decided not to deliver any audio to this output, so we write silence */ + dst = obufs.get_audio(o).data(); + memset (dst, 0, sizeof (Sample) * nframes); + } + } + + if (was_dirty) { + memcpy (gains, desired_gains, sizeof (gains)); + memcpy (outputs, desired_outputs, sizeof (outputs)); + } } diff --git a/libs/ardour/vbap_speakers.cc b/libs/ardour/vbap_speakers.cc index 5b29aeb380..a9a54e9d03 100644 --- a/libs/ardour/vbap_speakers.cc +++ b/libs/ardour/vbap_speakers.cc @@ -34,7 +34,7 @@ #include <cmath> #include <stdlib.h> -#include "ardour/vbap.h" +#include "ardour/vbap_speakers.h" using namespace ARDOUR; using namespace std; @@ -113,12 +113,14 @@ VBAPSpeakers::update () _dimension = dim; if (_dimension == 3) { - ls_triplet_chain *ls_triplets = NULL; + ls_triplet_chain *ls_triplets = 0; choose_ls_triplets (&ls_triplets); calculate_3x3_matrixes (ls_triplets); } else { choose_ls_pairs (); } + + Changed (); /* EMIT SIGNAL */ } void @@ -160,9 +162,9 @@ VBAPSpeakers::choose_ls_triplets(struct ls_triplet_chain **ls_triplets) fprintf(stderr,"Number of loudspeakers is zero\nExiting\n"); exit(-1); } - for(i=0;i<n_speakers;i++) - for(j=i+1;j<n_speakers;j++) - for(k=j+1;k<n_speakers;k++){ + for (i = 0; i < n_speakers; i++) { + for (j = i+1; j < n_speakers; j++) { + for(k=j+1;k<n_speakers;k++) { if (vol_p_side_lgth(i,j, k, _speakers) > MIN_VOL_P_SIDE_LGTH){ connections[i][j]=1; connections[j][i]=1; @@ -173,18 +175,24 @@ VBAPSpeakers::choose_ls_triplets(struct ls_triplet_chain **ls_triplets) add_ldsp_triplet(i,j,k,ls_triplets); } } + } + } + /*calculate distancies between all speakers and sorting them*/ table_size =(((n_speakers - 1) * (n_speakers)) / 2); - for(i=0;i<table_size; i++) + for (i = 0; i < table_size; i++) { distance_table[i] = 100000.0; - for(i=0;i<n_speakers;i++){ - for(j=(i+1);j<n_speakers; j++){ - if(connections[i][j] == 1) { + } + + for (i = 0;i < n_speakers; i++) { + for (j = i+1; j < n_speakers; j++) { + if (connections[i][j] == 1) { distance = fabs(vec_angle(_speakers[i].coords,_speakers[j].coords)); k=0; - while(distance_table[k] < distance) + while(distance_table[k] < distance) { k++; - for(l=(table_size - 1);l > k ;l--){ + } + 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]; @@ -200,33 +208,36 @@ VBAPSpeakers::choose_ls_triplets(struct ls_triplet_chain **ls_triplets) /* 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++){ + 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<n_speakers ; j++) - for(k=j+1; k<n_speakers; k++) - if( (j!=fst_ls) && (k != sec_ls) && (k!=fst_ls) && (j != sec_ls)){ - if(lines_intersect(fst_ls, sec_ls, j,k) == 1){ + if (connections[fst_ls][sec_ls] == 1) { + for (j = 0; j < n_speakers; j++) { + for (k = j+1; k < n_speakers; k++) { + if ((j!=fst_ls) && (k != sec_ls) && (k!=fst_ls) && (j != sec_ls)){ + if (lines_intersect(fst_ls, sec_ls, j,k) == 1){ connections[j][k] = 0; connections[k][j] = 0; } } + } + } + } } /* remove triangles which had crossing sides with smaller triangles or include loudspeakers*/ trip_ptr = *ls_triplets; - prev = NULL; - while (trip_ptr != NULL){ + prev = 0; + while (trip_ptr != 0){ 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) == 1 ){ - if(prev != NULL) { + if (connections[i][j] == 0 || + connections[i][k] == 0 || + connections[j][k] == 0 || + any_ls_inside_triplet(i,j,k) == 1 ){ + if (prev != 0) { prev->next = trip_ptr->next; tmp_ptr = trip_ptr; trip_ptr = trip_ptr->next; @@ -306,20 +317,20 @@ VBAPSpeakers::add_ldsp_triplet(int i, int j, int k, struct ls_triplet_chain **ls struct ls_triplet_chain *trip_ptr, *prev; trip_ptr = *ls_triplets; - prev = NULL; + prev = 0; - while (trip_ptr != NULL){ + while (trip_ptr != 0){ prev = trip_ptr; trip_ptr = trip_ptr->next; } trip_ptr = (struct ls_triplet_chain*) malloc (sizeof (struct ls_triplet_chain)); - if (prev == NULL) { + if (prev == 0) { *ls_triplets = trip_ptr; } else { prev->next = trip_ptr; } - trip_ptr->next = NULL; + trip_ptr->next = 0; trip_ptr->ls_nos[0] = i; trip_ptr->ls_nos[1] = j; trip_ptr->ls_nos[2] = k; @@ -463,7 +474,7 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) /* counting triplet amount */ - while (tr_ptr != NULL) { + while (tr_ptr != 0) { triplet_count++; tr_ptr = tr_ptr->next; } @@ -476,7 +487,7 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) _matrices.reserve (triplet_count); _speaker_tuples.reserve (triplet_count); - while (tr_ptr != NULL) { + while (tr_ptr != 0) { lp1 = &(_speakers[tr_ptr->ls_nos[0]].coords); lp2 = &(_speakers[tr_ptr->ls_nos[1]].coords); lp3 = &(_speakers[tr_ptr->ls_nos[2]].coords); @@ -509,9 +520,9 @@ VBAPSpeakers::calculate_3x3_matrixes(struct ls_triplet_chain *ls_triplets) _matrices[triplet][7] = invmx[7]; _matrices[triplet][8] = invmx[8]; - _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0]+1; - _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1]+1; - _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2]+1; + _speaker_tuples[triplet][0] = tr_ptr->ls_nos[0]; + _speaker_tuples[triplet][1] = tr_ptr->ls_nos[1]; + _speaker_tuples[triplet][2] = tr_ptr->ls_nos[2]; triplet++; @@ -553,7 +564,7 @@ VBAPSpeakers::choose_ls_pairs (){ } } - if(((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles.azi) + if (((6.283 - _speakers[sorted_speakers[n_speakers-1]].angles.azi) +_speakers[sorted_speakers[0]].angles.azi) <= (M_PI - 0.175)) { if(calc_2D_inv_tmatrix(_speakers[sorted_speakers[n_speakers-1]].angles.azi, _speakers[sorted_speakers[0]].angles.azi, @@ -578,8 +589,8 @@ VBAPSpeakers::choose_ls_pairs (){ _matrices[pair][2] = inverse_matrix[speaker][2]; _matrices[pair][3] = inverse_matrix[speaker][3]; - _speaker_tuples[pair][0] = sorted_speakers[speaker]+1; - _speaker_tuples[pair][1] = sorted_speakers[speaker+1]+1; + _speaker_tuples[pair][0] = sorted_speakers[speaker]; + _speaker_tuples[pair][1] = sorted_speakers[speaker+1]; pair++; } @@ -591,8 +602,8 @@ VBAPSpeakers::choose_ls_pairs (){ _matrices[pair][2] = inverse_matrix[speaker][2]; _matrices[pair][3] = inverse_matrix[speaker][3]; - _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1]+1; - _speaker_tuples[pair][1] = sorted_speakers[0]+1; + _speaker_tuples[pair][0] = sorted_speakers[n_speakers-1]; + _speaker_tuples[pair][1] = sorted_speakers[0]; } } |